diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap
new file mode 100644
index 000000000000..1b792503cf1c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HeaderPage it renders 1`] = `
+<Header
+  border={true}
+>
+  <EuiFlexGroup
+    alignItems="center"
+    justifyContent="spaceBetween"
+  >
+    <FlexItem
+      grow={false}
+    >
+      <EuiTitle
+        size="l"
+      >
+        <h1
+          data-test-subj="header-page-title"
+        >
+          Test title
+           
+          <StyledEuiBetaBadge
+            label="Beta"
+            tooltipContent="Test tooltip"
+            tooltipPosition="bottom"
+          />
+        </h1>
+      </EuiTitle>
+      <Subtitle
+        data-test-subj="header-page-subtitle"
+        items="Test subtitle"
+      />
+      <Subtitle
+        data-test-subj="header-page-subtitle-2"
+        items="Test subtitle 2"
+      />
+    </FlexItem>
+    <FlexItem
+      data-test-subj="header-page-supplements"
+    >
+      <p>
+        Test supplement
+      </p>
+    </FlexItem>
+  </EuiFlexGroup>
+</Header>
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx
new file mode 100644
index 000000000000..83a70fd90d82
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx
@@ -0,0 +1,224 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { shallow } from 'enzyme';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import { HeaderPage } from './index';
+import { useMountAppended } from '../../utils/use_mount_appended';
+
+describe('HeaderPage', () => {
+  const mount = useMountAppended();
+
+  test('it renders', () => {
+    const wrapper = shallow(
+      <HeaderPage
+        badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }}
+        border
+        subtitle="Test subtitle"
+        subtitle2="Test subtitle 2"
+        title="Test title"
+      >
+        <p>{'Test supplement'}</p>
+      </HeaderPage>
+    );
+
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  test('it renders the title', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-title"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it renders the back link when provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage backOptions={{ href: '#', text: 'Test link' }} title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('.siemHeaderPage__linkBack')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render the back link when not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('.siemHeaderPage__linkBack')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+
+  test('it renders the first subtitle when provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage subtitle="Test subtitle" title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-subtitle"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render the first subtitle when not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-section-subtitle"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+
+  test('it renders the second subtitle when provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage subtitle2="Test subtitle 2" title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-subtitle-2"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render the second subtitle when not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-section-subtitle-2"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+
+  test('it renders supplements when children provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title">
+          <p>{'Test supplement'}</p>
+        </HeaderPage>
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-supplements"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render supplements when children not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-supplements"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+
+  test('it applies border styles when border is true', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage border title="Test title" />
+      </TestProviders>
+    );
+    const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
+
+    expect(siemHeaderPage).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+    expect(siemHeaderPage).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
+  });
+
+  test('it DOES NOT apply border styles when border is false', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+    const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
+
+    expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+    expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
+  });
+
+  test('it renders as a draggable when arguments provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-draggable"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render as a draggable when arguments not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <HeaderPage title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-draggable"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx
new file mode 100644
index 000000000000..7e486c78fb9b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx
@@ -0,0 +1,220 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+  EuiBadge,
+  EuiBetaBadge,
+  EuiButton,
+  EuiButtonEmpty,
+  EuiButtonIcon,
+  EuiFieldText,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiProgress,
+  EuiTitle,
+} from '@elastic/eui';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+import { DefaultDraggable } from '../draggables';
+import { LinkIcon, LinkIconProps } from '../link_icon';
+import { Subtitle, SubtitleProps } from '../subtitle';
+import * as i18n from './translations';
+
+interface HeaderProps {
+  border?: boolean;
+  isLoading?: boolean;
+}
+
+const Header = styled.header.attrs({
+  className: 'siemHeaderPage',
+})<HeaderProps>`
+  ${({ border, theme }) => css`
+    margin-bottom: ${theme.eui.euiSizeL};
+
+    ${border &&
+      css`
+        border-bottom: ${theme.eui.euiBorderThin};
+        padding-bottom: ${theme.eui.paddingSizes.l};
+        .euiProgress {
+          top: ${theme.eui.paddingSizes.l};
+        }
+      `}
+  `}
+`;
+Header.displayName = 'Header';
+
+const FlexItem = styled(EuiFlexItem)`
+  display: block;
+`;
+FlexItem.displayName = 'FlexItem';
+
+const LinkBack = styled.div.attrs({
+  className: 'siemHeaderPage__linkBack',
+})`
+  ${({ theme }) => css`
+    font-size: ${theme.eui.euiFontSizeXS};
+    line-height: ${theme.eui.euiLineHeight};
+    margin-bottom: ${theme.eui.euiSizeS};
+  `}
+`;
+LinkBack.displayName = 'LinkBack';
+
+const Badge = styled(EuiBadge)`
+  letter-spacing: 0;
+`;
+Badge.displayName = 'Badge';
+
+const StyledEuiBetaBadge = styled(EuiBetaBadge)`
+  vertical-align: middle;
+`;
+
+StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
+
+const StyledEuiButtonIcon = styled(EuiButtonIcon)`
+  ${({ theme }) => css`
+    margin-left: ${theme.eui.euiSize};
+  `}
+`;
+
+StyledEuiButtonIcon.displayName = 'StyledEuiButtonIcon';
+
+interface BackOptions {
+  href: LinkIconProps['href'];
+  text: LinkIconProps['children'];
+}
+
+interface BadgeOptions {
+  beta?: boolean;
+  text: string;
+  tooltip?: string;
+}
+
+interface DraggableArguments {
+  field: string;
+  value: string;
+}
+interface IconAction {
+  'aria-label': string;
+  iconType: string;
+  onChange: (a: string) => void;
+  onClick: (b: boolean) => void;
+  onSubmit: () => void;
+}
+
+export interface HeaderPageProps extends HeaderProps {
+  backOptions?: BackOptions;
+  badgeOptions?: BadgeOptions;
+  children?: React.ReactNode;
+  draggableArguments?: DraggableArguments;
+  isEditTitle?: boolean;
+  iconAction?: IconAction;
+  subtitle2?: SubtitleProps['items'];
+  subtitle?: SubtitleProps['items'];
+  title: string | React.ReactNode;
+}
+
+const HeaderPageComponent: React.FC<HeaderPageProps> = ({
+  backOptions,
+  badgeOptions,
+  border,
+  children,
+  draggableArguments,
+  isEditTitle,
+  iconAction,
+  isLoading,
+  subtitle,
+  subtitle2,
+  title,
+  ...rest
+}) => (
+  <Header border={border} {...rest}>
+    <EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
+      <FlexItem grow={false}>
+        {backOptions && (
+          <LinkBack>
+            <LinkIcon href={backOptions.href} iconType="arrowLeft">
+              {backOptions.text}
+            </LinkIcon>
+          </LinkBack>
+        )}
+
+        {isEditTitle && iconAction ? (
+          <EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
+            <EuiFlexItem grow={false}>
+              <EuiFieldText
+                onChange={e => iconAction.onChange(e.target.value)}
+                value={`${title}`}
+              />
+            </EuiFlexItem>
+            <EuiFlexGroup gutterSize="none" responsive={false} wrap={true}>
+              <EuiFlexItem grow={false}>
+                <EuiButton
+                  fill
+                  isDisabled={isLoading}
+                  isLoading={isLoading}
+                  onClick={iconAction.onSubmit}
+                >
+                  {i18n.SUBMIT}
+                </EuiButton>
+              </EuiFlexItem>
+              <EuiFlexItem grow={false}>
+                <EuiButtonEmpty onClick={() => iconAction.onClick(false)}>
+                  {i18n.CANCEL}
+                </EuiButtonEmpty>
+              </EuiFlexItem>
+            </EuiFlexGroup>
+            <EuiFlexItem />
+          </EuiFlexGroup>
+        ) : (
+          <EuiTitle size="l">
+            <h1 data-test-subj="header-page-title">
+              {!draggableArguments ? (
+                title
+              ) : (
+                <DefaultDraggable
+                  data-test-subj="header-page-draggable"
+                  id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
+                  field={draggableArguments.field}
+                  value={`${draggableArguments.value}`}
+                />
+              )}
+              {badgeOptions && (
+                <>
+                  {' '}
+                  {badgeOptions.beta ? (
+                    <StyledEuiBetaBadge
+                      label={badgeOptions.text}
+                      tooltipContent={badgeOptions.tooltip}
+                      tooltipPosition="bottom"
+                    />
+                  ) : (
+                    <Badge color="hollow">{badgeOptions.text}</Badge>
+                  )}
+                </>
+              )}
+              {iconAction && (
+                <StyledEuiButtonIcon
+                  aria-label={iconAction['aria-label']}
+                  iconType={iconAction.iconType}
+                  onClick={() => iconAction.onClick(true)}
+                />
+              )}
+            </h1>
+          </EuiTitle>
+        )}
+
+        {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />}
+        {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />}
+        {border && isLoading && <EuiProgress size="xs" color="accent" />}
+      </FlexItem>
+
+      {children && <FlexItem data-test-subj="header-page-supplements">{children}</FlexItem>}
+    </EuiFlexGroup>
+  </Header>
+);
+
+export const HeaderPage = React.memo(HeaderPageComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts b/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts
new file mode 100644
index 000000000000..57b2cda0b0b0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const SUBMIT = i18n.translate('xpack.siem.case.casePage.title.submit', {
+  defaultMessage: 'Submit',
+});
+
+export const CANCEL = i18n.translate('xpack.siem.case.casePage.title.cancel', {
+  defaultMessage: 'Cancel',
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
index ad6147e5aad7..c93b415e017b 100644
--- a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
@@ -13,3 +13,10 @@ export { getOverviewUrl, RedirectToOverviewPage } from './redirect_to_overview';
 export { getHostDetailsUrl, getHostsUrl } from './redirect_to_hosts';
 export { getNetworkUrl, getIPDetailsUrl, RedirectToNetworkPage } from './redirect_to_network';
 export { getTimelinesUrl, RedirectToTimelinesPage } from './redirect_to_timelines';
+export {
+  getCaseDetailsUrl,
+  getCaseUrl,
+  getCreateCaseUrl,
+  RedirectToCasePage,
+  RedirectToCreatePage,
+} from './redirect_to_case';
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
index dc8c69630161..c08b429dc462 100644
--- a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
@@ -20,6 +20,7 @@ import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_ho
 import { RedirectToNetworkPage } from './redirect_to_network';
 import { RedirectToOverviewPage } from './redirect_to_overview';
 import { RedirectToTimelinesPage } from './redirect_to_timelines';
+import { RedirectToCasePage, RedirectToCreatePage } from './redirect_to_case';
 import { DetectionEngineTab } from '../../pages/detection_engine/types';
 
 interface LinkToPageProps {
@@ -32,6 +33,20 @@ export const LinkToPage = React.memo<LinkToPageProps>(({ match }) => (
       component={RedirectToOverviewPage}
       path={`${match.url}/:pageName(${SiemPageName.overview})`}
     />
+    <Route
+      exact
+      component={RedirectToCasePage}
+      path={`${match.url}/:pageName(${SiemPageName.case})`}
+    />
+    <Route
+      exact
+      component={RedirectToCreatePage}
+      path={`${match.url}/:pageName(${SiemPageName.case})/create`}
+    />
+    <Route
+      component={RedirectToCasePage}
+      path={`${match.url}/:pageName(${SiemPageName.case})/:detailName`}
+    />
     <Route
       component={RedirectToHostsPage}
       exact
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_case.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_case.tsx
new file mode 100644
index 000000000000..39e9f6b64b1d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_case.tsx
@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+import { RedirectWrapper } from './redirect_wrapper';
+import { SiemPageName } from '../../pages/home/types';
+
+export type CaseComponentProps = RouteComponentProps<{
+  detailName: string;
+}>;
+
+export const RedirectToCasePage = ({
+  match: {
+    params: { detailName },
+  },
+}: CaseComponentProps) => (
+  <RedirectWrapper
+    to={detailName ? `/${SiemPageName.case}/${detailName}` : `/${SiemPageName.case}`}
+  />
+);
+
+export const RedirectToCreatePage = () => <RedirectWrapper to={`/${SiemPageName.case}/create`} />;
+
+const baseCaseUrl = `#/link-to/${SiemPageName.case}`;
+
+export const getCaseUrl = () => baseCaseUrl;
+export const getCaseDetailsUrl = (detailName: string) => `${baseCaseUrl}/${detailName}`;
+export const getCreateCaseUrl = () => `${baseCaseUrl}/create`;
diff --git a/x-pack/legacy/plugins/siem/public/components/links/index.tsx b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
index e122b3e235a9..4f74f9ff2f5d 100644
--- a/x-pack/legacy/plugins/siem/public/components/links/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
@@ -8,7 +8,12 @@ import { EuiLink } from '@elastic/eui';
 import React from 'react';
 
 import { encodeIpv6 } from '../../lib/helpers';
-import { getHostDetailsUrl, getIPDetailsUrl } from '../link_to';
+import {
+  getCaseDetailsUrl,
+  getHostDetailsUrl,
+  getIPDetailsUrl,
+  getCreateCaseUrl,
+} from '../link_to';
 import { FlowTarget, FlowTargetSourceDest } from '../../graphql/types';
 
 // Internal Links
@@ -35,6 +40,23 @@ const IPDetailsLinkComponent: React.FC<{
 
 export const IPDetailsLink = React.memo(IPDetailsLinkComponent);
 
+const CaseDetailsLinkComponent: React.FC<{ children?: React.ReactNode; detailName: string }> = ({
+  children,
+  detailName,
+}) => (
+  <EuiLink href={getCaseDetailsUrl(encodeURIComponent(detailName))}>
+    {children ? children : detailName}
+  </EuiLink>
+);
+export const CaseDetailsLink = React.memo(CaseDetailsLinkComponent);
+CaseDetailsLink.displayName = 'CaseDetailsLink';
+
+export const CreateCaseLink = React.memo<{ children: React.ReactNode }>(({ children }) => (
+  <EuiLink href={getCreateCaseUrl()}>{children}</EuiLink>
+));
+
+CreateCaseLink.displayName = 'CreateCaseLink';
+
 // External Links
 export const GoogleLink = React.memo<{ children?: React.ReactNode; link: string }>(
   ({ children, link }) => (
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
index e8d5032fd754..e25fb4374bb1 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
@@ -11,6 +11,7 @@ import { APP_NAME } from '../../../../common/constants';
 import { StartServices } from '../../../plugin';
 import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../pages/hosts/details/utils';
 import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../pages/network/ip_details';
+import { getBreadcrumbs as getCaseDetailsBreadcrumbs } from '../../../pages/case/utils';
 import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../pages/detection_engine/rules/utils';
 import { SiemPageName } from '../../../pages/home/types';
 import { RouteSpyState, HostRouteSpyState, NetworkRouteSpyState } from '../../../utils/route/types';
@@ -43,6 +44,9 @@ const isNetworkRoutes = (spyState: RouteSpyState): spyState is NetworkRouteSpySt
 const isHostsRoutes = (spyState: RouteSpyState): spyState is HostRouteSpyState =>
   spyState != null && spyState.pageName === SiemPageName.hosts;
 
+const isCaseRoutes = (spyState: RouteSpyState): spyState is RouteSpyState =>
+  spyState != null && spyState.pageName === SiemPageName.case;
+
 const isDetectionsRoutes = (spyState: RouteSpyState) =>
   spyState != null && spyState.pageName === SiemPageName.detections;
 
@@ -102,6 +106,9 @@ export const getBreadcrumbsForRoute = (
       ),
     ];
   }
+  if (isCaseRoutes(spyState) && object.navTabs) {
+    return [...siemRootBreadcrumb, ...getCaseDetailsBreadcrumbs(spyState)];
+  }
   if (
     spyState != null &&
     object.navTabs &&
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
index ac7a4a0ee52b..8eb08bd3d62f 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
@@ -66,6 +66,13 @@ describe('SIEM Navigation', () => {
       {
         detailName: undefined,
         navTabs: {
+          case: {
+            disabled: true,
+            href: '#/link-to/case',
+            id: 'case',
+            name: 'Case',
+            urlKey: 'case',
+          },
           detections: {
             disabled: false,
             href: '#/link-to/detections',
@@ -152,6 +159,13 @@ describe('SIEM Navigation', () => {
         detailName: undefined,
         filters: [],
         navTabs: {
+          case: {
+            disabled: true,
+            href: '#/link-to/case',
+            id: 'case',
+            name: 'Case',
+            urlKey: 'case',
+          },
           detections: {
             disabled: false,
             href: '#/link-to/detections',
diff --git a/x-pack/legacy/plugins/siem/public/components/notes/add_note/new_note.tsx b/x-pack/legacy/plugins/siem/public/components/notes/add_note/new_note.tsx
index 5a3439d53dd8..15e58f3efd21 100644
--- a/x-pack/legacy/plugins/siem/public/components/notes/add_note/new_note.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/notes/add_note/new_note.tsx
@@ -32,8 +32,6 @@ const TextArea = styled(EuiTextArea)<{ height: number }>`
 
 TextArea.displayName = 'TextArea';
 
-TextArea.displayName = 'TextArea';
-
 /** An input for entering a new note  */
 export const NewNote = React.memo<{
   noteInputHeight: number;
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
index 22e8f99658f8..b6ef3c8ccd4e 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
@@ -6,6 +6,8 @@
 
 export enum CONSTANTS {
   appQuery = 'query',
+  caseDetails = 'case.details',
+  casePage = 'case.page',
   detectionsPage = 'detections.page',
   filters = 'filters',
   hostsDetails = 'hosts.details',
@@ -14,10 +16,10 @@ export enum CONSTANTS {
   networkPage = 'network.page',
   overviewPage = 'overview.page',
   savedQuery = 'savedQuery',
+  timeline = 'timeline',
   timelinePage = 'timeline.page',
   timerange = 'timerange',
-  timeline = 'timeline',
   unknown = 'unknown',
 }
 
-export type UrlStateType = 'detections' | 'host' | 'network' | 'overview' | 'timeline';
+export type UrlStateType = 'case' | 'detections' | 'host' | 'network' | 'overview' | 'timeline';
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
index 7be775ef0c0e..05329621aa97 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
@@ -98,6 +98,8 @@ export const getUrlType = (pageName: string): UrlStateType => {
     return 'detections';
   } else if (pageName === SiemPageName.timelines) {
     return 'timeline';
+  } else if (pageName === SiemPageName.case) {
+    return 'case';
   }
   return 'overview';
 };
@@ -131,6 +133,11 @@ export const getCurrentLocation = (
     return CONSTANTS.detectionsPage;
   } else if (pageName === SiemPageName.timelines) {
     return CONSTANTS.timelinePage;
+  } else if (pageName === SiemPageName.case) {
+    if (detailName != null) {
+      return CONSTANTS.caseDetails;
+    }
+    return CONSTANTS.casePage;
   }
   return CONSTANTS.unknown;
 };
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
index fea1bc016fd4..97979e514aea 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
@@ -60,9 +60,12 @@ export const URL_STATE_KEYS: Record<UrlStateType, KeyUrlState[]> = {
     CONSTANTS.timeline,
   ],
   timeline: [CONSTANTS.timeline, CONSTANTS.timerange],
+  case: [],
 };
 
 export type LocationTypes =
+  | CONSTANTS.caseDetails
+  | CONSTANTS.casePage
   | CONSTANTS.detectionsPage
   | CONSTANTS.hostsDetails
   | CONSTANTS.hostsPage
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/api.ts
new file mode 100644
index 000000000000..830e00c70975
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/api.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { KibanaServices } from '../../lib/kibana';
+import { AllCases, FetchCasesProps, Case, NewCase, SortFieldCase } from './types';
+import { Direction } from '../../graphql/types';
+import { throwIfNotOk } from '../../hooks/api/api';
+import { CASES_URL } from './constants';
+
+export const getCase = async (caseId: string, includeComments: boolean) => {
+  const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, {
+    method: 'GET',
+    asResponse: true,
+    query: {
+      includeComments,
+    },
+  });
+  await throwIfNotOk(response.response);
+  return response.body!;
+};
+
+export const getCases = async ({
+  filterOptions = {
+    search: '',
+    tags: [],
+  },
+  queryParams = {
+    page: 1,
+    perPage: 20,
+    sortField: SortFieldCase.createdAt,
+    sortOrder: Direction.desc,
+  },
+}: FetchCasesProps): Promise<AllCases> => {
+  const tags = [...(filterOptions.tags?.map(t => `case-workflow.attributes.tags: ${t}`) ?? [])];
+  const query = {
+    ...queryParams,
+    filter: tags.join(' AND '),
+    search: filterOptions.search,
+  };
+  const response = await KibanaServices.get().http.fetch(`${CASES_URL}`, {
+    method: 'GET',
+    query,
+    asResponse: true,
+  });
+  await throwIfNotOk(response.response);
+  return response.body!;
+};
+
+export const createCase = async (newCase: NewCase): Promise<Case> => {
+  const response = await KibanaServices.get().http.fetch(`${CASES_URL}`, {
+    method: 'POST',
+    asResponse: true,
+    body: JSON.stringify(newCase),
+  });
+  await throwIfNotOk(response.response);
+  return response.body!;
+};
+
+export const updateCaseProperty = async (
+  caseId: string,
+  updatedCase: Partial<Case>
+): Promise<Partial<Case>> => {
+  const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, {
+    method: 'PATCH',
+    asResponse: true,
+    body: JSON.stringify(updatedCase),
+  });
+  await throwIfNotOk(response.response);
+  return response.body!;
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/constants.ts b/x-pack/legacy/plugins/siem/public/containers/case/constants.ts
new file mode 100644
index 000000000000..c8d668527ae3
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/constants.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const CASES_URL = `/api/cases`;
+export const DEFAULT_TABLE_ACTIVE_PAGE = 1;
+export const DEFAULT_TABLE_LIMIT = 5;
+export const FETCH_FAILURE = 'FETCH_FAILURE';
+export const FETCH_INIT = 'FETCH_INIT';
+export const FETCH_SUCCESS = 'FETCH_SUCCESS';
+export const POST_NEW_CASE = 'POST_NEW_CASE';
+export const UPDATE_CASE_PROPERTY = 'UPDATE_CASE_PROPERTY';
+export const UPDATE_FILTER_OPTIONS = 'UPDATE_FILTER_OPTIONS';
+export const UPDATE_QUERY_PARAMS = 'UPDATE_QUERY_PARAMS';
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/translations.ts b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts
new file mode 100644
index 000000000000..0c8b896e2b42
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const ERROR_TITLE = i18n.translate('xpack.siem.containers.case.errorTitle', {
+  defaultMessage: 'Error fetching data',
+});
+
+export const TAG_FETCH_FAILURE = i18n.translate(
+  'xpack.siem.containers.case.tagFetchFailDescription',
+  {
+    defaultMessage: 'Failed to fetch Tags',
+  }
+);
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/types.ts b/x-pack/legacy/plugins/siem/public/containers/case/types.ts
new file mode 100644
index 000000000000..0f80b2327a30
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/types.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Direction } from '../../graphql/types';
+interface FormData {
+  isNew?: boolean;
+}
+
+export interface NewCase extends FormData {
+  description: string;
+  tags: string[];
+  title: string;
+}
+
+export interface Case {
+  case_id: string;
+  created_at: string;
+  created_by: ElasticUser;
+  description: string;
+  state: string;
+  tags: string[];
+  title: string;
+  updated_at: string;
+}
+
+export interface QueryParams {
+  page: number;
+  perPage: number;
+  sortField: SortFieldCase;
+  sortOrder: Direction;
+}
+
+export interface FilterOptions {
+  search: string;
+  tags: string[];
+}
+
+export interface AllCases {
+  cases: Case[];
+  page: number;
+  per_page: number;
+  total: number;
+}
+export enum SortFieldCase {
+  createdAt = 'created_at',
+  state = 'state',
+  updatedAt = 'updated_at',
+}
+
+export interface ElasticUser {
+  readonly username: string;
+  readonly full_name?: string;
+}
+
+export interface FetchCasesProps {
+  queryParams?: QueryParams;
+  filterOptions?: FilterOptions;
+}
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
new file mode 100644
index 000000000000..8cc961c68fdf
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
@@ -0,0 +1,99 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useEffect, useReducer } from 'react';
+
+import { Case } from './types';
+import { FETCH_INIT, FETCH_FAILURE, FETCH_SUCCESS } from './constants';
+import { getTypedPayload } from './utils';
+import { errorToToaster } from '../../components/ml/api/error_to_toaster';
+import * as i18n from './translations';
+import { useStateToaster } from '../../components/toasters';
+import { getCase } from './api';
+
+interface CaseState {
+  data: Case;
+  isLoading: boolean;
+  isError: boolean;
+}
+interface Action {
+  type: string;
+  payload?: Case;
+}
+
+const dataFetchReducer = (state: CaseState, action: Action): CaseState => {
+  switch (action.type) {
+    case FETCH_INIT:
+      return {
+        ...state,
+        isLoading: true,
+        isError: false,
+      };
+    case FETCH_SUCCESS:
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: getTypedPayload<Case>(action.payload),
+      };
+    case FETCH_FAILURE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: true,
+      };
+    default:
+      throw new Error();
+  }
+};
+const initialData: Case = {
+  case_id: '',
+  created_at: '',
+  created_by: {
+    username: '',
+  },
+  description: '',
+  state: '',
+  tags: [],
+  title: '',
+  updated_at: '',
+};
+
+export const useGetCase = (caseId: string): [CaseState] => {
+  const [state, dispatch] = useReducer(dataFetchReducer, {
+    isLoading: true,
+    isError: false,
+    data: initialData,
+  });
+  const [, dispatchToaster] = useStateToaster();
+
+  const callFetch = () => {
+    let didCancel = false;
+    const fetchData = async () => {
+      dispatch({ type: FETCH_INIT });
+      try {
+        const response = await getCase(caseId, false);
+        if (!didCancel) {
+          dispatch({ type: FETCH_SUCCESS, payload: response });
+        }
+      } catch (error) {
+        if (!didCancel) {
+          errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
+          dispatch({ type: FETCH_FAILURE });
+        }
+      }
+    };
+    fetchData();
+    return () => {
+      didCancel = true;
+    };
+  };
+
+  useEffect(() => {
+    callFetch();
+  }, [caseId]);
+  return [state];
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx
new file mode 100644
index 000000000000..db9c07747ba0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx
@@ -0,0 +1,154 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Dispatch, SetStateAction, useEffect, useReducer, useState } from 'react';
+import { isEqual } from 'lodash/fp';
+import {
+  DEFAULT_TABLE_ACTIVE_PAGE,
+  DEFAULT_TABLE_LIMIT,
+  FETCH_FAILURE,
+  FETCH_INIT,
+  FETCH_SUCCESS,
+  UPDATE_QUERY_PARAMS,
+  UPDATE_FILTER_OPTIONS,
+} from './constants';
+import { AllCases, SortFieldCase, FilterOptions, QueryParams } from './types';
+import { getTypedPayload } from './utils';
+import { Direction } from '../../graphql/types';
+import { errorToToaster } from '../../components/ml/api/error_to_toaster';
+import { useStateToaster } from '../../components/toasters';
+import * as i18n from './translations';
+import { getCases } from './api';
+
+export interface UseGetCasesState {
+  data: AllCases;
+  isLoading: boolean;
+  isError: boolean;
+  queryParams: QueryParams;
+  filterOptions: FilterOptions;
+}
+
+export interface QueryArgs {
+  page?: number;
+  perPage?: number;
+  sortField?: SortFieldCase;
+  sortOrder?: Direction;
+}
+
+export interface Action {
+  type: string;
+  payload?: AllCases | QueryArgs | FilterOptions;
+}
+const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesState => {
+  switch (action.type) {
+    case FETCH_INIT:
+      return {
+        ...state,
+        isLoading: true,
+        isError: false,
+      };
+    case FETCH_SUCCESS:
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: getTypedPayload<AllCases>(action.payload),
+      };
+    case FETCH_FAILURE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: true,
+      };
+    case UPDATE_QUERY_PARAMS:
+      return {
+        ...state,
+        queryParams: {
+          ...state.queryParams,
+          ...action.payload,
+        },
+      };
+    case UPDATE_FILTER_OPTIONS:
+      return {
+        ...state,
+        filterOptions: getTypedPayload<FilterOptions>(action.payload),
+      };
+    default:
+      throw new Error();
+  }
+};
+
+const initialData: AllCases = {
+  page: 0,
+  per_page: 0,
+  total: 0,
+  cases: [],
+};
+export const useGetCases = (): [
+  UseGetCasesState,
+  Dispatch<SetStateAction<QueryArgs>>,
+  Dispatch<SetStateAction<FilterOptions>>
+] => {
+  const [state, dispatch] = useReducer(dataFetchReducer, {
+    isLoading: false,
+    isError: false,
+    data: initialData,
+    filterOptions: {
+      search: '',
+      tags: [],
+    },
+    queryParams: {
+      page: DEFAULT_TABLE_ACTIVE_PAGE,
+      perPage: DEFAULT_TABLE_LIMIT,
+      sortField: SortFieldCase.createdAt,
+      sortOrder: Direction.desc,
+    },
+  });
+  const [queryParams, setQueryParams] = useState(state.queryParams as QueryArgs);
+  const [filterQuery, setFilters] = useState(state.filterOptions as FilterOptions);
+  const [, dispatchToaster] = useStateToaster();
+
+  useEffect(() => {
+    if (!isEqual(queryParams, state.queryParams)) {
+      dispatch({ type: UPDATE_QUERY_PARAMS, payload: queryParams });
+    }
+  }, [queryParams, state.queryParams]);
+
+  useEffect(() => {
+    if (!isEqual(filterQuery, state.filterOptions)) {
+      dispatch({ type: UPDATE_FILTER_OPTIONS, payload: filterQuery });
+    }
+  }, [filterQuery, state.filterOptions]);
+
+  useEffect(() => {
+    let didCancel = false;
+    const fetchData = async () => {
+      dispatch({ type: FETCH_INIT });
+      try {
+        const response = await getCases({
+          filterOptions: state.filterOptions,
+          queryParams: state.queryParams,
+        });
+        if (!didCancel) {
+          dispatch({
+            type: FETCH_SUCCESS,
+            payload: response,
+          });
+        }
+      } catch (error) {
+        if (!didCancel) {
+          errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
+          dispatch({ type: FETCH_FAILURE });
+        }
+      }
+    };
+    fetchData();
+    return () => {
+      didCancel = true;
+    };
+  }, [state.queryParams, state.filterOptions]);
+  return [state, setQueryParams, setFilters];
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx
new file mode 100644
index 000000000000..f796ae550c9e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_tags.tsx
@@ -0,0 +1,92 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useEffect, useReducer } from 'react';
+import chrome from 'ui/chrome';
+import { useStateToaster } from '../../components/toasters';
+import { errorToToaster } from '../../components/ml/api/error_to_toaster';
+import * as i18n from './translations';
+import { FETCH_FAILURE, FETCH_INIT, FETCH_SUCCESS } from './constants';
+import { throwIfNotOk } from '../../hooks/api/api';
+
+interface TagsState {
+  data: string[];
+  isLoading: boolean;
+  isError: boolean;
+}
+interface Action {
+  type: string;
+  payload?: string[];
+}
+
+const dataFetchReducer = (state: TagsState, action: Action): TagsState => {
+  switch (action.type) {
+    case FETCH_INIT:
+      return {
+        ...state,
+        isLoading: true,
+        isError: false,
+      };
+    case FETCH_SUCCESS:
+      const getTypedPayload = (a: Action['payload']) => a as string[];
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: getTypedPayload(action.payload),
+      };
+    case FETCH_FAILURE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: true,
+      };
+    default:
+      throw new Error();
+  }
+};
+const initialData: string[] = [];
+
+export const useGetTags = (): [TagsState] => {
+  const [state, dispatch] = useReducer(dataFetchReducer, {
+    isLoading: false,
+    isError: false,
+    data: initialData,
+  });
+  const [, dispatchToaster] = useStateToaster();
+
+  useEffect(() => {
+    let didCancel = false;
+    const fetchData = async () => {
+      dispatch({ type: FETCH_INIT });
+      try {
+        const response = await fetch(`${chrome.getBasePath()}/api/cases/tags`, {
+          method: 'GET',
+          credentials: 'same-origin',
+          headers: {
+            'content-type': 'application/json',
+            'kbn-system-api': 'true',
+          },
+        });
+        if (!didCancel) {
+          await throwIfNotOk(response);
+          const responseJson = await response.json();
+          dispatch({ type: FETCH_SUCCESS, payload: responseJson });
+        }
+      } catch (error) {
+        if (!didCancel) {
+          errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
+          dispatch({ type: FETCH_FAILURE });
+        }
+      }
+    };
+    fetchData();
+    return () => {
+      didCancel = true;
+    };
+  }, []);
+  return [state];
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx
new file mode 100644
index 000000000000..5cf99701977d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_post_case.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Dispatch, SetStateAction, useEffect, useReducer, useState } from 'react';
+import { useStateToaster } from '../../components/toasters';
+import { errorToToaster } from '../../components/ml/api/error_to_toaster';
+import * as i18n from './translations';
+import { FETCH_FAILURE, FETCH_INIT, FETCH_SUCCESS, POST_NEW_CASE } from './constants';
+import { Case, NewCase } from './types';
+import { createCase } from './api';
+import { getTypedPayload } from './utils';
+
+interface NewCaseState {
+  data: NewCase;
+  newCase?: Case;
+  isLoading: boolean;
+  isError: boolean;
+}
+interface Action {
+  type: string;
+  payload?: NewCase | Case;
+}
+
+const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => {
+  switch (action.type) {
+    case FETCH_INIT:
+      return {
+        ...state,
+        isLoading: true,
+        isError: false,
+      };
+    case POST_NEW_CASE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: getTypedPayload<NewCase>(action.payload),
+      };
+    case FETCH_SUCCESS:
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        newCase: getTypedPayload<Case>(action.payload),
+      };
+    case FETCH_FAILURE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: true,
+      };
+    default:
+      throw new Error();
+  }
+};
+const initialData: NewCase = {
+  description: '',
+  isNew: false,
+  tags: [],
+  title: '',
+};
+
+export const usePostCase = (): [NewCaseState, Dispatch<SetStateAction<NewCase>>] => {
+  const [state, dispatch] = useReducer(dataFetchReducer, {
+    isLoading: false,
+    isError: false,
+    data: initialData,
+  });
+  const [formData, setFormData] = useState(initialData);
+  const [, dispatchToaster] = useStateToaster();
+
+  useEffect(() => {
+    dispatch({ type: POST_NEW_CASE, payload: formData });
+  }, [formData]);
+
+  useEffect(() => {
+    const postCase = async () => {
+      dispatch({ type: FETCH_INIT });
+      try {
+        const dataWithoutIsNew = state.data;
+        delete dataWithoutIsNew.isNew;
+        const response = await createCase(dataWithoutIsNew);
+        dispatch({ type: FETCH_SUCCESS, payload: response });
+      } catch (error) {
+        errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
+        dispatch({ type: FETCH_FAILURE });
+      }
+    };
+    if (state.data.isNew) {
+      postCase();
+    }
+  }, [state.data.isNew]);
+  return [state, setFormData];
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx
new file mode 100644
index 000000000000..68592c17e58d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx
@@ -0,0 +1,112 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useEffect, useReducer } from 'react';
+import { useStateToaster } from '../../components/toasters';
+import { errorToToaster } from '../../components/ml/api/error_to_toaster';
+import * as i18n from './translations';
+import { FETCH_FAILURE, FETCH_INIT, FETCH_SUCCESS, UPDATE_CASE_PROPERTY } from './constants';
+import { Case } from './types';
+import { updateCaseProperty } from './api';
+import { getTypedPayload } from './utils';
+
+type UpdateKey = keyof Case;
+
+interface NewCaseState {
+  data: Case;
+  isLoading: boolean;
+  isError: boolean;
+  updateKey?: UpdateKey | null;
+}
+
+interface UpdateByKey {
+  updateKey: UpdateKey;
+  updateValue: Case[UpdateKey];
+}
+
+interface Action {
+  type: string;
+  payload?: Partial<Case> | UpdateByKey;
+}
+
+const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => {
+  switch (action.type) {
+    case FETCH_INIT:
+      return {
+        ...state,
+        isLoading: true,
+        isError: false,
+        updateKey: null,
+      };
+    case UPDATE_CASE_PROPERTY:
+      const { updateKey, updateValue } = getTypedPayload<UpdateByKey>(action.payload);
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: {
+          ...state.data,
+          [updateKey]: updateValue,
+        },
+        updateKey,
+      };
+    case FETCH_SUCCESS:
+      return {
+        ...state,
+        isLoading: false,
+        isError: false,
+        data: {
+          ...state.data,
+          ...getTypedPayload<Case>(action.payload),
+        },
+      };
+    case FETCH_FAILURE:
+      return {
+        ...state,
+        isLoading: false,
+        isError: true,
+      };
+    default:
+      throw new Error();
+  }
+};
+
+export const useUpdateCase = (
+  caseId: string,
+  initialData: Case
+): [{ data: Case }, (updates: UpdateByKey) => void] => {
+  const [state, dispatch] = useReducer(dataFetchReducer, {
+    isLoading: false,
+    isError: false,
+    data: initialData,
+  });
+  const [, dispatchToaster] = useStateToaster();
+
+  const dispatchUpdateCaseProperty = ({ updateKey, updateValue }: UpdateByKey) => {
+    dispatch({
+      type: UPDATE_CASE_PROPERTY,
+      payload: { updateKey, updateValue },
+    });
+  };
+
+  useEffect(() => {
+    const updateData = async (updateKey: keyof Case) => {
+      dispatch({ type: FETCH_INIT });
+      try {
+        const response = await updateCaseProperty(caseId, { [updateKey]: state.data[updateKey] });
+        dispatch({ type: FETCH_SUCCESS, payload: response });
+      } catch (error) {
+        errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
+        dispatch({ type: FETCH_FAILURE });
+      }
+    };
+    if (state.updateKey) {
+      updateData(state.updateKey);
+    }
+  }, [state.updateKey]);
+
+  return [{ data: state.data }, dispatchUpdateCaseProperty];
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/utils.ts b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts
new file mode 100644
index 000000000000..8e6eaca1a8f0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const getTypedPayload = <T>(a: unknown): T => a as T;
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/case.tsx b/x-pack/legacy/plugins/siem/public/pages/case/case.tsx
new file mode 100644
index 000000000000..1206ec950dee
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/case.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { EuiButton, EuiFlexGroup } from '@elastic/eui';
+import { HeaderPage } from '../../components/header_page';
+import { WrapperPage } from '../../components/wrapper_page';
+import { AllCases } from './components/all_cases';
+import { SpyRoute } from '../../utils/route/spy_routes';
+import * as i18n from './translations';
+import { getCreateCaseUrl } from '../../components/link_to';
+
+const badgeOptions = {
+  beta: true,
+  text: i18n.PAGE_BADGE_LABEL,
+  tooltip: i18n.PAGE_BADGE_TOOLTIP,
+};
+
+export const CasesPage = React.memo(() => (
+  <>
+    <WrapperPage>
+      <HeaderPage badgeOptions={badgeOptions} subtitle={i18n.PAGE_SUBTITLE} title={i18n.PAGE_TITLE}>
+        <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
+          <EuiButton fill href={getCreateCaseUrl()} iconType="plusInCircle">
+            {i18n.CREATE_TITLE}
+          </EuiButton>
+        </EuiFlexGroup>
+      </HeaderPage>
+      <AllCases />
+    </WrapperPage>
+    <SpyRoute />
+  </>
+));
+
+CasesPage.displayName = 'CasesPage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx b/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx
new file mode 100644
index 000000000000..890df91c8560
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { useParams } from 'react-router-dom';
+
+import { CaseView } from './components/case_view';
+import { SpyRoute } from '../../utils/route/spy_routes';
+
+export const CaseDetailsPage = React.memo(() => {
+  const { detailName: caseId } = useParams();
+  if (!caseId) {
+    return null;
+  }
+  return (
+    <>
+      <CaseView caseId={caseId} />
+      <SpyRoute />
+    </>
+  );
+});
+
+CaseDetailsPage.displayName = 'CaseDetailsPage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx
new file mode 100644
index 000000000000..92cd16fd2000
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { EuiBadge, EuiTableFieldDataColumnType, EuiTableComputedColumnType } from '@elastic/eui';
+import { getEmptyTagValue } from '../../../../components/empty_value';
+import { Case } from '../../../../containers/case/types';
+import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date';
+import { CaseDetailsLink } from '../../../../components/links';
+import { TruncatableText } from '../../../../components/truncatable_text';
+import * as i18n from './translations';
+
+export type CasesColumns = EuiTableFieldDataColumnType<Case> | EuiTableComputedColumnType<Case>;
+
+const renderStringField = (field: string) => (field != null ? field : getEmptyTagValue());
+
+export const getCasesColumns = (): CasesColumns[] => [
+  {
+    name: i18n.CASE_TITLE,
+    render: (theCase: Case) => {
+      if (theCase.case_id != null && theCase.title != null) {
+        return <CaseDetailsLink detailName={theCase.case_id}>{theCase.title}</CaseDetailsLink>;
+      }
+      return getEmptyTagValue();
+    },
+  },
+  {
+    field: 'tags',
+    name: i18n.TAGS,
+    render: (tags: Case['tags']) => {
+      if (tags != null && tags.length > 0) {
+        return (
+          <TruncatableText>
+            {tags.map((tag: string, i: number) => (
+              <EuiBadge color="hollow" key={`${tag}-${i}`}>
+                {tag}
+              </EuiBadge>
+            ))}
+          </TruncatableText>
+        );
+      }
+      return getEmptyTagValue();
+    },
+    truncateText: true,
+  },
+  {
+    field: 'created_at',
+    name: i18n.CREATED_AT,
+    sortable: true,
+    render: (createdAt: Case['created_at']) => {
+      if (createdAt != null) {
+        return <FormattedRelativePreferenceDate value={createdAt} />;
+      }
+      return getEmptyTagValue();
+    },
+  },
+  {
+    field: 'created_by.username',
+    name: i18n.REPORTER,
+    render: (createdBy: Case['created_by']['username']) => renderStringField(createdBy),
+  },
+  {
+    field: 'updated_at',
+    name: i18n.LAST_UPDATED,
+    sortable: true,
+    render: (updatedAt: Case['updated_at']) => {
+      if (updatedAt != null) {
+        return <FormattedRelativePreferenceDate value={updatedAt} />;
+      }
+      return getEmptyTagValue();
+    },
+  },
+  {
+    field: 'state',
+    name: i18n.STATE,
+    sortable: true,
+    render: (state: Case['state']) => renderStringField(state),
+  },
+];
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx
new file mode 100644
index 000000000000..b1dd39c95e19
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx
@@ -0,0 +1,147 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useMemo } from 'react';
+import {
+  EuiBasicTable,
+  EuiButton,
+  EuiEmptyPrompt,
+  EuiLoadingContent,
+  EuiTableSortingType,
+} from '@elastic/eui';
+import { isEmpty } from 'lodash/fp';
+import * as i18n from './translations';
+
+import { getCasesColumns } from './columns';
+import { SortFieldCase, Case, FilterOptions } from '../../../../containers/case/types';
+
+import { Direction } from '../../../../graphql/types';
+import { useGetCases } from '../../../../containers/case/use_get_cases';
+import { EuiBasicTableOnChange } from '../../../detection_engine/rules/types';
+import { Panel } from '../../../../components/panel';
+import { HeaderSection } from '../../../../components/header_section';
+import { CasesTableFilters } from './table_filters';
+
+import {
+  UtilityBar,
+  UtilityBarGroup,
+  UtilityBarSection,
+  UtilityBarText,
+} from '../../../../components/detection_engine/utility_bar';
+import { getCreateCaseUrl } from '../../../../components/link_to';
+
+export const AllCases = React.memo(() => {
+  const [
+    { data, isLoading, queryParams, filterOptions },
+    setQueryParams,
+    setFilters,
+  ] = useGetCases();
+
+  const tableOnChangeCallback = useCallback(
+    ({ page, sort }: EuiBasicTableOnChange) => {
+      let newQueryParams = queryParams;
+      if (sort) {
+        let newSort;
+        switch (sort.field) {
+          case 'state':
+            newSort = SortFieldCase.state;
+            break;
+          case 'created_at':
+            newSort = SortFieldCase.createdAt;
+            break;
+          case 'updated_at':
+            newSort = SortFieldCase.updatedAt;
+            break;
+          default:
+            newSort = SortFieldCase.createdAt;
+        }
+        newQueryParams = {
+          ...newQueryParams,
+          sortField: newSort,
+          sortOrder: sort.direction as Direction,
+        };
+      }
+      if (page) {
+        newQueryParams = {
+          ...newQueryParams,
+          page: page.index + 1,
+          perPage: page.size,
+        };
+      }
+      setQueryParams(newQueryParams);
+    },
+    [setQueryParams, queryParams]
+  );
+
+  const onFilterChangedCallback = useCallback(
+    (newFilterOptions: Partial<FilterOptions>) => {
+      setFilters({ ...filterOptions, ...newFilterOptions });
+    },
+    [filterOptions, setFilters]
+  );
+
+  const memoizedGetCasesColumns = useMemo(() => getCasesColumns(), []);
+  const memoizedPagination = useMemo(
+    () => ({
+      pageIndex: queryParams.page - 1,
+      pageSize: queryParams.perPage,
+      totalItemCount: data.total,
+      pageSizeOptions: [5, 10, 20, 50, 100, 200, 300],
+    }),
+    [data, queryParams]
+  );
+
+  const sorting: EuiTableSortingType<Case> = {
+    sort: { field: queryParams.sortField, direction: queryParams.sortOrder },
+  };
+
+  return (
+    <Panel loading={isLoading}>
+      <HeaderSection split title={i18n.ALL_CASES}>
+        <CasesTableFilters
+          onFilterChanged={onFilterChangedCallback}
+          initial={{ search: filterOptions.search, tags: filterOptions.tags }}
+        />
+      </HeaderSection>
+      {isLoading && isEmpty(data.cases) && (
+        <EuiLoadingContent data-test-subj="initialLoadingPanelAllCases" lines={10} />
+      )}
+      {!isLoading && !isEmpty(data.cases) && (
+        <>
+          <UtilityBar border>
+            <UtilityBarSection>
+              <UtilityBarGroup>
+                <UtilityBarText>{i18n.SHOWING_CASES(data.total ?? 0)}</UtilityBarText>
+              </UtilityBarGroup>
+            </UtilityBarSection>
+          </UtilityBar>
+          <EuiBasicTable
+            columns={memoizedGetCasesColumns}
+            itemId="id"
+            items={data.cases}
+            noItemsMessage={
+              <EuiEmptyPrompt
+                title={<h3>{i18n.NO_CASES}</h3>}
+                titleSize="xs"
+                body={i18n.NO_CASES_BODY}
+                actions={
+                  <EuiButton fill size="s" href={getCreateCaseUrl()} iconType="plusInCircle">
+                    {i18n.ADD_NEW_CASE}
+                  </EuiButton>
+                }
+              />
+            }
+            onChange={tableOnChangeCallback}
+            pagination={memoizedPagination}
+            sorting={sorting}
+          />
+        </>
+      )}
+    </Panel>
+  );
+});
+
+AllCases.displayName = 'AllCases';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx
new file mode 100644
index 000000000000..e59362378804
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useState } from 'react';
+import { isEqual } from 'lodash/fp';
+import { EuiFieldSearch, EuiFilterGroup, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import * as i18n from './translations';
+
+import { FilterOptions } from '../../../../containers/case/types';
+import { useGetTags } from '../../../../containers/case/use_get_tags';
+import { TagsFilterPopover } from '../../../../pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover';
+
+interface Initial {
+  search: string;
+  tags: string[];
+}
+interface CasesTableFiltersProps {
+  onFilterChanged: (filterOptions: Partial<FilterOptions>) => void;
+  initial: Initial;
+}
+
+/**
+ * Collection of filters for filtering data within the CasesTable. Contains search bar,
+ * and tag selection
+ *
+ * @param onFilterChanged change listener to be notified on filter changes
+ */
+
+const CasesTableFiltersComponent = ({
+  onFilterChanged,
+  initial = { search: '', tags: [] },
+}: CasesTableFiltersProps) => {
+  const [search, setSearch] = useState(initial.search);
+  const [selectedTags, setSelectedTags] = useState(initial.tags);
+  const [{ isLoading, data }] = useGetTags();
+
+  const handleSelectedTags = useCallback(
+    newTags => {
+      if (!isEqual(newTags, selectedTags)) {
+        setSelectedTags(newTags);
+        onFilterChanged({ search, tags: newTags });
+      }
+    },
+    [search, selectedTags]
+  );
+  const handleOnSearch = useCallback(
+    newSearch => {
+      const trimSearch = newSearch.trim();
+      if (!isEqual(trimSearch, search)) {
+        setSearch(trimSearch);
+        onFilterChanged({ tags: selectedTags, search: trimSearch });
+      }
+    },
+    [search, selectedTags]
+  );
+
+  return (
+    <EuiFlexGroup gutterSize="m" justifyContent="flexEnd">
+      <EuiFlexItem grow={true}>
+        <EuiFieldSearch
+          aria-label={i18n.SEARCH_CASES}
+          fullWidth
+          incremental={false}
+          placeholder={i18n.SEARCH_PLACEHOLDER}
+          onSearch={handleOnSearch}
+        />
+      </EuiFlexItem>
+
+      <EuiFlexItem grow={false}>
+        <EuiFilterGroup>
+          <TagsFilterPopover
+            isLoading={isLoading}
+            onSelectedTagsChanged={handleSelectedTags}
+            selectedTags={selectedTags}
+            tags={data}
+          />
+        </EuiFilterGroup>
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+CasesTableFiltersComponent.displayName = 'CasesTableFiltersComponent';
+
+export const CasesTableFilters = React.memo(CasesTableFiltersComponent);
+
+CasesTableFilters.displayName = 'CasesTableFilters';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts
new file mode 100644
index 000000000000..ab8e22ebcf1b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/translations.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export * from '../../translations';
+
+export const ALL_CASES = i18n.translate('xpack.siem.case.caseTable.title', {
+  defaultMessage: 'All Cases',
+});
+export const NO_CASES = i18n.translate('xpack.siem.case.caseTable.noCases.title', {
+  defaultMessage: 'No Cases',
+});
+export const NO_CASES_BODY = i18n.translate('xpack.siem.case.caseTable.noCases.body', {
+  defaultMessage: 'Create a new case to see it displayed in the case workflow table.',
+});
+export const ADD_NEW_CASE = i18n.translate('xpack.siem.case.caseTable.addNewCase', {
+  defaultMessage: 'Add New Case',
+});
+
+export const SHOWING_CASES = (totalRules: number) =>
+  i18n.translate('xpack.siem.case.caseTable.showingCasesTitle', {
+    values: { totalRules },
+    defaultMessage: 'Showing {totalRules} {totalRules, plural, =1 {case} other {cases}}',
+  });
+
+export const UNIT = (totalCount: number) =>
+  i18n.translate('xpack.siem.case.caseTable.unit', {
+    values: { totalCount },
+    defaultMessage: `{totalCount, plural, =1 {case} other {cases}}`,
+  });
+
+export const SEARCH_CASES = i18n.translate(
+  'xpack.siem.detectionEngine.case.caseTable.searchAriaLabel',
+  {
+    defaultMessage: 'Search cases',
+  }
+);
+
+export const SEARCH_PLACEHOLDER = i18n.translate(
+  'xpack.siem.detectionEngine.case.caseTable.searchPlaceholder',
+  {
+    defaultMessage: 'e.g. case name',
+  }
+);
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
new file mode 100644
index 000000000000..4f43a6edeeac
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
@@ -0,0 +1,312 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useEffect, useState } from 'react';
+import {
+  EuiBadge,
+  EuiButton,
+  EuiButtonEmpty,
+  EuiButtonToggle,
+  EuiDescriptionList,
+  EuiDescriptionListDescription,
+  EuiDescriptionListTitle,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiLoadingSpinner,
+} from '@elastic/eui';
+
+import styled, { css } from 'styled-components';
+import * as i18n from './translations';
+import { DescriptionMarkdown } from '../description_md_editor';
+import { Case } from '../../../../containers/case/types';
+import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date';
+import { getCaseUrl } from '../../../../components/link_to';
+import { HeaderPage } from '../../../../components/header_page_new';
+import { Markdown } from '../../../../components/markdown';
+import { PropertyActions } from '../property_actions';
+import { TagList } from '../tag_list';
+import { useGetCase } from '../../../../containers/case/use_get_case';
+import { UserActionTree } from '../user_action_tree';
+import { UserList } from '../user_list';
+import { useUpdateCase } from '../../../../containers/case/use_update_case';
+import { WrapperPage } from '../../../../components/wrapper_page';
+
+interface Props {
+  caseId: string;
+}
+
+const MyDescriptionList = styled(EuiDescriptionList)`
+  ${({ theme }) => css`
+    & {
+      padding-right: ${theme.eui.euiSizeL};
+      border-right: ${theme.eui.euiBorderThin};
+    }
+  `}
+`;
+
+const MyWrapper = styled(WrapperPage)`
+  padding-bottom: 0;
+`;
+const BackgroundWrapper = styled.div`
+  ${({ theme }) => css`
+    background-color: ${theme.eui.euiColorEmptyShade};
+    border-top: ${theme.eui.euiBorderThin};
+    height: 100%;
+  `}
+`;
+
+interface CasesProps {
+  caseId: string;
+  initialData: Case;
+  isLoading: boolean;
+}
+
+export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading }) => {
+  const [{ data }, dispatchUpdateCaseProperty] = useUpdateCase(caseId, initialData);
+  const [isEditDescription, setIsEditDescription] = useState(false);
+  const [isEditTitle, setIsEditTitle] = useState(false);
+  const [isEditTags, setIsEditTags] = useState(false);
+  const [isCaseOpen, setIsCaseOpen] = useState(data.state === 'open');
+  const [description, setDescription] = useState(data.description);
+  const [title, setTitle] = useState(data.title);
+  const [tags, setTags] = useState(data.tags);
+
+  const onUpdateField = useCallback(
+    async (updateKey: keyof Case, updateValue: string | string[]) => {
+      switch (updateKey) {
+        case 'title':
+          if (updateValue.length > 0) {
+            dispatchUpdateCaseProperty({
+              updateKey: 'title',
+              updateValue,
+            });
+            setIsEditTitle(false);
+          }
+          break;
+        case 'description':
+          if (updateValue.length > 0) {
+            dispatchUpdateCaseProperty({
+              updateKey: 'description',
+              updateValue,
+            });
+            setIsEditDescription(false);
+          }
+          break;
+        case 'tags':
+          setTags(updateValue as string[]);
+          if (updateValue.length > 0) {
+            dispatchUpdateCaseProperty({
+              updateKey: 'tags',
+              updateValue,
+            });
+            setIsEditTags(false);
+          }
+          break;
+        default:
+          return null;
+      }
+    },
+    [dispatchUpdateCaseProperty, title]
+  );
+
+  const onSetIsCaseOpen = useCallback(() => setIsCaseOpen(!isCaseOpen), [
+    isCaseOpen,
+    setIsCaseOpen,
+  ]);
+
+  useEffect(() => {
+    const caseState = isCaseOpen ? 'open' : 'closed';
+    if (data.state !== caseState) {
+      dispatchUpdateCaseProperty({
+        updateKey: 'state',
+        updateValue: caseState,
+      });
+    }
+  }, [isCaseOpen]);
+
+  // TO DO refactor each of these const's into their own components
+  const propertyActions = [
+    {
+      iconType: 'documentEdit',
+      label: 'Edit description',
+      onClick: () => setIsEditDescription(true),
+    },
+    {
+      iconType: 'securitySignalResolved',
+      label: 'Close case',
+      onClick: () => null,
+    },
+    {
+      iconType: 'trash',
+      label: 'Delete case',
+      onClick: () => null,
+    },
+    {
+      iconType: 'importAction',
+      label: 'Push as ServiceNow incident',
+      onClick: () => null,
+    },
+    {
+      iconType: 'popout',
+      label: 'View ServiceNow incident',
+      onClick: () => null,
+    },
+    {
+      iconType: 'importAction',
+      label: 'Update ServiceNow incident',
+      onClick: () => null,
+    },
+  ];
+  const userActions = [
+    {
+      avatarName: data.created_by.username,
+      title: (
+        <EuiFlexGroup alignItems="baseline" gutterSize="none" justifyContent="spaceBetween">
+          <EuiFlexItem grow={false}>
+            <p>
+              <strong>{`${data.created_by.username}`}</strong>
+              {` ${i18n.ADDED_DESCRIPTION} `}{' '}
+              <FormattedRelativePreferenceDate value={data.created_at} />
+              {/* STEPH FIX come back and add label `on` */}
+            </p>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <PropertyActions propertyActions={propertyActions} />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      ),
+      children: isEditDescription ? (
+        <>
+          <DescriptionMarkdown
+            descriptionInputHeight={200}
+            initialDescription={data.description}
+            isLoading={isLoading}
+            onChange={updatedDescription => setDescription(updatedDescription)}
+          />
+
+          <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
+            <EuiFlexItem grow={false}>
+              <EuiButton
+                fill
+                isDisabled={isLoading}
+                isLoading={isLoading}
+                onClick={() => onUpdateField('description', description)}
+              >
+                {i18n.SUBMIT}
+              </EuiButton>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButtonEmpty onClick={() => setIsEditDescription(false)}>
+                {i18n.CANCEL}
+              </EuiButtonEmpty>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </>
+      ) : (
+        <Markdown raw={data.description} />
+      ),
+    },
+  ];
+  return (
+    <>
+      <MyWrapper>
+        <HeaderPage
+          backOptions={{
+            href: getCaseUrl(),
+            text: i18n.BACK_TO_ALL,
+          }}
+          iconAction={{
+            'aria-label': title,
+            iconType: 'pencil',
+            onChange: newTitle => setTitle(newTitle),
+            onSubmit: () => onUpdateField('title', title),
+            onClick: isEdit => setIsEditTitle(isEdit),
+          }}
+          isEditTitle={isEditTitle}
+          title={title}
+        >
+          <EuiFlexGroup gutterSize="l" justifyContent="flexEnd">
+            <EuiFlexItem grow={false}>
+              <MyDescriptionList compressed>
+                <EuiFlexGroup>
+                  <EuiFlexItem grow={false}>
+                    <EuiDescriptionListTitle>{i18n.STATUS}</EuiDescriptionListTitle>
+                    <EuiDescriptionListDescription>
+                      <EuiBadge color={isCaseOpen ? 'secondary' : 'danger'}>{data.state}</EuiBadge>
+                    </EuiDescriptionListDescription>
+                  </EuiFlexItem>
+                  <EuiFlexItem>
+                    <EuiDescriptionListTitle>{i18n.CASE_OPENED}</EuiDescriptionListTitle>
+                    <EuiDescriptionListDescription>
+                      <FormattedRelativePreferenceDate value={data.created_at} />
+                    </EuiDescriptionListDescription>
+                  </EuiFlexItem>
+                </EuiFlexGroup>
+              </MyDescriptionList>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiFlexGroup gutterSize="l" alignItems="center">
+                <EuiFlexItem>
+                  <EuiButtonToggle
+                    label={isCaseOpen ? 'Close case' : 'Reopen case'}
+                    iconType={isCaseOpen ? 'checkInCircleFilled' : 'magnet'}
+                    onChange={onSetIsCaseOpen}
+                    isSelected={isCaseOpen}
+                  />
+                </EuiFlexItem>
+                <EuiFlexItem grow={false}>
+                  <PropertyActions propertyActions={propertyActions} />
+                </EuiFlexItem>
+              </EuiFlexGroup>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </HeaderPage>
+      </MyWrapper>
+      <BackgroundWrapper>
+        <MyWrapper>
+          <EuiFlexGroup>
+            <EuiFlexItem grow={6}>
+              <UserActionTree userActions={userActions} />
+            </EuiFlexItem>
+            <EuiFlexItem grow={2}>
+              <UserList headline={i18n.REPORTER} users={[data.created_by]} />
+              <TagList
+                tags={tags}
+                iconAction={{
+                  'aria-label': title,
+                  iconType: 'pencil',
+                  onSubmit: newTags => onUpdateField('tags', newTags),
+                  onClick: isEdit => setIsEditTags(isEdit),
+                }}
+                isEditTags={isEditTags}
+              />
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </MyWrapper>
+      </BackgroundWrapper>
+    </>
+  );
+});
+
+export const CaseView = React.memo(({ caseId }: Props) => {
+  const [{ data, isLoading, isError }] = useGetCase(caseId);
+  if (isError) {
+    return null;
+  }
+  if (isLoading) {
+    return (
+      <EuiFlexGroup justifyContent="center" alignItems="center">
+        <EuiFlexItem grow={false}>
+          <EuiLoadingSpinner size="xl" />
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    );
+  }
+
+  return <Cases caseId={caseId} initialData={data} isLoading={isLoading} />;
+});
+
+CaseView.displayName = 'CaseView';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts
new file mode 100644
index 000000000000..f45c52533d2e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/translations.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export * from '../../translations';
+
+export const SHOWING_CASES = (actionDate: string, actionName: string, userName: string) =>
+  i18n.translate('xpack.siem.case.caseView.actionHeadline', {
+    values: {
+      actionDate,
+      actionName,
+      userName,
+    },
+    defaultMessage: '{userName} {actionName} on {actionDate}',
+  });
+
+export const ADDED_DESCRIPTION = i18n.translate(
+  'xpack.siem.case.caseView.actionLabel.addDescription',
+  {
+    defaultMessage: 'added description',
+  }
+);
+
+export const EDITED_DESCRIPTION = i18n.translate(
+  'xpack.siem.case.caseView.actionLabel.editDescription',
+  {
+    defaultMessage: 'edited description',
+  }
+);
+
+export const ADDED_COMMENT = i18n.translate('xpack.siem.case.caseView.actionLabel.addComment', {
+  defaultMessage: 'added comment',
+});
+
+export const STATUS = i18n.translate('xpack.siem.case.caseView.statusLabel', {
+  defaultMessage: 'Status',
+});
+
+export const CASE_OPENED = i18n.translate('xpack.siem.case.caseView.caseOpened', {
+  defaultMessage: 'Case opened',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/form_options.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/create/form_options.ts
new file mode 100644
index 000000000000..7bc43e23a72c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/form_options.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const stateOptions = [
+  {
+    value: 'open',
+    inputDisplay: 'Open',
+  },
+  {
+    value: 'closed',
+    inputDisplay: 'Closed',
+  },
+];
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx
new file mode 100644
index 000000000000..9fd1525003b0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx
@@ -0,0 +1,110 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { useCallback } from 'react';
+import {
+  EuiButton,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiHorizontalRule,
+  EuiLoadingSpinner,
+  EuiPanel,
+} from '@elastic/eui';
+import styled from 'styled-components';
+import { Redirect } from 'react-router-dom';
+import { Field, Form, getUseField, useForm } from '../../../shared_imports';
+import { NewCase } from '../../../../containers/case/types';
+import { usePostCase } from '../../../../containers/case/use_post_case';
+import { schema } from './schema';
+import * as i18n from '../../translations';
+import { SiemPageName } from '../../../home/types';
+import { DescriptionMarkdown } from '../description_md_editor';
+
+export const CommonUseField = getUseField({ component: Field });
+
+const TagContainer = styled.div`
+  margin-top: 16px;
+`;
+const MySpinner = styled(EuiLoadingSpinner)`
+  position: absolute;
+  top: 50%;
+  left: 50%;
+`;
+
+export const Create = React.memo(() => {
+  const [{ data, isLoading, newCase }, setFormData] = usePostCase();
+  const { form } = useForm({
+    defaultValue: data,
+    options: { stripEmptyFields: false },
+    schema,
+  });
+
+  const onSubmit = useCallback(async () => {
+    const { isValid, data: newData } = await form.submit();
+    if (isValid) {
+      setFormData({ ...newData, isNew: true } as NewCase);
+    }
+  }, [form]);
+
+  if (newCase && newCase.case_id) {
+    return <Redirect to={`/${SiemPageName.case}/${newCase.case_id}`} />;
+  }
+  return (
+    <EuiPanel>
+      {isLoading && <MySpinner size="xl" />}
+      <Form form={form}>
+        <CommonUseField
+          path="title"
+          componentProps={{
+            idAria: 'caseTitle',
+            'data-test-subj': 'caseTitle',
+            euiFieldProps: {
+              fullWidth: false,
+            },
+            isDisabled: isLoading,
+          }}
+        />
+        <DescriptionMarkdown
+          descriptionInputHeight={200}
+          formHook={true}
+          initialDescription={data.description}
+          isLoading={isLoading}
+          onChange={description => setFormData({ ...data, description })}
+        />
+        <TagContainer>
+          <CommonUseField
+            path="tags"
+            componentProps={{
+              idAria: 'caseTags',
+              'data-test-subj': 'caseTags',
+              euiFieldProps: {
+                fullWidth: true,
+                placeholder: '',
+              },
+              isDisabled: isLoading,
+            }}
+          />
+        </TagContainer>
+      </Form>
+      <>
+        <EuiHorizontalRule margin="m" />
+        <EuiFlexGroup
+          alignItems="center"
+          justifyContent="flexEnd"
+          gutterSize="xs"
+          responsive={false}
+        >
+          <EuiFlexItem grow={false}>
+            <EuiButton fill isDisabled={isLoading} isLoading={isLoading} onClick={onSubmit}>
+              {i18n.SUBMIT}
+            </EuiButton>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </>
+    </EuiPanel>
+  );
+});
+
+Create.displayName = 'Create';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/optional_field_label/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/optional_field_label/index.tsx
new file mode 100644
index 000000000000..b86198e09cea
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/optional_field_label/index.tsx
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiText } from '@elastic/eui';
+import React from 'react';
+
+import * as i18n from '../../../translations';
+
+export const OptionalFieldLabel = (
+  <EuiText color="subdued" size="xs">
+    {i18n.OPTIONAL}
+  </EuiText>
+);
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx
new file mode 100644
index 000000000000..1b5df72a6671
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/schema.tsx
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FIELD_TYPES, fieldValidators, FormSchema } from '../../../shared_imports';
+import { OptionalFieldLabel } from './optional_field_label';
+import * as i18n from '../../translations';
+
+const { emptyField } = fieldValidators;
+
+export const schema: FormSchema = {
+  title: {
+    type: FIELD_TYPES.TEXT,
+    label: i18n.CASE_TITLE,
+    validations: [
+      {
+        validator: emptyField(i18n.TITLE_REQUIRED),
+      },
+    ],
+  },
+  description: {
+    type: FIELD_TYPES.TEXTAREA,
+    validations: [
+      {
+        validator: emptyField(i18n.DESCRIPTION_REQUIRED),
+      },
+    ],
+  },
+  tags: {
+    type: FIELD_TYPES.COMBO_BOX,
+    label: i18n.TAGS,
+    helpText: i18n.TAGS_HELP,
+    labelAppend: OptionalFieldLabel,
+  },
+};
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/description_md_editor/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/description_md_editor/index.tsx
new file mode 100644
index 000000000000..44062a5a1d58
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/description_md_editor/index.tsx
@@ -0,0 +1,111 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiFlexItem, EuiPanel, EuiTabbedContent, EuiTextArea } from '@elastic/eui';
+import React, { useState } from 'react';
+import styled from 'styled-components';
+
+import { Markdown } from '../../../../components/markdown';
+import * as i18n from '../../translations';
+import { MarkdownHint } from '../../../../components/markdown/markdown_hint';
+import { CommonUseField } from '../create';
+
+const TextArea = styled(EuiTextArea)<{ height: number }>`
+  min-height: ${({ height }) => `${height}px`};
+  width: 100%;
+`;
+
+TextArea.displayName = 'TextArea';
+
+const DescriptionContainer = styled.div`
+  margin-top: 15px;
+  margin-bottom: 15px;
+`;
+
+const DescriptionMarkdownTabs = styled(EuiTabbedContent)`
+  width: 100%;
+`;
+
+DescriptionMarkdownTabs.displayName = 'DescriptionMarkdownTabs';
+
+const MarkdownContainer = styled(EuiPanel)<{ height: number }>`
+  height: ${({ height }) => height}px;
+  overflow: auto;
+`;
+
+MarkdownContainer.displayName = 'MarkdownContainer';
+
+/** An input for entering a new case description  */
+export const DescriptionMarkdown = React.memo<{
+  descriptionInputHeight: number;
+  initialDescription: string;
+  isLoading: boolean;
+  formHook?: boolean;
+  onChange: (description: string) => void;
+}>(({ initialDescription, isLoading, descriptionInputHeight, onChange, formHook = false }) => {
+  const [description, setDescription] = useState(initialDescription);
+  const tabs = [
+    {
+      id: 'description',
+      name: i18n.DESCRIPTION,
+      content: formHook ? (
+        <CommonUseField
+          path="description"
+          onChange={e => {
+            setDescription(e as string);
+            onChange(e as string);
+          }}
+          componentProps={{
+            idAria: 'caseDescription',
+            'data-test-subj': 'caseDescription',
+            isDisabled: isLoading,
+            spellcheck: false,
+          }}
+        />
+      ) : (
+        <TextArea
+          onChange={e => {
+            setDescription(e.target.value);
+            onChange(e.target.value);
+          }}
+          fullWidth={true}
+          height={descriptionInputHeight}
+          aria-label={i18n.DESCRIPTION}
+          disabled={isLoading}
+          spellCheck={false}
+          value={description}
+        />
+      ),
+    },
+    {
+      id: 'preview',
+      name: i18n.PREVIEW,
+      content: (
+        <MarkdownContainer
+          data-test-subj="markdown-container"
+          height={descriptionInputHeight}
+          paddingSize="s"
+        >
+          <Markdown raw={description} />
+        </MarkdownContainer>
+      ),
+    },
+  ];
+  return (
+    <DescriptionContainer>
+      <DescriptionMarkdownTabs
+        data-test-subj="new-description-tabs"
+        tabs={tabs}
+        initialSelectedTab={tabs[0]}
+      />
+      <EuiFlexItem grow={true}>
+        <MarkdownHint show={description.trim().length > 0} />
+      </EuiFlexItem>
+    </DescriptionContainer>
+  );
+});
+
+DescriptionMarkdown.displayName = 'DescriptionMarkdown';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/constants.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/constants.ts
new file mode 100644
index 000000000000..14e4b46eb83f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/constants.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const SET_STATE = 'SET_STATE';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx
new file mode 100644
index 000000000000..7fe5b6f5f879
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx
@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useState } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiPopover, EuiButtonIcon, EuiButtonEmpty } from '@elastic/eui';
+
+export interface PropertyActionButtonProps {
+  onClick: () => void;
+  iconType: string;
+  label: string;
+}
+
+const PropertyActionButton = React.memo<PropertyActionButtonProps>(
+  ({ onClick, iconType, label }) => (
+    <EuiButtonEmpty
+      aria-label={label}
+      color="text"
+      iconSide="left"
+      iconType={iconType}
+      onClick={onClick}
+    >
+      {label}
+    </EuiButtonEmpty>
+  )
+);
+
+PropertyActionButton.displayName = 'PropertyActionButton';
+
+export interface PropertyActionsProps {
+  propertyActions: PropertyActionButtonProps[];
+}
+
+export const PropertyActions = React.memo<PropertyActionsProps>(({ propertyActions }) => {
+  const [showActions, setShowActions] = useState(false);
+
+  const onButtonClick = useCallback(() => {
+    setShowActions(!showActions);
+  }, [showActions]);
+
+  const onClosePopover = useCallback((cb?: () => void) => {
+    setShowActions(false);
+    if (cb) {
+      cb();
+    }
+  }, []);
+
+  return (
+    <EuiFlexGroup alignItems="flexStart" data-test-subj="properties-right" gutterSize="none">
+      <EuiFlexItem grow={false}>
+        <EuiPopover
+          anchorPosition="downRight"
+          button={
+            <EuiButtonIcon
+              data-test-subj="ellipses"
+              aria-label="Actions"
+              iconType="boxesHorizontal"
+              onClick={onButtonClick}
+            />
+          }
+          id="settingsPopover"
+          isOpen={showActions}
+          closePopover={onClosePopover}
+        >
+          <EuiFlexGroup alignItems="flexStart" direction="column" gutterSize="none">
+            {propertyActions.map((action, key) => (
+              <EuiFlexItem grow={false} key={`${action.label}${key}`}>
+                <PropertyActionButton
+                  iconType={action.iconType}
+                  label={action.label}
+                  onClick={() => onClosePopover(action.onClick)}
+                />
+              </EuiFlexItem>
+            ))}
+          </EuiFlexGroup>
+        </EuiPopover>
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+});
+
+PropertyActions.displayName = 'PropertyActions';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx
new file mode 100644
index 000000000000..6634672cb6a7
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx
@@ -0,0 +1,125 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback } from 'react';
+import {
+  EuiText,
+  EuiHorizontalRule,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiBadge,
+  EuiButton,
+  EuiButtonEmpty,
+  EuiButtonIcon,
+} from '@elastic/eui';
+import styled, { css } from 'styled-components';
+import * as i18n from '../../translations';
+import { Form, useForm } from '../../../shared_imports';
+import { schema } from './schema';
+import { CommonUseField } from '../create';
+
+interface IconAction {
+  'aria-label': string;
+  iconType: string;
+  onClick: (b: boolean) => void;
+  onSubmit: (a: string[]) => void;
+}
+
+interface TagListProps {
+  tags: string[];
+  iconAction?: IconAction;
+  isEditTags?: boolean;
+}
+
+const MyFlexGroup = styled(EuiFlexGroup)`
+  ${({ theme }) => css`
+    margin-top: ${theme.eui.euiSizeM};
+    p {
+      font-size: ${theme.eui.euiSizeM};
+    }
+  `}
+`;
+
+export const TagList = React.memo(({ tags, isEditTags, iconAction }: TagListProps) => {
+  const { form } = useForm({
+    defaultValue: { tags },
+    options: { stripEmptyFields: false },
+    schema,
+  });
+
+  const onSubmit = useCallback(async () => {
+    const { isValid, data: newData } = await form.submit();
+    if (isValid && iconAction) {
+      iconAction.onSubmit(newData.tags);
+      iconAction.onClick(false);
+    }
+  }, [form]);
+
+  const onActionClick = useCallback(
+    (cb: (b: boolean) => void, onClickBool: boolean) => cb(onClickBool),
+    [iconAction]
+  );
+  return (
+    <EuiText>
+      <EuiFlexGroup alignItems="center" gutterSize="xs" justifyContent="spaceBetween">
+        <EuiFlexItem grow={false}>
+          <h4>{i18n.TAGS}</h4>
+        </EuiFlexItem>
+        {iconAction && (
+          <EuiFlexItem grow={false}>
+            <EuiButtonIcon
+              aria-label={iconAction['aria-label']}
+              iconType={iconAction.iconType}
+              onClick={() => onActionClick(iconAction.onClick, true)}
+            />
+          </EuiFlexItem>
+        )}
+      </EuiFlexGroup>
+      <EuiHorizontalRule margin="xs" />
+      <MyFlexGroup gutterSize="xs">
+        {tags.length === 0 && !isEditTags && <p>{i18n.NO_TAGS}</p>}
+        {tags.length > 0 &&
+          !isEditTags &&
+          tags.map((tag, key) => (
+            <EuiFlexItem grow={false} key={`${tag}${key}`}>
+              <EuiBadge color="hollow">{tag}</EuiBadge>
+            </EuiFlexItem>
+          ))}
+        {isEditTags && iconAction && (
+          <EuiFlexGroup direction="column">
+            <EuiFlexItem>
+              <Form form={form}>
+                <CommonUseField
+                  path="tags"
+                  componentProps={{
+                    idAria: 'caseTags',
+                    'data-test-subj': 'caseTags',
+                    euiFieldProps: {
+                      fullWidth: true,
+                      placeholder: '',
+                    },
+                  }}
+                />
+              </Form>
+            </EuiFlexItem>
+            <EuiFlexItem>
+              <EuiButton fill onClick={onSubmit}>
+                {i18n.SUBMIT}
+              </EuiButton>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButtonEmpty onClick={() => onActionClick(iconAction.onClick, false)}>
+                {i18n.CANCEL}
+              </EuiButtonEmpty>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        )}
+      </MyFlexGroup>
+    </EuiText>
+  );
+});
+
+TagList.displayName = 'TagList';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/schema.tsx
new file mode 100644
index 000000000000..dfc9c61cd5f0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/schema.tsx
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { FormSchema } from '../../../shared_imports';
+import { schema as createSchema } from '../create/schema';
+
+export const schema: FormSchema = {
+  tags: createSchema.tags,
+};
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx
new file mode 100644
index 000000000000..8df98a4cef0e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { ReactNode } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiAvatar, EuiPanel, EuiText } from '@elastic/eui';
+import styled, { css } from 'styled-components';
+
+export interface UserActionItem {
+  avatarName: string;
+  children?: ReactNode;
+  title: ReactNode;
+}
+
+export interface UserActionTreeProps {
+  userActions: UserActionItem[];
+}
+
+const UserAction = styled(EuiFlexGroup)`
+  ${({ theme }) => css`
+    & {
+      background-image: linear-gradient(
+        to right,
+        transparent 0,
+        transparent 15px,
+        ${theme.eui.euiBorderColor} 15px,
+        ${theme.eui.euiBorderColor} 17px,
+        transparent 17px,
+        transparent 100%
+      );
+      background-repeat: no-repeat;
+      background-position: left ${theme.eui.euiSizeXXL};
+      margin-bottom: ${theme.eui.euiSizeS};
+    }
+    .userAction__panel {
+      margin-bottom: ${theme.eui.euiSize};
+    }
+    .userAction__circle {
+      flex-shrink: 0;
+      margin-right: ${theme.eui.euiSize};
+      vertical-align: top;
+    }
+    .userAction__title {
+      padding: ${theme.eui.euiSizeS} ${theme.eui.euiSizeL};
+      background: ${theme.eui.euiColorLightestShade};
+      border-bottom: ${theme.eui.euiBorderThin};
+      border-radius: ${theme.eui.euiBorderRadius} ${theme.eui.euiBorderRadius} 0 0;
+    }
+    .userAction__content {
+      padding: ${theme.eui.euiSizeM} ${theme.eui.euiSizeL};
+    }
+    .euiText--small * {
+      margin-bottom: 0;
+    }
+  `}
+`;
+
+const renderUserActions = (userActions: UserActionItem[]) => {
+  return userActions.map(({ avatarName, children, title }, key) => (
+    <UserAction key={key} gutterSize={'none'}>
+      <EuiFlexItem grow={false}>
+        <EuiAvatar className="userAction__circle" name={avatarName} />
+      </EuiFlexItem>
+      <EuiFlexItem>
+        <EuiPanel className="userAction__panel" paddingSize="none">
+          <EuiText size="s" className="userAction__title">
+            {title}
+          </EuiText>
+          {children && <div className="userAction__content">{children}</div>}
+        </EuiPanel>
+      </EuiFlexItem>
+    </UserAction>
+  ));
+};
+
+export const UserActionTree = React.memo(({ userActions }: UserActionTreeProps) => (
+  <div>{renderUserActions(userActions)}</div>
+));
+
+UserActionTree.displayName = 'UserActionTree';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx
new file mode 100644
index 000000000000..b80ee58f8abb
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import {
+  EuiButtonIcon,
+  EuiText,
+  EuiHorizontalRule,
+  EuiAvatar,
+  EuiFlexGroup,
+  EuiFlexItem,
+} from '@elastic/eui';
+import styled, { css } from 'styled-components';
+import { ElasticUser } from '../../../../containers/case/types';
+
+interface UserListProps {
+  headline: string;
+  users: ElasticUser[];
+}
+
+const MyAvatar = styled(EuiAvatar)`
+  top: -4px;
+`;
+
+const MyFlexGroup = styled(EuiFlexGroup)`
+  ${({ theme }) => css`
+    margin-top: ${theme.eui.euiSizeM};
+  `}
+`;
+
+const renderUsers = (users: ElasticUser[]) => {
+  return users.map(({ username }, key) => (
+    <MyFlexGroup key={key} justifyContent="spaceBetween">
+      <EuiFlexItem grow={false}>
+        <EuiFlexGroup gutterSize="xs">
+          <EuiFlexItem>
+            <MyAvatar name={username} />
+          </EuiFlexItem>
+          <EuiFlexItem>
+            <p>
+              <strong>
+                <small>{username}</small>
+              </strong>
+            </p>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiFlexItem>
+      <EuiFlexItem grow={false}>
+        <EuiButtonIcon
+          onClick={() => window.alert('Email clicked')}
+          iconType="email"
+          aria-label="email"
+        />
+      </EuiFlexItem>
+    </MyFlexGroup>
+  ));
+};
+
+export const UserList = React.memo(({ headline, users }: UserListProps) => {
+  return (
+    <EuiText>
+      <h4>{headline}</h4>
+      <EuiHorizontalRule margin="xs" />
+      {renderUsers(users)}
+    </EuiText>
+  );
+});
+
+UserList.displayName = 'UserList';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx b/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx
new file mode 100644
index 000000000000..9bc356517cc6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/create_case.tsx
@@ -0,0 +1,35 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { WrapperPage } from '../../components/wrapper_page';
+import { Create } from './components/create';
+import { SpyRoute } from '../../utils/route/spy_routes';
+import { HeaderPage } from '../../components/header_page';
+import * as i18n from './translations';
+import { getCaseUrl } from '../../components/link_to';
+
+const backOptions = {
+  href: getCaseUrl(),
+  text: i18n.BACK_TO_ALL,
+};
+const badgeOptions = {
+  beta: true,
+  text: i18n.PAGE_BADGE_LABEL,
+  tooltip: i18n.PAGE_BADGE_TOOLTIP,
+};
+export const CreateCasePage = React.memo(() => (
+  <>
+    <WrapperPage>
+      <HeaderPage backOptions={backOptions} badgeOptions={badgeOptions} title={i18n.CREATE_TITLE} />
+      <Create />
+    </WrapperPage>
+    <SpyRoute />
+  </>
+));
+
+CreateCasePage.displayName = 'CreateCasePage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/index.tsx
new file mode 100644
index 000000000000..9bd91b1c6d62
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/index.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { Route, Switch } from 'react-router-dom';
+import { SiemPageName } from '../home/types';
+import { CaseDetailsPage } from './case_details';
+import { CasesPage } from './case';
+import { CreateCasePage } from './create_case';
+
+const casesPagePath = `/:pageName(${SiemPageName.case})`;
+const caseDetailsPagePath = `${casesPagePath}/:detailName`;
+const createCasePagePath = `${casesPagePath}/create`;
+
+const CaseContainerComponent: React.FC = () => (
+  <Switch>
+    <Route strict exact path={casesPagePath}>
+      <CasesPage />
+    </Route>
+    <Route strict exact path={createCasePagePath}>
+      <CreateCasePage />
+    </Route>
+    <Route strict path={caseDetailsPagePath}>
+      <CaseDetailsPage />
+    </Route>
+  </Switch>
+);
+
+export const Case = React.memo(CaseContainerComponent);
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts
new file mode 100644
index 000000000000..4e878ba58411
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const BACK_TO_ALL = i18n.translate('xpack.siem.case.caseView.backLabel', {
+  defaultMessage: 'Back to cases',
+});
+
+export const CANCEL = i18n.translate('xpack.siem.case.caseView.cancel', {
+  defaultMessage: 'Cancel',
+});
+
+export const CASE_TITLE = i18n.translate('xpack.siem.case.caseView.caseTitle', {
+  defaultMessage: 'Case Title',
+});
+
+export const CREATED_AT = i18n.translate('xpack.siem.case.caseView.createdAt', {
+  defaultMessage: 'Created at',
+});
+
+export const REPORTER = i18n.translate('xpack.siem.case.caseView.createdBy', {
+  defaultMessage: 'Reporter',
+});
+
+export const CREATE_BC_TITLE = i18n.translate('xpack.siem.case.caseView.breadcrumb', {
+  defaultMessage: 'Create',
+});
+
+export const CREATE_TITLE = i18n.translate('xpack.siem.case.caseView.create', {
+  defaultMessage: 'Create new case',
+});
+
+export const DESCRIPTION = i18n.translate('xpack.siem.case.caseView.description', {
+  defaultMessage: 'Description',
+});
+
+export const DESCRIPTION_REQUIRED = i18n.translate(
+  'xpack.siem.case.createCase.descriptionFieldRequiredError',
+  {
+    defaultMessage: 'A description is required.',
+  }
+);
+
+export const EDIT = i18n.translate('xpack.siem.case.caseView.edit', {
+  defaultMessage: 'Edit',
+});
+
+export const OPTIONAL = i18n.translate('xpack.siem.case.caseView.optional', {
+  defaultMessage: 'Optional',
+});
+
+export const LAST_UPDATED = i18n.translate('xpack.siem.case.caseView.updatedAt', {
+  defaultMessage: 'Last updated',
+});
+
+export const PAGE_BADGE_LABEL = i18n.translate('xpack.siem.case.caseView.pageBadgeLabel', {
+  defaultMessage: 'Beta',
+});
+
+export const PAGE_BADGE_TOOLTIP = i18n.translate('xpack.siem.case.caseView.pageBadgeTooltip', {
+  defaultMessage:
+    'Case Workflow is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo.',
+});
+
+export const PAGE_SUBTITLE = i18n.translate('xpack.siem.case.caseView.pageSubtitle', {
+  defaultMessage: 'Case Workflow Management within the Elastic SIEM',
+});
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.case.pageTitle', {
+  defaultMessage: 'Case Workflows',
+});
+
+export const PREVIEW = i18n.translate('xpack.siem.case.caseView.preview', {
+  defaultMessage: 'Preview',
+});
+
+export const STATE = i18n.translate('xpack.siem.case.caseView.state', {
+  defaultMessage: 'State',
+});
+
+export const SUBMIT = i18n.translate('xpack.siem.case.caseView.submit', {
+  defaultMessage: 'Submit',
+});
+
+export const TAGS = i18n.translate('xpack.siem.case.caseView.tags', {
+  defaultMessage: 'Tags',
+});
+
+export const TAGS_HELP = i18n.translate('xpack.siem.case.createCase.fieldTagsHelpText', {
+  defaultMessage:
+    'Type one or more custom identifying tags for this case. Press enter after each tag to begin a new one.',
+});
+
+export const NO_TAGS = i18n.translate('xpack.siem.case.caseView.noTags', {
+  defaultMessage: 'No tags are currently assigned to this case.',
+});
+
+export const TITLE_REQUIRED = i18n.translate('xpack.siem.case.createCase.titleFieldRequiredError', {
+  defaultMessage: 'A title is required.',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/utils.ts b/x-pack/legacy/plugins/siem/public/pages/case/utils.ts
new file mode 100644
index 000000000000..bd6cb5da5eb0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/utils.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Breadcrumb } from 'ui/chrome';
+import { getCaseDetailsUrl, getCaseUrl, getCreateCaseUrl } from '../../components/link_to';
+import { RouteSpyState } from '../../utils/route/types';
+import * as i18n from './translations';
+
+export const getBreadcrumbs = (params: RouteSpyState): Breadcrumb[] => {
+  let breadcrumb = [
+    {
+      text: i18n.PAGE_TITLE,
+      href: getCaseUrl(),
+    },
+  ];
+  if (params.detailName === 'create') {
+    breadcrumb = [
+      ...breadcrumb,
+      {
+        text: i18n.CREATE_BC_TITLE,
+        href: getCreateCaseUrl(),
+      },
+    ];
+  } else if (params.detailName != null) {
+    breadcrumb = [
+      ...breadcrumb,
+      {
+        text: params.detailName,
+        href: getCaseDetailsUrl(params.detailName),
+      },
+    ];
+  }
+  return breadcrumb;
+};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
index c54a2e8d4984..fa4f6a874ca5 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
@@ -13,6 +13,7 @@ import {
   EuiFlexGroup,
   EuiFlexItem,
 } from '@elastic/eui';
+import { isEqual } from 'lodash/fp';
 import * as i18n from '../../translations';
 
 import { FilterOptions } from '../../../../../containers/detection_engine/rules';
@@ -59,6 +60,15 @@ const RulesTableFiltersComponent = ({
     setShowElasticRules(false);
   }, [setShowElasticRules, showCustomRules, setShowCustomRules]);
 
+  const handleSelectedTags = useCallback(
+    newTags => {
+      if (!isEqual(newTags, selectedTags)) {
+        setSelectedTags(newTags);
+      }
+    },
+    [selectedTags]
+  );
+
   return (
     <EuiFlexGroup gutterSize="m" justifyContent="flexEnd">
       <EuiFlexItem grow={true}>
@@ -74,9 +84,10 @@ const RulesTableFiltersComponent = ({
       <EuiFlexItem grow={false}>
         <EuiFilterGroup>
           <TagsFilterPopover
-            tags={tags}
-            onSelectedTagsChanged={setSelectedTags}
             isLoading={isLoadingTags}
+            onSelectedTagsChanged={handleSelectedTags}
+            selectedTags={selectedTags}
+            tags={tags}
           />
         </EuiFilterGroup>
       </EuiFlexItem>
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
index b9d2c97f063b..44149a072f5c 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
+import React, { Dispatch, SetStateAction, useState } from 'react';
 import {
   EuiFilterButton,
   EuiFilterSelectItem,
@@ -19,9 +19,10 @@ import * as i18n from '../../translations';
 import { toggleSelectedGroup } from '../../../../../components/ml_popover/jobs_table/filters/toggle_selected_group';
 
 interface TagsFilterPopoverProps {
+  selectedTags: string[];
   tags: string[];
   onSelectedTagsChanged: Dispatch<SetStateAction<string[]>>;
-  isLoading: boolean;
+  isLoading: boolean; // TO DO reimplement?
 }
 
 const ScrollableDiv = styled.div`
@@ -37,14 +38,10 @@ const ScrollableDiv = styled.div`
  */
 export const TagsFilterPopoverComponent = ({
   tags,
+  selectedTags,
   onSelectedTagsChanged,
 }: TagsFilterPopoverProps) => {
   const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false);
-  const [selectedTags, setSelectedTags] = useState<string[]>([]);
-
-  useEffect(() => {
-    onSelectedTagsChanged(selectedTags);
-  }, [selectedTags.sort().join()]);
 
   return (
     <EuiPopover
@@ -70,7 +67,7 @@ export const TagsFilterPopoverComponent = ({
           <EuiFilterSelectItem
             checked={selectedTags.includes(tag) ? 'on' : undefined}
             key={`${index}-${tag}`}
-            onClick={() => toggleSelectedGroup(tag, selectedTags, setSelectedTags)}
+            onClick={() => toggleSelectedGroup(tag, selectedTags, onSelectedTagsChanged)}
           >
             {`${tag}`}
           </EuiFilterSelectItem>
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.tsx
index 0c75da7d8a63..cc5e9b38eb2f 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.tsx
@@ -18,7 +18,7 @@ import React, { ChangeEvent, useCallback, useEffect, useState, useRef } from 're
 import styled from 'styled-components';
 
 import * as RuleI18n from '../../translations';
-import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
+import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports';
 
 interface AddItemProps {
   addText: string;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
index 09f4c13acbf6..1cc7bba5558d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
@@ -19,7 +19,7 @@ import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/searc
 import { useKibana } from '../../../../../lib/kibana';
 import { IMitreEnterpriseAttack } from '../../types';
 import { FieldValueTimeline } from '../pick_timeline';
-import { FormSchema } from '../shared_imports';
+import { FormSchema } from '../../../../shared_imports';
 import { ListItems } from './types';
 import {
   buildQueryBarDescription,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
index d85be053065f..b49126c8c0fe 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
@@ -20,7 +20,7 @@ import styled from 'styled-components';
 
 import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_techniques';
 import * as Rulei18n from '../../translations';
-import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
+import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports';
 import { threatDefault } from '../step_about_rule/default_value';
 import { IMitreEnterpriseAttack } from '../../types';
 import { MyAddItemButton } from '../add_item_form';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.tsx
index f467d0ebede4..56cb02c9ec81 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.tsx
@@ -8,7 +8,7 @@ import { EuiFormRow } from '@elastic/eui';
 import React, { useCallback, useEffect, useState } from 'react';
 
 import { SearchTimelineSuperSelect } from '../../../../../components/timeline/search_super_select';
-import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
+import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports';
 
 export interface FieldValueTimeline {
   id: string | null;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
index 7f55d76c6d6b..88795f9195e6 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
@@ -29,7 +29,7 @@ import { convertKueryToElasticSearchQuery } from '../../../../../lib/keury';
 import { useKibana } from '../../../../../lib/kibana';
 import { TimelineModel } from '../../../../../store/timeline/model';
 import { useSavedQueryServices } from '../../../../../utils/saved_query_services';
-import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
+import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports';
 import * as i18n from './translations';
 
 export interface FieldValueQueryBar {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.tsx
index 3bde2087f26b..ffb6c4eda324 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.tsx
@@ -16,7 +16,7 @@ import { isEmpty } from 'lodash/fp';
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import styled from 'styled-components';
 
-import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
+import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports';
 
 import * as I18n from './translations';
 
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
index 9c351e66c2f0..45da7d081333 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
@@ -23,7 +23,14 @@ import * as RuleI18n from '../../translations';
 import { AddItem } from '../add_item_form';
 import { StepRuleDescription } from '../description_step';
 import { AddMitreThreat } from '../mitre';
-import { Field, Form, FormDataProvider, getUseField, UseField, useForm } from '../shared_imports';
+import {
+  Field,
+  Form,
+  FormDataProvider,
+  getUseField,
+  UseField,
+  useForm,
+} from '../../../../shared_imports';
 
 import { defaultRiskScoreBySeverity, severityOptions, SeverityValue } from './data';
 import { stepAboutDefaultValue } from './default_value';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
index 22033dcf6b0f..27887bcbbe60 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
@@ -13,7 +13,7 @@ import {
   FormSchema,
   ValidationFunc,
   ERROR_CODE,
-} from '../shared_imports';
+} from '../../../../shared_imports';
 import { isMitreAttackInvalid } from '../mitre/helpers';
 import { OptionalFieldLabel } from '../optional_field_label';
 import { isUrlInvalid } from './helpers';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
index 5409a5f161bb..920a9f2dfe56 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
@@ -25,7 +25,14 @@ import { DefineStepRule, RuleStep, RuleStepProps } from '../../types';
 import { StepRuleDescription } from '../description_step';
 import { QueryBarDefineRule } from '../query_bar';
 import { StepContentWrapper } from '../step_content_wrapper';
-import { Field, Form, FormDataProvider, getUseField, UseField, useForm } from '../shared_imports';
+import {
+  Field,
+  Form,
+  FormDataProvider,
+  getUseField,
+  UseField,
+  useForm,
+} from '../../../../shared_imports';
 import { schema } from './schema';
 import * as i18n from './translations';
 
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
index 079ec0dab4c5..bb178d719706 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
@@ -17,7 +17,7 @@ import {
   fieldValidators,
   FormSchema,
   ValidationFunc,
-} from '../shared_imports';
+} from '../../../../shared_imports';
 import { CUSTOM_QUERY_REQUIRED, INVALID_CUSTOM_QUERY, INDEX_HELPER_TEXT } from './translations';
 
 const { emptyField } = fieldValidators;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
index 532df628a83a..cfbb0a622c72 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
@@ -12,7 +12,7 @@ import { setFieldValue } from '../../helpers';
 import { RuleStep, RuleStepProps, ScheduleStepRule } from '../../types';
 import { StepRuleDescription } from '../description_step';
 import { ScheduleItem } from '../schedule_item_form';
-import { Form, UseField, useForm } from '../shared_imports';
+import { Form, UseField, useForm } from '../../../../shared_imports';
 import { StepContentWrapper } from '../step_content_wrapper';
 import { schema } from './schema';
 import * as I18n from './translations';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
index a951c1fab7cc..9932e4f6ef43 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
@@ -7,7 +7,7 @@
 import { i18n } from '@kbn/i18n';
 
 import { OptionalFieldLabel } from '../optional_field_label';
-import { FormSchema } from '../shared_imports';
+import { FormSchema } from '../../../../shared_imports';
 
 export const schema: FormSchema = {
   interval: {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx
index 3adc22329ac4..c985045b1897 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx
@@ -17,7 +17,7 @@ import { displaySuccessToast, useStateToaster } from '../../../../components/toa
 import { SpyRoute } from '../../../../utils/route/spy_routes';
 import { useUserInfo } from '../../components/user_info';
 import { AccordionTitle } from '../components/accordion_title';
-import { FormData, FormHook } from '../components/shared_imports';
+import { FormData, FormHook } from '../../../shared_imports';
 import { StepAboutRule } from '../components/step_about_rule';
 import { StepDefineRule } from '../components/step_define_rule';
 import { StepScheduleRule } from '../components/step_schedule_rule';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx
index 99fcff6b8d2f..0fac4641e54a 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx
@@ -26,7 +26,7 @@ import { displaySuccessToast, useStateToaster } from '../../../../components/toa
 import { SpyRoute } from '../../../../utils/route/spy_routes';
 import { useUserInfo } from '../../components/user_info';
 import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page';
-import { FormHook, FormData } from '../components/shared_imports';
+import { FormHook, FormData } from '../../../shared_imports';
 import { StepPanel } from '../components/step_panel';
 import { StepAboutRule } from '../components/step_about_rule';
 import { StepDefineRule } from '../components/step_define_rule';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
index cfff71851b2e..3fab456d856c 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
@@ -11,7 +11,7 @@ import { useLocation } from 'react-router-dom';
 
 import { Filter } from '../../../../../../../../src/plugins/data/public';
 import { Rule } from '../../../containers/detection_engine/rules';
-import { FormData, FormHook, FormSchema } from './components/shared_imports';
+import { FormData, FormHook, FormSchema } from '../../shared_imports';
 import { AboutStepRule, DefineStepRule, IMitreEnterpriseAttack, ScheduleStepRule } from './types';
 
 interface GetStepsData {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
index fc2e3fba2444..55eb45fb5ed9 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
@@ -7,7 +7,7 @@
 import { Filter } from '../../../../../../../../src/plugins/data/common';
 import { Rule } from '../../../containers/detection_engine/rules';
 import { FieldValueQueryBar } from './components/query_bar';
-import { FormData, FormHook } from './components/shared_imports';
+import { FormData, FormHook } from '../../shared_imports';
 import { FieldValueTimeline } from './components/pick_timeline';
 
 export interface EuiBasicTableSortTypes {
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
index c0e959c5e97f..42d333f4f893 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
@@ -10,6 +10,7 @@ import {
   getNetworkUrl,
   getTimelinesUrl,
   getHostsUrl,
+  getCaseUrl,
 } from '../../components/link_to';
 import * as i18n from './translations';
 import { SiemPageName, SiemNavTab } from './types';
@@ -50,4 +51,11 @@ export const navTabs: SiemNavTab = {
     disabled: false,
     urlKey: 'timeline',
   },
+  [SiemPageName.case]: {
+    id: SiemPageName.case,
+    name: i18n.CASE,
+    href: getCaseUrl(),
+    disabled: true,
+    urlKey: 'case',
+  },
 };
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
index fd7c536d5408..1dce26b7c5d3 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
@@ -26,6 +26,7 @@ import { DetectionEngineContainer } from '../detection_engine';
 import { HostsContainer } from '../hosts';
 import { NetworkContainer } from '../network';
 import { Overview } from '../overview';
+import { Case } from '../case';
 import { Timelines } from '../timelines';
 import { navTabs } from './home_navigations';
 import { SiemPageName } from './types';
@@ -42,6 +43,11 @@ const WrappedByAutoSizer = styled.div`
 `;
 WrappedByAutoSizer.displayName = 'WrappedByAutoSizer';
 
+const Main = styled.main`
+  height: 100%;
+`;
+Main.displayName = 'Main';
+
 const usersViewing = ['elastic']; // TODO: get the users viewing this timeline from Elasticsearch (persistance)
 
 /** the global Kibana navigation at the top of every page */
@@ -61,7 +67,7 @@ export const HomePage: React.FC = () => (
       <WrappedByAutoSizer data-test-subj="wrapped-by-auto-sizer" ref={measureRef}>
         <HeaderGlobal />
 
-        <main data-test-subj="pageContainer">
+        <Main data-test-subj="pageContainer">
           <WithSource sourceId="default">
             {({ browserFields, indexPattern, indicesExist }) => (
               <DragDropContextWrapper browserFields={browserFields}>
@@ -129,12 +135,15 @@ export const HomePage: React.FC = () => (
                       <MlNetworkConditionalContainer location={location} url={match.url} />
                     )}
                   />
+                  <Route path={`/:pageName(${SiemPageName.case})`}>
+                    <Case />
+                  </Route>
                   <Route render={() => <NotFoundPage />} />
                 </Switch>
               </DragDropContextWrapper>
             )}
           </WithSource>
-        </main>
+        </Main>
 
         <HelpMenu />
 
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
index 80800a3bd419..581c81d9f98a 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
@@ -25,3 +25,7 @@ export const DETECTION_ENGINE = i18n.translate('xpack.siem.navigation.detectionE
 export const TIMELINES = i18n.translate('xpack.siem.navigation.timelines', {
   defaultMessage: 'Timelines',
 });
+
+export const CASE = i18n.translate('xpack.siem.navigation.case', {
+  defaultMessage: 'Case',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/types.ts b/x-pack/legacy/plugins/siem/public/pages/home/types.ts
index 678de6dbcc12..6445ac91d9e1 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/home/types.ts
@@ -12,6 +12,7 @@ export enum SiemPageName {
   network = 'network',
   detections = 'detections',
   timelines = 'timelines',
+  case = 'case',
 }
 
 export type SiemNavTabKey =
@@ -19,6 +20,7 @@ export type SiemNavTabKey =
   | SiemPageName.hosts
   | SiemPageName.network
   | SiemPageName.detections
-  | SiemPageName.timelines;
+  | SiemPageName.timelines
+  | SiemPageName.case;
 
 export type SiemNavTab = Record<SiemNavTabKey, NavTab>;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/shared_imports.ts b/x-pack/legacy/plugins/siem/public/pages/shared_imports.ts
similarity index 50%
rename from x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/shared_imports.ts
rename to x-pack/legacy/plugins/siem/public/pages/shared_imports.ts
index 494da24be706..a41f121b3692 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/shared_imports.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/shared_imports.ts
@@ -17,7 +17,7 @@ export {
   UseField,
   useForm,
   ValidationFunc,
-} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
-export { Field } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/components';
-export { fieldValidators } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
-export { ERROR_CODE } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers/field_validators/types';
+} from '../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
+export { Field } from '../../../../../../src/plugins/es_ui_shared/static/forms/components';
+export { fieldValidators } from '../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
+export { ERROR_CODE } from '../../../../../../src/plugins/es_ui_shared/static/forms/helpers/field_validators/types';
diff --git a/x-pack/legacy/plugins/siem/public/store/model.ts b/x-pack/legacy/plugins/siem/public/store/model.ts
index 6f04f22866be..9e9e663a59fe 100644
--- a/x-pack/legacy/plugins/siem/public/store/model.ts
+++ b/x-pack/legacy/plugins/siem/public/store/model.ts
@@ -5,9 +5,9 @@
  */
 
 export { appModel } from './app';
-export { inputsModel } from './inputs';
-export { hostsModel } from './hosts';
 export { dragAndDropModel } from './drag_and_drop';
+export { hostsModel } from './hosts';
+export { inputsModel } from './inputs';
 export { networkModel } from './network';
 
 export type KueryFilterQueryKind = 'kuery' | 'lucene';
diff --git a/x-pack/legacy/plugins/siem/public/utils/use_global_loading.ts b/x-pack/legacy/plugins/siem/public/utils/use_global_loading.ts
deleted file mode 100644
index 37abe2f28d31..000000000000
--- a/x-pack/legacy/plugins/siem/public/utils/use_global_loading.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { useState, useEffect } from 'react';
-
-export const useGlobalLoading = () => {
-  const [isInitializing, setIsInitializing] = useState(true);
-  useEffect(() => {
-    if (isInitializing) {
-      setIsInitializing(false);
-    }
-  });
-  return isInitializing;
-};
diff --git a/x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings_temp.ts b/x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings.ts
similarity index 77%
rename from x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings_temp.ts
rename to x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings.ts
index bd73805600a3..80cdb9e979a6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings_temp.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/case/saved_object_mappings.ts
@@ -5,10 +5,7 @@
  */
 /* eslint-disable @typescript-eslint/no-empty-interface */
 /* eslint-disable @typescript-eslint/camelcase */
-import {
-  NewCaseFormatted,
-  NewCommentFormatted,
-} from '../../../../../../../x-pack/plugins/case/server';
+import { CaseAttributes, CommentAttributes } from '../../../../../../../x-pack/plugins/case/server';
 import { ElasticsearchMappingOf } from '../../utils/typed_elasticsearch_mappings';
 
 // Temporary file to write mappings for case
@@ -19,20 +16,10 @@ export const caseSavedObjectType = 'case-workflow';
 export const caseCommentSavedObjectType = 'case-workflow-comment';
 
 export const caseSavedObjectMappings: {
-  [caseSavedObjectType]: ElasticsearchMappingOf<NewCaseFormatted>;
+  [caseSavedObjectType]: ElasticsearchMappingOf<CaseAttributes>;
 } = {
   [caseSavedObjectType]: {
     properties: {
-      assignees: {
-        properties: {
-          username: {
-            type: 'keyword',
-          },
-          full_name: {
-            type: 'keyword',
-          },
-        },
-      },
       created_at: {
         type: 'date',
       },
@@ -58,15 +45,15 @@ export const caseSavedObjectMappings: {
       tags: {
         type: 'keyword',
       },
-      case_type: {
-        type: 'keyword',
+      updated_at: {
+        type: 'date',
       },
     },
   },
 };
 
 export const caseCommentSavedObjectMappings: {
-  [caseCommentSavedObjectType]: ElasticsearchMappingOf<NewCommentFormatted>;
+  [caseCommentSavedObjectType]: ElasticsearchMappingOf<CommentAttributes>;
 } = {
   [caseCommentSavedObjectType]: {
     properties: {
@@ -86,6 +73,9 @@ export const caseCommentSavedObjectMappings: {
           },
         },
       },
+      updated_at: {
+        type: 'date',
+      },
     },
   },
 };
diff --git a/x-pack/legacy/plugins/siem/server/saved_objects.ts b/x-pack/legacy/plugins/siem/server/saved_objects.ts
index 8b9a1891c8a5..58da333c7bc9 100644
--- a/x-pack/legacy/plugins/siem/server/saved_objects.ts
+++ b/x-pack/legacy/plugins/siem/server/saved_objects.ts
@@ -16,6 +16,10 @@ import {
   ruleStatusSavedObjectMappings,
   ruleStatusSavedObjectType,
 } from './lib/detection_engine/rules/saved_object_mappings';
+import {
+  caseSavedObjectMappings,
+  caseCommentSavedObjectMappings,
+} from './lib/case/saved_object_mappings';
 
 export {
   noteSavedObjectType,
@@ -27,5 +31,8 @@ export const savedObjectMappings = {
   ...timelineSavedObjectMappings,
   ...noteSavedObjectMappings,
   ...pinnedEventSavedObjectMappings,
+  // TODO: Remove once while Saved Object Mappings API is programmed for the NP See: https://github.com/elastic/kibana/issues/50309
+  ...caseSavedObjectMappings,
+  ...caseCommentSavedObjectMappings,
   ...ruleStatusSavedObjectMappings,
 };
diff --git a/x-pack/plugins/case/server/index.ts b/x-pack/plugins/case/server/index.ts
index 3963debea979..990aef19b74f 100644
--- a/x-pack/plugins/case/server/index.ts
+++ b/x-pack/plugins/case/server/index.ts
@@ -7,7 +7,7 @@
 import { PluginInitializerContext } from '../../../../src/core/server';
 import { ConfigSchema } from './config';
 import { CasePlugin } from './plugin';
-export { NewCaseFormatted, NewCommentFormatted } from './routes/api/types';
+export { CaseAttributes, CommentAttributes } from './routes/api/types';
 
 export const config = { schema: ConfigSchema };
 export const plugin = (initializerContext: PluginInitializerContext) =>
diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts
index 360c6de67b2a..eb9afb27a749 100644
--- a/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts
+++ b/x-pack/plugins/case/server/routes/api/__fixtures__/create_mock_so_repository.ts
@@ -21,6 +21,8 @@ export const createMockSavedObjectsRepository = (savedObject: any[] = []) => {
         throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing');
       }
       return {
+        page: 1,
+        per_page: 5,
         total: savedObject.length,
         saved_objects: savedObject,
       };
diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_router.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_router.ts
index 84889c3ac49b..ac9eddd6dd2c 100644
--- a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_router.ts
+++ b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_router.ts
@@ -12,7 +12,7 @@ import { RouteDeps } from '../index';
 
 export const createRoute = async (
   api: (deps: RouteDeps) => void,
-  method: 'get' | 'post' | 'delete',
+  method: 'get' | 'post' | 'delete' | 'patch',
   badAuth = false
 ) => {
   const httpService = httpServiceMock.createSetupContract();
diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
index d59f0977e699..c7f6b6fad7d1 100644
--- a/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
+++ b/x-pack/plugins/case/server/routes/api/__fixtures__/mock_saved_objects.ts
@@ -9,17 +9,16 @@ export const mockCases = [
     type: 'case-workflow',
     id: 'mock-id-1',
     attributes: {
-      created_at: 1574718888885,
+      created_at: '2019-11-25T21:54:48.952Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
       description: 'This is a brand new case of a bad meanie defacing data',
       title: 'Super Bad Security Issue',
       state: 'open',
       tags: ['defacement'],
-      case_type: 'security',
-      assignees: [],
+      updated_at: '2019-11-25T21:54:48.952Z',
     },
     references: [],
     updated_at: '2019-11-25T21:54:48.952Z',
@@ -29,17 +28,16 @@ export const mockCases = [
     type: 'case-workflow',
     id: 'mock-id-2',
     attributes: {
-      created_at: 1574721120834,
+      created_at: '2019-11-25T22:32:00.900Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
       description: 'Oh no, a bad meanie destroying data!',
       title: 'Damaging Data Destruction Detected',
       state: 'open',
       tags: ['Data Destruction'],
-      case_type: 'security',
-      assignees: [],
+      updated_at: '2019-11-25T22:32:00.900Z',
     },
     references: [],
     updated_at: '2019-11-25T22:32:00.900Z',
@@ -49,17 +47,16 @@ export const mockCases = [
     type: 'case-workflow',
     id: 'mock-id-3',
     attributes: {
-      created_at: 1574721137881,
+      created_at: '2019-11-25T22:32:17.947Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
       description: 'Oh no, a bad meanie going LOLBins all over the place!',
       title: 'Another bad one',
       state: 'open',
       tags: ['LOLBins'],
-      case_type: 'security',
-      assignees: [],
+      updated_at: '2019-11-25T22:32:17.947Z',
     },
     references: [],
     updated_at: '2019-11-25T22:32:17.947Z',
@@ -82,11 +79,12 @@ export const mockCaseComments = [
     id: 'mock-comment-1',
     attributes: {
       comment: 'Wow, good luck catching that bad meanie!',
-      created_at: 1574718900112,
+      created_at: '2019-11-25T21:55:00.177Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
+      updated_at: '2019-11-25T21:55:00.177Z',
     },
     references: [
       {
@@ -103,11 +101,12 @@ export const mockCaseComments = [
     id: 'mock-comment-2',
     attributes: {
       comment: 'Well I decided to update my comment. So what? Deal with it.',
-      created_at: 1574718902724,
+      created_at: '2019-11-25T21:55:14.633Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
+      updated_at: '2019-11-25T21:55:14.633Z',
     },
     references: [
       {
@@ -124,11 +123,12 @@ export const mockCaseComments = [
     id: 'mock-comment-3',
     attributes: {
       comment: 'Wow, good luck catching that bad meanie!',
-      created_at: 1574721150542,
+      created_at: '2019-11-25T22:32:30.608Z',
       created_by: {
-        full_name: null,
+        full_name: 'elastic',
         username: 'elastic',
       },
+      updated_at: '2019-11-25T22:32:30.608Z',
     },
     references: [
       {
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/get_all_cases.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/get_all_cases.test.ts
index 2f8a229c08f2..96c411a746d4 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/get_all_cases.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/get_all_cases.test.ts
@@ -19,7 +19,7 @@ describe('GET all cases', () => {
   beforeAll(async () => {
     routeHandler = await createRoute(initGetAllCasesApi, 'get');
   });
-  it(`returns the case without case comments when includeComments is false`, async () => {
+  it(`gets all the cases`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases',
       method: 'get',
@@ -29,6 +29,6 @@ describe('GET all cases', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload.saved_objects).toHaveLength(3);
+    expect(response.payload.cases).toHaveLength(3);
   });
 });
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts
index 3c5f8e52d194..60becf1228a0 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts
@@ -12,15 +12,17 @@ import {
   mockCasesErrorTriggerData,
 } from '../__fixtures__';
 import { initGetCaseApi } from '../get_case';
-import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
+import { kibanaResponseFactory, RequestHandler, SavedObject } from 'src/core/server';
 import { httpServerMock } from 'src/core/server/mocks';
+import { flattenCaseSavedObject } from '../utils';
+import { CaseAttributes } from '../types';
 
 describe('GET case', () => {
   let routeHandler: RequestHandler<any, any, any>;
   beforeAll(async () => {
     routeHandler = await createRoute(initGetCaseApi, 'get');
   });
-  it(`returns the case without case comments when includeComments is false`, async () => {
+  it(`returns the case with empty case comments when includeComments is false`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/{id}',
       params: {
@@ -37,8 +39,13 @@ describe('GET case', () => {
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
 
     expect(response.status).toEqual(200);
-    expect(response.payload).toEqual(mockCases.find(s => s.id === 'mock-id-1'));
-    expect(response.payload.comments).toBeUndefined();
+    expect(response.payload).toEqual(
+      flattenCaseSavedObject(
+        (mockCases.find(s => s.id === 'mock-id-1') as unknown) as SavedObject<CaseAttributes>,
+        []
+      )
+    );
+    expect(response.payload.comments).toEqual([]);
   });
   it(`returns an error when thrown from getCase`, async () => {
     const request = httpServerMock.createKibanaRequest({
@@ -76,7 +83,7 @@ describe('GET case', () => {
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
 
     expect(response.status).toEqual(200);
-    expect(response.payload.comments.saved_objects).toHaveLength(3);
+    expect(response.payload.comments).toHaveLength(3);
   });
   it(`returns an error when thrown from getAllCaseComments`, async () => {
     const request = httpServerMock.createKibanaRequest({
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/get_comment.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/get_comment.test.ts
index 9b6a1e435838..3add93acc641 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/get_comment.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/get_comment.test.ts
@@ -11,8 +11,10 @@ import {
   mockCaseComments,
 } from '../__fixtures__';
 import { initGetCommentApi } from '../get_comment';
-import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
+import { kibanaResponseFactory, RequestHandler, SavedObject } from 'src/core/server';
 import { httpServerMock } from 'src/core/server/mocks';
+import { flattenCommentSavedObject } from '../utils';
+import { CommentAttributes } from '../types';
 
 describe('GET comment', () => {
   let routeHandler: RequestHandler<any, any, any>;
@@ -32,7 +34,11 @@ describe('GET comment', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload).toEqual(mockCaseComments.find(s => s.id === 'mock-comment-1'));
+    expect(response.payload).toEqual(
+      flattenCommentSavedObject(
+        mockCaseComments.find(s => s.id === 'mock-comment-1') as SavedObject<CommentAttributes>
+      )
+    );
   });
   it(`returns an error when getComment throws`, async () => {
     const request = httpServerMock.createKibanaRequest({
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/post_case.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/post_case.test.ts
index bb688dde4c58..32c7c5a015af 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/post_case.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/post_case.test.ts
@@ -28,7 +28,6 @@ describe('POST cases', () => {
         title: 'Super Bad Security Issue',
         state: 'open',
         tags: ['defacement'],
-        case_type: 'security',
       },
     });
 
@@ -36,8 +35,8 @@ describe('POST cases', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload.id).toEqual('mock-it');
-    expect(response.payload.attributes.created_by.username).toEqual('awesome');
+    expect(response.payload.case_id).toEqual('mock-it');
+    expect(response.payload.created_by.username).toEqual('awesome');
   });
   it(`Returns an error if postNewCase throws`, async () => {
     const request = httpServerMock.createKibanaRequest({
@@ -48,7 +47,6 @@ describe('POST cases', () => {
         title: 'Super Bad Security Issue',
         state: 'open',
         tags: ['error'],
-        case_type: 'security',
       },
     });
 
@@ -69,7 +67,6 @@ describe('POST cases', () => {
         title: 'Super Bad Security Issue',
         state: 'open',
         tags: ['defacement'],
-        case_type: 'security',
       },
     });
 
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/post_comment.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/post_comment.test.ts
index 0c059b7f15ea..653140af2a7c 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/post_comment.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/post_comment.test.ts
@@ -35,8 +35,7 @@ describe('POST comment', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload.id).toEqual('mock-comment');
-    expect(response.payload.references[0].id).toEqual('mock-id-1');
+    expect(response.payload.comment_id).toEqual('mock-comment');
   });
   it(`Returns an error if the case does not exist`, async () => {
     const request = httpServerMock.createKibanaRequest({
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
index 7ed478d2e7c0..23283d7f8a5b 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
@@ -17,12 +17,12 @@ import { httpServerMock } from 'src/core/server/mocks';
 describe('UPDATE case', () => {
   let routeHandler: RequestHandler<any, any, any>;
   beforeAll(async () => {
-    routeHandler = await createRoute(initUpdateCaseApi, 'post');
+    routeHandler = await createRoute(initUpdateCaseApi, 'patch');
   });
   it(`Updates a case`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/{id}',
-      method: 'post',
+      method: 'patch',
       params: {
         id: 'mock-id-1',
       },
@@ -35,13 +35,13 @@ describe('UPDATE case', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload.id).toEqual('mock-id-1');
-    expect(response.payload.attributes.state).toEqual('closed');
+    expect(typeof response.payload.updated_at).toBe('string');
+    expect(response.payload.state).toEqual('closed');
   });
   it(`Returns an error if updateCase throws`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/{id}',
-      method: 'post',
+      method: 'patch',
       params: {
         id: 'mock-id-does-not-exist',
       },
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/update_comment.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/update_comment.test.ts
index 8aa84b45b7db..5bfd121691ab 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/update_comment.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/update_comment.test.ts
@@ -17,12 +17,12 @@ import { httpServerMock } from 'src/core/server/mocks';
 describe('UPDATE comment', () => {
   let routeHandler: RequestHandler<any, any, any>;
   beforeAll(async () => {
-    routeHandler = await createRoute(initUpdateCommentApi, 'post');
+    routeHandler = await createRoute(initUpdateCommentApi, 'patch');
   });
   it(`Updates a comment`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/comment/{id}',
-      method: 'post',
+      method: 'patch',
       params: {
         id: 'mock-comment-1',
       },
@@ -35,13 +35,12 @@ describe('UPDATE comment', () => {
 
     const response = await routeHandler(theContext, request, kibanaResponseFactory);
     expect(response.status).toEqual(200);
-    expect(response.payload.id).toEqual('mock-comment-1');
-    expect(response.payload.attributes.comment).toEqual('Update my comment');
+    expect(response.payload.comment).toEqual('Update my comment');
   });
   it(`Returns an error if updateComment throws`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/comment/{id}',
-      method: 'post',
+      method: 'patch',
       params: {
         id: 'mock-comment-does-not-exist',
       },
diff --git a/x-pack/plugins/case/server/routes/api/get_all_case_comments.ts b/x-pack/plugins/case/server/routes/api/get_all_case_comments.ts
index cc4956ead1bd..b74227fa8d98 100644
--- a/x-pack/plugins/case/server/routes/api/get_all_case_comments.ts
+++ b/x-pack/plugins/case/server/routes/api/get_all_case_comments.ts
@@ -6,7 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { RouteDeps } from '.';
-import { wrapError } from './utils';
+import { formatAllComments, wrapError } from './utils';
 
 export function initGetAllCaseCommentsApi({ caseService, router }: RouteDeps) {
   router.get(
@@ -24,7 +24,7 @@ export function initGetAllCaseCommentsApi({ caseService, router }: RouteDeps) {
           client: context.core.savedObjects.client,
           caseId: request.params.id,
         });
-        return response.ok({ body: theComments });
+        return response.ok({ body: formatAllComments(theComments) });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/get_all_cases.ts b/x-pack/plugins/case/server/routes/api/get_all_cases.ts
index 749a183dfe98..09075a32ac37 100644
--- a/x-pack/plugins/case/server/routes/api/get_all_cases.ts
+++ b/x-pack/plugins/case/server/routes/api/get_all_cases.ts
@@ -4,21 +4,35 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { schema } from '@kbn/config-schema';
 import { RouteDeps } from '.';
-import { wrapError } from './utils';
+import { formatAllCases, wrapError } from './utils';
+import { SavedObjectsFindOptionsSchema } from './schema';
+import { AllCases } from './types';
 
 export function initGetAllCasesApi({ caseService, router }: RouteDeps) {
   router.get(
     {
       path: '/api/cases',
-      validate: false,
+      validate: {
+        query: schema.nullable(SavedObjectsFindOptionsSchema),
+      },
     },
     async (context, request, response) => {
       try {
-        const cases = await caseService.getAllCases({
-          client: context.core.savedObjects.client,
+        const args = request.query
+          ? {
+              client: context.core.savedObjects.client,
+              options: request.query,
+            }
+          : {
+              client: context.core.savedObjects.client,
+            };
+        const cases = await caseService.getAllCases(args);
+        const body: AllCases = formatAllCases(cases);
+        return response.ok({
+          body,
         });
-        return response.ok({ body: cases });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/get_case.ts b/x-pack/plugins/case/server/routes/api/get_case.ts
index 6aad22a1ebf1..2481197000be 100644
--- a/x-pack/plugins/case/server/routes/api/get_case.ts
+++ b/x-pack/plugins/case/server/routes/api/get_case.ts
@@ -6,7 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { RouteDeps } from '.';
-import { wrapError } from './utils';
+import { flattenCaseSavedObject, wrapError } from './utils';
 
 export function initGetCaseApi({ caseService, router }: RouteDeps) {
   router.get(
@@ -33,14 +33,16 @@ export function initGetCaseApi({ caseService, router }: RouteDeps) {
         return response.customError(wrapError(error));
       }
       if (!includeComments) {
-        return response.ok({ body: theCase });
+        return response.ok({ body: flattenCaseSavedObject(theCase, []) });
       }
       try {
         const theComments = await caseService.getAllCaseComments({
           client: context.core.savedObjects.client,
           caseId: request.params.id,
         });
-        return response.ok({ body: { ...theCase, comments: theComments } });
+        return response.ok({
+          body: { ...flattenCaseSavedObject(theCase, theComments.saved_objects) },
+        });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/get_comment.ts b/x-pack/plugins/case/server/routes/api/get_comment.ts
index 6fd507d89738..d892b4cfebc3 100644
--- a/x-pack/plugins/case/server/routes/api/get_comment.ts
+++ b/x-pack/plugins/case/server/routes/api/get_comment.ts
@@ -6,7 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { RouteDeps } from '.';
-import { wrapError } from './utils';
+import { flattenCommentSavedObject, wrapError } from './utils';
 
 export function initGetCommentApi({ caseService, router }: RouteDeps) {
   router.get(
@@ -24,7 +24,7 @@ export function initGetCommentApi({ caseService, router }: RouteDeps) {
           client: context.core.savedObjects.client,
           commentId: request.params.id,
         });
-        return response.ok({ body: theComment });
+        return response.ok({ body: flattenCommentSavedObject(theComment) });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/get_tags.ts b/x-pack/plugins/case/server/routes/api/get_tags.ts
new file mode 100644
index 000000000000..1d714db4c0c2
--- /dev/null
+++ b/x-pack/plugins/case/server/routes/api/get_tags.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { RouteDeps } from './index';
+import { wrapError } from './utils';
+
+export function initGetTagsApi({ caseService, router }: RouteDeps) {
+  router.get(
+    {
+      path: '/api/cases/tags',
+      validate: {},
+    },
+    async (context, request, response) => {
+      let theCase;
+      try {
+        theCase = await caseService.getTags({
+          client: context.core.savedObjects.client,
+        });
+        return response.ok({ body: theCase });
+      } catch (error) {
+        return response.customError(wrapError(error));
+      }
+    }
+  );
+}
diff --git a/x-pack/plugins/case/server/routes/api/index.ts b/x-pack/plugins/case/server/routes/api/index.ts
index 11ef91d539e8..32dfd6a78d1c 100644
--- a/x-pack/plugins/case/server/routes/api/index.ts
+++ b/x-pack/plugins/case/server/routes/api/index.ts
@@ -5,17 +5,18 @@
  */
 
 import { IRouter } from 'src/core/server';
-import { initDeleteCommentApi } from './delete_comment';
+import { CaseServiceSetup } from '../../services';
 import { initDeleteCaseApi } from './delete_case';
+import { initDeleteCommentApi } from './delete_comment';
 import { initGetAllCaseCommentsApi } from './get_all_case_comments';
 import { initGetAllCasesApi } from './get_all_cases';
 import { initGetCaseApi } from './get_case';
 import { initGetCommentApi } from './get_comment';
+import { initGetTagsApi } from './get_tags';
 import { initPostCaseApi } from './post_case';
 import { initPostCommentApi } from './post_comment';
 import { initUpdateCaseApi } from './update_case';
 import { initUpdateCommentApi } from './update_comment';
-import { CaseServiceSetup } from '../../services';
 
 export interface RouteDeps {
   caseService: CaseServiceSetup;
@@ -23,12 +24,13 @@ export interface RouteDeps {
 }
 
 export function initCaseApi(deps: RouteDeps) {
+  initDeleteCaseApi(deps);
+  initDeleteCommentApi(deps);
   initGetAllCaseCommentsApi(deps);
   initGetAllCasesApi(deps);
   initGetCaseApi(deps);
   initGetCommentApi(deps);
-  initDeleteCaseApi(deps);
-  initDeleteCommentApi(deps);
+  initGetTagsApi(deps);
   initPostCaseApi(deps);
   initPostCommentApi(deps);
   initUpdateCaseApi(deps);
diff --git a/x-pack/plugins/case/server/routes/api/post_case.ts b/x-pack/plugins/case/server/routes/api/post_case.ts
index e5aa0a3548b4..948bf02d5b3c 100644
--- a/x-pack/plugins/case/server/routes/api/post_case.ts
+++ b/x-pack/plugins/case/server/routes/api/post_case.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { formatNewCase, wrapError } from './utils';
+import { flattenCaseSavedObject, formatNewCase, wrapError } from './utils';
 import { NewCaseSchema } from './schema';
 import { RouteDeps } from '.';
 
@@ -31,7 +31,7 @@ export function initPostCaseApi({ caseService, router }: RouteDeps) {
             ...createdBy,
           }),
         });
-        return response.ok({ body: newCase });
+        return response.ok({ body: flattenCaseSavedObject(newCase, []) });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/post_comment.ts b/x-pack/plugins/case/server/routes/api/post_comment.ts
index 3f4592f5bb11..f3f21becddfa 100644
--- a/x-pack/plugins/case/server/routes/api/post_comment.ts
+++ b/x-pack/plugins/case/server/routes/api/post_comment.ts
@@ -5,7 +5,7 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { formatNewComment, wrapError } from './utils';
+import { flattenCommentSavedObject, formatNewComment, wrapError } from './utils';
 import { NewCommentSchema } from './schema';
 import { RouteDeps } from '.';
 import { CASE_SAVED_OBJECT } from '../../constants';
@@ -53,7 +53,7 @@ export function initPostCommentApi({ caseService, router }: RouteDeps) {
           ],
         });
 
-        return response.ok({ body: newComment });
+        return response.ok({ body: flattenCommentSavedObject(newComment) });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/schema.ts b/x-pack/plugins/case/server/routes/api/schema.ts
index 4a4a0c3a11e3..962dc474254f 100644
--- a/x-pack/plugins/case/server/routes/api/schema.ts
+++ b/x-pack/plugins/case/server/routes/api/schema.ts
@@ -7,8 +7,8 @@
 import { schema } from '@kbn/config-schema';
 
 export const UserSchema = schema.object({
-  username: schema.string(),
   full_name: schema.maybe(schema.string()),
+  username: schema.string(),
 });
 
 export const NewCommentSchema = schema.object({
@@ -17,28 +17,38 @@ export const NewCommentSchema = schema.object({
 
 export const CommentSchema = schema.object({
   comment: schema.string(),
-  created_at: schema.number(),
+  created_at: schema.string(),
   created_by: UserSchema,
+  updated_at: schema.string(),
 });
 
 export const UpdatedCommentSchema = schema.object({
   comment: schema.string(),
+  updated_at: schema.string(),
 });
 
 export const NewCaseSchema = schema.object({
-  assignees: schema.arrayOf(UserSchema, { defaultValue: [] }),
   description: schema.string(),
-  title: schema.string(),
   state: schema.oneOf([schema.literal('open'), schema.literal('closed')], { defaultValue: 'open' }),
   tags: schema.arrayOf(schema.string(), { defaultValue: [] }),
-  case_type: schema.string(),
+  title: schema.string(),
 });
 
 export const UpdatedCaseSchema = schema.object({
-  assignees: schema.maybe(schema.arrayOf(UserSchema)),
   description: schema.maybe(schema.string()),
-  title: schema.maybe(schema.string()),
   state: schema.maybe(schema.oneOf([schema.literal('open'), schema.literal('closed')])),
   tags: schema.maybe(schema.arrayOf(schema.string())),
-  case_type: schema.maybe(schema.string()),
+  title: schema.maybe(schema.string()),
+});
+
+export const SavedObjectsFindOptionsSchema = schema.object({
+  defaultSearchOperator: schema.maybe(schema.oneOf([schema.literal('AND'), schema.literal('OR')])),
+  fields: schema.maybe(schema.arrayOf(schema.string())),
+  filter: schema.maybe(schema.string()),
+  page: schema.maybe(schema.number()),
+  perPage: schema.maybe(schema.number()),
+  search: schema.maybe(schema.string()),
+  searchFields: schema.maybe(schema.arrayOf(schema.string())),
+  sortField: schema.maybe(schema.string()),
+  sortOrder: schema.maybe(schema.oneOf([schema.literal('desc'), schema.literal('asc')])),
 });
diff --git a/x-pack/plugins/case/server/routes/api/types.ts b/x-pack/plugins/case/server/routes/api/types.ts
index d943e4e5fd7d..2d1a88bcf142 100644
--- a/x-pack/plugins/case/server/routes/api/types.ts
+++ b/x-pack/plugins/case/server/routes/api/types.ts
@@ -9,28 +9,63 @@ import {
   CommentSchema,
   NewCaseSchema,
   NewCommentSchema,
+  SavedObjectsFindOptionsSchema,
   UpdatedCaseSchema,
   UpdatedCommentSchema,
   UserSchema,
 } from './schema';
+import { SavedObjectAttributes } from '../../../../../../src/core/types';
 
 export type NewCaseType = TypeOf<typeof NewCaseSchema>;
-export type NewCommentFormatted = TypeOf<typeof CommentSchema>;
+export type CommentAttributes = TypeOf<typeof CommentSchema> & SavedObjectAttributes;
 export type NewCommentType = TypeOf<typeof NewCommentSchema>;
+export type SavedObjectsFindOptionsType = TypeOf<typeof SavedObjectsFindOptionsSchema>;
 export type UpdatedCaseTyped = TypeOf<typeof UpdatedCaseSchema>;
 export type UpdatedCommentType = TypeOf<typeof UpdatedCommentSchema>;
 export type UserType = TypeOf<typeof UserSchema>;
 
-export interface NewCaseFormatted extends NewCaseType {
-  created_at: number;
+export interface CaseAttributes extends NewCaseType, SavedObjectAttributes {
+  created_at: string;
   created_by: UserType;
+  updated_at: string;
+}
+
+export type FlattenedCaseSavedObject = CaseAttributes & {
+  case_id: string;
+  comments: FlattenedCommentSavedObject[];
+};
+
+export type FlattenedCasesSavedObject = Array<
+  CaseAttributes & {
+    case_id: string;
+    // TO DO it is partial because we need to add it the commentCount
+    commentCount?: number;
+  }
+>;
+
+export interface AllCases {
+  cases: FlattenedCasesSavedObject;
+  page: number;
+  per_page: number;
+  total: number;
+}
+
+export type FlattenedCommentSavedObject = CommentAttributes & {
+  comment_id: string;
+  // TO DO We might want to add the case_id where this comment is related too
+};
+
+export interface AllComments {
+  comments: FlattenedCommentSavedObject[];
+  page: number;
+  per_page: number;
+  total: number;
 }
 
 export interface UpdatedCaseType {
-  assignees?: UpdatedCaseTyped['assignees'];
   description?: UpdatedCaseTyped['description'];
-  title?: UpdatedCaseTyped['title'];
   state?: UpdatedCaseTyped['state'];
   tags?: UpdatedCaseTyped['tags'];
-  case_type?: UpdatedCaseTyped['case_type'];
+  title?: UpdatedCaseTyped['title'];
+  updated_at: string;
 }
diff --git a/x-pack/plugins/case/server/routes/api/update_case.ts b/x-pack/plugins/case/server/routes/api/update_case.ts
index 52c8cab0022d..2a814c7259e4 100644
--- a/x-pack/plugins/case/server/routes/api/update_case.ts
+++ b/x-pack/plugins/case/server/routes/api/update_case.ts
@@ -10,7 +10,7 @@ import { RouteDeps } from '.';
 import { UpdatedCaseSchema } from './schema';
 
 export function initUpdateCaseApi({ caseService, router }: RouteDeps) {
-  router.post(
+  router.patch(
     {
       path: '/api/cases/{id}',
       validate: {
@@ -25,9 +25,12 @@ export function initUpdateCaseApi({ caseService, router }: RouteDeps) {
         const updatedCase = await caseService.updateCase({
           client: context.core.savedObjects.client,
           caseId: request.params.id,
-          updatedAttributes: request.body,
+          updatedAttributes: {
+            ...request.body,
+            updated_at: new Date().toISOString(),
+          },
         });
-        return response.ok({ body: updatedCase });
+        return response.ok({ body: updatedCase.attributes });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/update_comment.ts b/x-pack/plugins/case/server/routes/api/update_comment.ts
index e1ee6029e8e4..815f44a14e2e 100644
--- a/x-pack/plugins/case/server/routes/api/update_comment.ts
+++ b/x-pack/plugins/case/server/routes/api/update_comment.ts
@@ -10,7 +10,7 @@ import { NewCommentSchema } from './schema';
 import { RouteDeps } from '.';
 
 export function initUpdateCommentApi({ caseService, router }: RouteDeps) {
-  router.post(
+  router.patch(
     {
       path: '/api/cases/comment/{id}',
       validate: {
@@ -25,9 +25,12 @@ export function initUpdateCommentApi({ caseService, router }: RouteDeps) {
         const updatedComment = await caseService.updateComment({
           client: context.core.savedObjects.client,
           commentId: request.params.id,
-          updatedAttributes: request.body,
+          updatedAttributes: {
+            ...request.body,
+            updated_at: new Date().toISOString(),
+          },
         });
-        return response.ok({ body: updatedComment });
+        return response.ok({ body: updatedComment.attributes });
       } catch (error) {
         return response.customError(wrapError(error));
       }
diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts
index c6e33dbb8433..51944b04836a 100644
--- a/x-pack/plugins/case/server/routes/api/utils.ts
+++ b/x-pack/plugins/case/server/routes/api/utils.ts
@@ -5,21 +5,31 @@
  */
 
 import { boomify, isBoom } from 'boom';
-import { CustomHttpResponseOptions, ResponseError } from 'kibana/server';
 import {
+  CustomHttpResponseOptions,
+  ResponseError,
+  SavedObject,
+  SavedObjectsFindResponse,
+} from 'kibana/server';
+import {
+  AllComments,
+  CaseAttributes,
+  CommentAttributes,
+  FlattenedCaseSavedObject,
+  FlattenedCommentSavedObject,
+  AllCases,
   NewCaseType,
-  NewCaseFormatted,
   NewCommentType,
-  NewCommentFormatted,
   UserType,
 } from './types';
 
 export const formatNewCase = (
   newCase: NewCaseType,
   { full_name, username }: { full_name?: string; username: string }
-): NewCaseFormatted => ({
-  created_at: new Date().valueOf(),
+): CaseAttributes => ({
+  created_at: new Date().toISOString(),
   created_by: { full_name, username },
+  updated_at: new Date().toISOString(),
   ...newCase,
 });
 
@@ -32,10 +42,11 @@ export const formatNewComment = ({
   newComment,
   full_name,
   username,
-}: NewCommentArgs): NewCommentFormatted => ({
+}: NewCommentArgs): CommentAttributes => ({
   ...newComment,
-  created_at: new Date().valueOf(),
+  created_at: new Date().toISOString(),
   created_by: { full_name, username },
+  updated_at: new Date().toISOString(),
 });
 
 export function wrapError(error: any): CustomHttpResponseOptions<ResponseError> {
@@ -46,3 +57,55 @@ export function wrapError(error: any): CustomHttpResponseOptions<ResponseError>
     statusCode: boom.output.statusCode,
   };
 }
+
+export const formatAllCases = (cases: SavedObjectsFindResponse<CaseAttributes>): AllCases => ({
+  page: cases.page,
+  per_page: cases.per_page,
+  total: cases.total,
+  cases: flattenCaseSavedObjects(cases.saved_objects),
+});
+
+export const flattenCaseSavedObjects = (
+  savedObjects: SavedObjectsFindResponse<CaseAttributes>['saved_objects']
+): FlattenedCaseSavedObject[] =>
+  savedObjects.reduce(
+    (acc: FlattenedCaseSavedObject[], savedObject: SavedObject<CaseAttributes>) => {
+      return [...acc, flattenCaseSavedObject(savedObject, [])];
+    },
+    []
+  );
+
+export const flattenCaseSavedObject = (
+  savedObject: SavedObject<CaseAttributes>,
+  comments: Array<SavedObject<CommentAttributes>>
+): FlattenedCaseSavedObject => ({
+  case_id: savedObject.id,
+  comments: flattenCommentSavedObjects(comments),
+  ...savedObject.attributes,
+});
+
+export const formatAllComments = (
+  comments: SavedObjectsFindResponse<CommentAttributes>
+): AllComments => ({
+  page: comments.page,
+  per_page: comments.per_page,
+  total: comments.total,
+  comments: flattenCommentSavedObjects(comments.saved_objects),
+});
+
+export const flattenCommentSavedObjects = (
+  savedObjects: SavedObjectsFindResponse<CommentAttributes>['saved_objects']
+): FlattenedCommentSavedObject[] =>
+  savedObjects.reduce(
+    (acc: FlattenedCommentSavedObject[], savedObject: SavedObject<CommentAttributes>) => {
+      return [...acc, flattenCommentSavedObject(savedObject)];
+    },
+    []
+  );
+
+export const flattenCommentSavedObject = (
+  savedObject: SavedObject<CommentAttributes>
+): FlattenedCommentSavedObject => ({
+  comment_id: savedObject.id,
+  ...savedObject.attributes,
+});
diff --git a/x-pack/plugins/case/server/services/index.ts b/x-pack/plugins/case/server/services/index.ts
index 531d5fa5b87e..d6d4bd606676 100644
--- a/x-pack/plugins/case/server/services/index.ts
+++ b/x-pack/plugins/case/server/services/index.ts
@@ -16,12 +16,14 @@ import {
 } from 'kibana/server';
 import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT } from '../constants';
 import {
-  NewCaseFormatted,
-  NewCommentFormatted,
+  CaseAttributes,
+  CommentAttributes,
+  SavedObjectsFindOptionsType,
   UpdatedCaseType,
   UpdatedCommentType,
 } from '../routes/api/types';
 import { AuthenticatedUser, SecurityPluginSetup } from '../../../security/server';
+import { readTags } from './tags/read_tags';
 
 interface ClientArgs {
   client: SavedObjectsClientContract;
@@ -30,15 +32,19 @@ interface ClientArgs {
 interface GetCaseArgs extends ClientArgs {
   caseId: string;
 }
+
+interface GetCasesArgs extends ClientArgs {
+  options?: SavedObjectsFindOptionsType;
+}
 interface GetCommentArgs extends ClientArgs {
   commentId: string;
 }
 interface PostCaseArgs extends ClientArgs {
-  attributes: NewCaseFormatted;
+  attributes: CaseAttributes;
 }
 
 interface PostCommentArgs extends ClientArgs {
-  attributes: NewCommentFormatted;
+  attributes: CommentAttributes;
   references: SavedObjectReference[];
 }
 interface UpdateCaseArgs extends ClientArgs {
@@ -61,15 +67,16 @@ interface CaseServiceDeps {
 export interface CaseServiceSetup {
   deleteCase(args: GetCaseArgs): Promise<{}>;
   deleteComment(args: GetCommentArgs): Promise<{}>;
-  getAllCases(args: ClientArgs): Promise<SavedObjectsFindResponse>;
-  getAllCaseComments(args: GetCaseArgs): Promise<SavedObjectsFindResponse>;
-  getCase(args: GetCaseArgs): Promise<SavedObject>;
-  getComment(args: GetCommentArgs): Promise<SavedObject>;
+  getAllCases(args: GetCasesArgs): Promise<SavedObjectsFindResponse<CaseAttributes>>;
+  getAllCaseComments(args: GetCaseArgs): Promise<SavedObjectsFindResponse<CommentAttributes>>;
+  getCase(args: GetCaseArgs): Promise<SavedObject<CaseAttributes>>;
+  getComment(args: GetCommentArgs): Promise<SavedObject<CommentAttributes>>;
+  getTags(args: ClientArgs): Promise<string[]>;
   getUser(args: GetUserArgs): Promise<AuthenticatedUser>;
-  postNewCase(args: PostCaseArgs): Promise<SavedObject>;
-  postNewComment(args: PostCommentArgs): Promise<SavedObject>;
-  updateCase(args: UpdateCaseArgs): Promise<SavedObjectsUpdateResponse>;
-  updateComment(args: UpdateCommentArgs): Promise<SavedObjectsUpdateResponse>;
+  postNewCase(args: PostCaseArgs): Promise<SavedObject<CaseAttributes>>;
+  postNewComment(args: PostCommentArgs): Promise<SavedObject<CommentAttributes>>;
+  updateCase(args: UpdateCaseArgs): Promise<SavedObjectsUpdateResponse<CaseAttributes>>;
+  updateComment(args: UpdateCommentArgs): Promise<SavedObjectsUpdateResponse<CommentAttributes>>;
 }
 
 export class CaseService {
@@ -111,10 +118,10 @@ export class CaseService {
         throw error;
       }
     },
-    getAllCases: async ({ client }: ClientArgs) => {
+    getAllCases: async ({ client, options }: GetCasesArgs) => {
       try {
         this.log.debug(`Attempting to GET all cases`);
-        return await client.find({ type: CASE_SAVED_OBJECT });
+        return await client.find({ ...options, type: CASE_SAVED_OBJECT });
       } catch (error) {
         this.log.debug(`Error on GET cases: ${error}`);
         throw error;
@@ -132,6 +139,15 @@ export class CaseService {
         throw error;
       }
     },
+    getTags: async ({ client }: ClientArgs) => {
+      try {
+        this.log.debug(`Attempting to GET all cases`);
+        return await readTags({ client });
+      } catch (error) {
+        this.log.debug(`Error on GET cases: ${error}`);
+        throw error;
+      }
+    },
     getUser: async ({ request, response }: GetUserArgs) => {
       let user;
       try {
diff --git a/x-pack/plugins/case/server/services/tags/read_tags.ts b/x-pack/plugins/case/server/services/tags/read_tags.ts
new file mode 100644
index 000000000000..58ab99b164cf
--- /dev/null
+++ b/x-pack/plugins/case/server/services/tags/read_tags.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { SavedObject, SavedObjectsClientContract } from 'kibana/server';
+import { CASE_SAVED_OBJECT } from '../../constants';
+import { CaseAttributes } from '../..';
+
+const DEFAULT_PER_PAGE: number = 1000;
+
+export const convertToTags = (tagObjects: Array<SavedObject<CaseAttributes>>): string[] =>
+  tagObjects.reduce<string[]>((accum, tagObj) => {
+    if (tagObj && tagObj.attributes && tagObj.attributes.tags) {
+      return [...accum, ...tagObj.attributes.tags];
+    } else {
+      return accum;
+    }
+  }, []);
+
+export const convertTagsToSet = (tagObjects: Array<SavedObject<CaseAttributes>>): Set<string> => {
+  return new Set(convertToTags(tagObjects));
+};
+
+// Note: This is doing an in-memory aggregation of the tags by calling each of the alerting
+// records in batches of this const setting and uses the fields to try to get the least
+// amount of data per record back. If saved objects at some point supports aggregations
+// then this should be replaced with a an aggregation call.
+// Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html
+export const readTags = async ({
+  client,
+  perPage = DEFAULT_PER_PAGE,
+}: {
+  client: SavedObjectsClientContract;
+  perPage?: number;
+}): Promise<string[]> => {
+  const tags = await readRawTags({ client, perPage });
+  return tags;
+};
+
+export const readRawTags = async ({
+  client,
+  perPage = DEFAULT_PER_PAGE,
+}: {
+  client: SavedObjectsClientContract;
+  perPage?: number;
+}): Promise<string[]> => {
+  const firstTags = await client.find({
+    type: CASE_SAVED_OBJECT,
+    fields: ['tags'],
+    page: 1,
+    perPage,
+  });
+  const tags = await client.find({
+    type: CASE_SAVED_OBJECT,
+    fields: ['tags'],
+    page: 1,
+    perPage: firstTags.total,
+  });
+
+  return Array.from(convertTagsToSet(tags.saved_objects));
+};