diff --git a/x-pack/plugins/stack_alerts/kibana.json b/x-pack/plugins/stack_alerts/kibana.json
index b7405c38d1611..884d33ef669e5 100644
--- a/x-pack/plugins/stack_alerts/kibana.json
+++ b/x-pack/plugins/stack_alerts/kibana.json
@@ -3,7 +3,7 @@
   "server": true,
   "version": "8.0.0",
   "kibanaVersion": "kibana",
-  "requiredPlugins": ["alerts", "features", "triggersActionsUi", "kibanaReact"],
+  "requiredPlugins": ["alerts", "features", "triggersActionsUi", "kibanaReact", "savedObjects", "data"],
   "configPath": ["xpack", "stack_alerts"],
   "ui": true
 }
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap
new file mode 100644
index 0000000000000..dae168417b0bc
--- /dev/null
+++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap
@@ -0,0 +1,210 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render BoundaryIndexExpression 1`] = `
+<ExpressionWithPopover
+  defaultValue="Select an index pattern and geo shape field"
+  expressionDescription="index"
+  popoverContent={
+    <React.Fragment>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoIndexPatternSelect"
+        labelType="label"
+      >
+        <GeoIndexPatternSelect
+          IndexPatternSelectComponent={null}
+          http={null}
+          includedGeoTypes={
+            Array [
+              "geo_shape",
+            ]
+          }
+          onChange={[Function]}
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoField"
+        label="Geospatial field"
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select geo field"
+          value=""
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="boundaryNameFieldSelect"
+        label="Human-readable boundary name (optional)"
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select boundary name"
+          value="testNameField"
+        />
+      </EuiFormRow>
+    </React.Fragment>
+  }
+/>
+`;
+
+exports[`should render EntityIndexExpression 1`] = `
+<ExpressionWithPopover
+  defaultValue="Select an index pattern and geo shape/point field"
+  expressionDescription="index"
+  isInvalid={false}
+  popoverContent={
+    <React.Fragment>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoIndexPatternSelect"
+        labelType="label"
+      >
+        <GeoIndexPatternSelect
+          IndexPatternSelectComponent={null}
+          http={null}
+          includedGeoTypes={
+            Array [
+              "geo_point",
+            ]
+          }
+          onChange={[Function]}
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="thresholdTimeField"
+        label={
+          <FormattedMessage
+            defaultMessage="Time field"
+            id="xpack.stackAlerts.geoThreshold.timeFieldLabel"
+            values={Object {}}
+          />
+        }
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select time field"
+          value="testDateField"
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoField"
+        label="Geospatial field"
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select geo field"
+          value="testGeoField"
+        />
+      </EuiFormRow>
+    </React.Fragment>
+  }
+/>
+`;
+
+exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = `
+<ExpressionWithPopover
+  defaultValue="Select an index pattern and geo shape/point field"
+  expressionDescription="index"
+  isInvalid={true}
+  popoverContent={
+    <React.Fragment>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoIndexPatternSelect"
+        labelType="label"
+      >
+        <GeoIndexPatternSelect
+          IndexPatternSelectComponent={null}
+          http={null}
+          includedGeoTypes={
+            Array [
+              "geo_point",
+            ]
+          }
+          onChange={[Function]}
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="thresholdTimeField"
+        label={
+          <FormattedMessage
+            defaultMessage="Time field"
+            id="xpack.stackAlerts.geoThreshold.timeFieldLabel"
+            values={Object {}}
+          />
+        }
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select time field"
+          value="testDateField"
+        />
+      </EuiFormRow>
+      <EuiFormRow
+        describedByIds={Array []}
+        display="row"
+        fullWidth={true}
+        hasChildLabel={true}
+        hasEmptyLabelSpace={false}
+        id="geoField"
+        label="Geospatial field"
+        labelType="label"
+      >
+        <SingleFieldSelect
+          fields={Array []}
+          onChange={[Function]}
+          placeholder="Select geo field"
+          value="testGeoField"
+        />
+      </EuiFormRow>
+    </React.Fragment>
+  }
+/>
+`;
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx
new file mode 100644
index 0000000000000..d115dbeb76e37
--- /dev/null
+++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx
@@ -0,0 +1,94 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import { EntityIndexExpression } from './expressions/entity_index_expression';
+import { BoundaryIndexExpression } from './expressions/boundary_index_expression';
+import { ApplicationStart, DocLinksStart, HttpSetup, ToastsStart } from 'kibana/public';
+import {
+  ActionTypeRegistryContract,
+  AlertTypeRegistryContract,
+  IErrorObject,
+} from '../../../../../triggers_actions_ui/public';
+import { IIndexPattern } from '../../../../../../../src/plugins/data/common';
+
+const alertsContext = {
+  http: (null as unknown) as HttpSetup,
+  alertTypeRegistry: (null as unknown) as AlertTypeRegistryContract,
+  actionTypeRegistry: (null as unknown) as ActionTypeRegistryContract,
+  toastNotifications: (null as unknown) as ToastsStart,
+  docLinks: (null as unknown) as DocLinksStart,
+  capabilities: (null as unknown) as ApplicationStart['capabilities'],
+};
+
+const alertParams = {
+  index: '',
+  indexId: '',
+  geoField: '',
+  entity: '',
+  dateField: '',
+  trackingEvent: '',
+  boundaryType: '',
+  boundaryIndexTitle: '',
+  boundaryIndexId: '',
+  boundaryGeoField: '',
+};
+
+test('should render EntityIndexExpression', async () => {
+  const component = shallow(
+    <EntityIndexExpression
+      dateField={'testDateField'}
+      geoField={'testGeoField'}
+      alertsContext={alertsContext}
+      errors={{} as IErrorObject}
+      setAlertParamsDate={() => {}}
+      setAlertParamsGeoField={() => {}}
+      setAlertProperty={() => {}}
+      setIndexPattern={() => {}}
+      indexPattern={('' as unknown) as IIndexPattern}
+      isInvalid={false}
+    />
+  );
+
+  expect(component).toMatchSnapshot();
+});
+
+test('should render EntityIndexExpression w/ invalid flag if invalid', async () => {
+  const component = shallow(
+    <EntityIndexExpression
+      dateField={'testDateField'}
+      geoField={'testGeoField'}
+      alertsContext={alertsContext}
+      errors={{} as IErrorObject}
+      setAlertParamsDate={() => {}}
+      setAlertParamsGeoField={() => {}}
+      setAlertProperty={() => {}}
+      setIndexPattern={() => {}}
+      indexPattern={('' as unknown) as IIndexPattern}
+      isInvalid={true}
+    />
+  );
+
+  expect(component).toMatchSnapshot();
+});
+
+test('should render BoundaryIndexExpression', async () => {
+  const component = shallow(
+    <BoundaryIndexExpression
+      alertParams={alertParams}
+      alertsContext={alertsContext}
+      errors={{} as IErrorObject}
+      boundaryIndexPattern={('' as unknown) as IIndexPattern}
+      setBoundaryIndexPattern={() => {}}
+      setBoundaryGeoField={() => {}}
+      setBoundaryNameField={() => {}}
+      boundaryNameField={'testNameField'}
+    />
+  );
+
+  expect(component).toMatchSnapshot();
+});
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx
index f138c08c0f993..623223d66ea00 100644
--- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx
+++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx
@@ -30,6 +30,12 @@ import { EntityIndexExpression } from './expressions/entity_index_expression';
 import { EntityByExpression } from './expressions/entity_by_expression';
 import { BoundaryIndexExpression } from './expressions/boundary_index_expression';
 import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns';
+import {
+  esQuery,
+  esKuery,
+  Query,
+  QueryStringInput,
+} from '../../../../../../../src/plugins/data/public';
 
 const DEFAULT_VALUES = {
   TRACKING_EVENT: '',
@@ -67,6 +73,18 @@ const labelForDelayOffset = (
   </>
 );
 
+function validateQuery(query: Query) {
+  try {
+    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+    query.language === 'kuery'
+      ? esKuery.fromKueryExpression(query.query)
+      : esQuery.luceneStringToDsl(query.query);
+  } catch (err) {
+    return false;
+  }
+  return true;
+}
+
 export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeParamsExpressionProps<
   GeoThresholdAlertParams,
   AlertsContextValue
@@ -74,6 +92,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
   const {
     index,
     indexId,
+    indexQuery,
     geoField,
     entity,
     dateField,
@@ -81,6 +100,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
     boundaryType,
     boundaryIndexTitle,
     boundaryIndexId,
+    boundaryIndexQuery,
     boundaryGeoField,
     boundaryNameField,
     delayOffsetWithUnits,
@@ -102,6 +122,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
       }
     }
   };
+  const [indexQueryInput, setIndexQueryInput] = useState<Query>(
+    indexQuery || {
+      query: '',
+      language: 'kuery',
+    }
+  );
   const [boundaryIndexPattern, _setBoundaryIndexPattern] = useState<IIndexPattern>({
     id: '',
     fields: [],
@@ -118,6 +144,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
       }
     }
   };
+  const [boundaryIndexQueryInput, setBoundaryIndexQueryInput] = useState<Query>(
+    boundaryIndexQuery || {
+      query: '',
+      language: 'kuery',
+    }
+  );
   const [delayOffset, _setDelayOffset] = useState<number>(0);
   function setDelayOffset(_delayOffset: number) {
     setAlertParams('delayOffsetWithUnits', `${_delayOffset}${delayOffsetUnit}`);
@@ -248,6 +280,23 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
         indexFields={indexPattern.fields}
         isInvalid={indexId && dateField && geoField ? !entity : false}
       />
+      <EuiSpacer size="s" />
+      <EuiFlexItem>
+        <QueryStringInput
+          disableAutoFocus
+          bubbleSubmitEvent
+          indexPatterns={indexPattern ? [indexPattern] : []}
+          query={indexQueryInput}
+          onChange={(query) => {
+            if (query.language) {
+              if (validateQuery(query)) {
+                setAlertParams('indexQuery', query);
+              }
+              setIndexQueryInput(query);
+            }
+          }}
+        />
+      </EuiFlexItem>
 
       <EuiSpacer size="l" />
       <EuiTitle size="xs">
@@ -313,6 +362,24 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
         }
         boundaryNameField={boundaryNameField}
       />
+      <EuiSpacer size="s" />
+      <EuiFlexItem>
+        <QueryStringInput
+          disableAutoFocus
+          bubbleSubmitEvent
+          indexPatterns={boundaryIndexPattern ? [boundaryIndexPattern] : []}
+          query={boundaryIndexQueryInput}
+          onChange={(query) => {
+            if (query.language) {
+              if (validateQuery(query)) {
+                setAlertParams('boundaryIndexQuery', query);
+              }
+              setBoundaryIndexQueryInput(query);
+            }
+          }}
+        />
+      </EuiFlexItem>
+      <EuiSpacer size="l" />
     </Fragment>
   );
 };
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts
index 0358fcd66a467..86faa4ed2fb4a 100644
--- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts
+++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts
@@ -4,6 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { Query } from '../../../../../../src/plugins/data/common';
+
 export enum TrackingEvent {
   entered = 'entered',
   exited = 'exited',
@@ -22,6 +24,8 @@ export interface GeoThresholdAlertParams {
   boundaryGeoField: string;
   boundaryNameField?: string;
   delayOffsetWithUnits?: string;
+  indexQuery?: Query;
+  boundaryIndexQuery?: Query;
 }
 
 // Will eventually include 'geo_shape'
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts
index 9fc46fe2f2586..0c40f5b5f3866 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts
@@ -15,6 +15,7 @@ import {
   ActionVariable,
   AlertTypeState,
 } from '../../../../alerts/server';
+import { Query } from '../../../../../../src/plugins/data/common/query';
 
 export const GEO_THRESHOLD_ID = '.geo-threshold';
 export type TrackingEvent = 'entered' | 'exited';
@@ -155,6 +156,8 @@ export const ParamsSchema = schema.object({
   boundaryGeoField: schema.string({ minLength: 1 }),
   boundaryNameField: schema.maybe(schema.string({ minLength: 1 })),
   delayOffsetWithUnits: schema.maybe(schema.string({ minLength: 1 })),
+  indexQuery: schema.maybe(schema.any({})),
+  boundaryIndexQuery: schema.maybe(schema.any({})),
 });
 
 export interface GeoThresholdParams {
@@ -170,6 +173,8 @@ export interface GeoThresholdParams {
   boundaryGeoField: string;
   boundaryNameField?: string;
   delayOffsetWithUnits?: string;
+  indexQuery?: Query;
+  boundaryIndexQuery?: Query;
 }
 
 export function getAlertType(
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts
index 97be51b2a6256..02ac19e7b6f1e 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts
@@ -7,6 +7,13 @@
 import { ILegacyScopedClusterClient } from 'kibana/server';
 import { SearchResponse } from 'elasticsearch';
 import { Logger } from 'src/core/server';
+import {
+  Query,
+  IIndexPattern,
+  fromKueryExpression,
+  toElasticsearchQuery,
+  luceneStringToDsl,
+} from '../../../../../../src/plugins/data/common';
 
 export const OTHER_CATEGORY = 'other';
 // Consider dynamically obtaining from config?
@@ -14,6 +21,19 @@ const MAX_TOP_LEVEL_QUERY_SIZE = 0;
 const MAX_SHAPES_QUERY_SIZE = 10000;
 const MAX_BUCKETS_LIMIT = 65535;
 
+export const getEsFormattedQuery = (query: Query, indexPattern?: IIndexPattern) => {
+  let esFormattedQuery;
+
+  const queryLanguage = query.language;
+  if (queryLanguage === 'kuery') {
+    const ast = fromKueryExpression(query.query);
+    esFormattedQuery = toElasticsearchQuery(ast, indexPattern);
+  } else {
+    esFormattedQuery = luceneStringToDsl(query.query);
+  }
+  return esFormattedQuery;
+};
+
 export async function getShapesFilters(
   boundaryIndexTitle: string,
   boundaryGeoField: string,
@@ -21,7 +41,8 @@ export async function getShapesFilters(
   callCluster: ILegacyScopedClusterClient['callAsCurrentUser'],
   log: Logger,
   alertId: string,
-  boundaryNameField?: string
+  boundaryNameField?: string,
+  boundaryIndexQuery?: Query
 ) {
   const filters: Record<string, unknown> = {};
   const shapesIdsNamesMap: Record<string, unknown> = {};
@@ -30,8 +51,10 @@ export async function getShapesFilters(
     index: boundaryIndexTitle,
     body: {
       size: MAX_SHAPES_QUERY_SIZE,
+      ...(boundaryIndexQuery ? { query: getEsFormattedQuery(boundaryIndexQuery) } : {}),
     },
   });
+
   boundaryData.hits.hits.forEach(({ _index, _id }) => {
     filters[_id] = {
       geo_shape: {
@@ -66,6 +89,7 @@ export async function executeEsQueryFactory(
     boundaryGeoField,
     geoField,
     boundaryIndexTitle,
+    indexQuery,
   }: {
     entity: string;
     index: string;
@@ -74,6 +98,7 @@ export async function executeEsQueryFactory(
     geoField: string;
     boundaryIndexTitle: string;
     boundaryNameField?: string;
+    indexQuery?: Query;
   },
   { callCluster }: { callCluster: ILegacyScopedClusterClient['callAsCurrentUser'] },
   log: Logger,
@@ -83,6 +108,19 @@ export async function executeEsQueryFactory(
     gteDateTime: Date | null,
     ltDateTime: Date | null
   ): Promise<SearchResponse<unknown> | undefined> => {
+    let esFormattedQuery;
+    if (indexQuery) {
+      const gteEpochDateTime = gteDateTime ? new Date(gteDateTime).getTime() : null;
+      const ltEpochDateTime = ltDateTime ? new Date(ltDateTime).getTime() : null;
+      const dateRangeUpdatedQuery =
+        indexQuery.language === 'kuery'
+          ? `(${dateField} >= "${gteEpochDateTime}" and ${dateField} < "${ltEpochDateTime}") and (${indexQuery.query})`
+          : `(${dateField}:[${gteDateTime} TO ${ltDateTime}]) AND (${indexQuery.query})`;
+      esFormattedQuery = getEsFormattedQuery({
+        query: dateRangeUpdatedQuery,
+        language: indexQuery.language,
+      });
+    }
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     const esQuery: Record<string, any> = {
       index,
@@ -120,27 +158,29 @@ export async function executeEsQueryFactory(
             },
           },
         },
-        query: {
-          bool: {
-            must: [],
-            filter: [
-              {
-                match_all: {},
-              },
-              {
-                range: {
-                  [dateField]: {
-                    ...(gteDateTime ? { gte: gteDateTime } : {}),
-                    lt: ltDateTime, // 'less than' to prevent overlap between intervals
-                    format: 'strict_date_optional_time',
+        query: esFormattedQuery
+          ? esFormattedQuery
+          : {
+              bool: {
+                must: [],
+                filter: [
+                  {
+                    match_all: {},
                   },
-                },
+                  {
+                    range: {
+                      [dateField]: {
+                        ...(gteDateTime ? { gte: gteDateTime } : {}),
+                        lt: ltDateTime, // 'less than' to prevent overlap between intervals
+                        format: 'strict_date_optional_time',
+                      },
+                    },
+                  },
+                ],
+                should: [],
+                must_not: [],
               },
-            ],
-            should: [],
-            must_not: [],
-          },
-        },
+            },
         stored_fields: ['*'],
         docvalue_fields: [
           {
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts
index e223cdb7ea545..8247cc787d365 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts
@@ -194,7 +194,8 @@ export const getGeoThresholdExecutor = (log: Logger) =>
           services.callCluster,
           log,
           alertId,
-          params.boundaryNameField
+          params.boundaryNameField,
+          params.boundaryIndexQuery
         );
 
     const executeEsQuery = await executeEsQueryFactory(params, services, log, shapesFilters);
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/es_query_builder.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/es_query_builder.test.ts
new file mode 100644
index 0000000000000..d577a88e8e2f8
--- /dev/null
+++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/es_query_builder.test.ts
@@ -0,0 +1,67 @@
+/*
+ * 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 { getEsFormattedQuery } from '../es_query_builder';
+
+describe('esFormattedQuery', () => {
+  it('lucene queries are converted correctly', async () => {
+    const testLuceneQuery1 = {
+      query: `"airport": "Denver"`,
+      language: 'lucene',
+    };
+    const esFormattedQuery1 = getEsFormattedQuery(testLuceneQuery1);
+    expect(esFormattedQuery1).toStrictEqual({ query_string: { query: '"airport": "Denver"' } });
+    const testLuceneQuery2 = {
+      query: `title:"Fun with turnips" AND text:Cabbage, cabbage and more cabbage!`,
+      language: 'lucene',
+    };
+    const esFormattedQuery2 = getEsFormattedQuery(testLuceneQuery2);
+    expect(esFormattedQuery2).toStrictEqual({
+      query_string: {
+        query: `title:"Fun with turnips" AND text:Cabbage, cabbage and more cabbage!`,
+      },
+    });
+  });
+
+  it('kuery queries are converted correctly', async () => {
+    const testKueryQuery1 = {
+      query: `"airport": "Denver"`,
+      language: 'kuery',
+    };
+    const esFormattedQuery1 = getEsFormattedQuery(testKueryQuery1);
+    expect(esFormattedQuery1).toStrictEqual({
+      bool: { minimum_should_match: 1, should: [{ match_phrase: { airport: 'Denver' } }] },
+    });
+    const testKueryQuery2 = {
+      query: `"airport": "Denver" and ("animal": "goat" or "animal": "narwhal")`,
+      language: 'kuery',
+    };
+    const esFormattedQuery2 = getEsFormattedQuery(testKueryQuery2);
+    expect(esFormattedQuery2).toStrictEqual({
+      bool: {
+        filter: [
+          { bool: { should: [{ match_phrase: { airport: 'Denver' } }], minimum_should_match: 1 } },
+          {
+            bool: {
+              should: [
+                {
+                  bool: { should: [{ match_phrase: { animal: 'goat' } }], minimum_should_match: 1 },
+                },
+                {
+                  bool: {
+                    should: [{ match_phrase: { animal: 'narwhal' } }],
+                    minimum_should_match: 1,
+                  },
+                },
+              ],
+              minimum_should_match: 1,
+            },
+          },
+        ],
+      },
+    });
+  });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/kibana.json b/x-pack/plugins/triggers_actions_ui/kibana.json
index 9d79ab9232bf3..ab2d6c6a3c400 100644
--- a/x-pack/plugins/triggers_actions_ui/kibana.json
+++ b/x-pack/plugins/triggers_actions_ui/kibana.json
@@ -4,8 +4,8 @@
   "server": true,
   "ui": true,
   "optionalPlugins": ["alerts", "features", "home"],
-  "requiredPlugins": ["management", "charts", "data"],
+  "requiredPlugins": ["management", "charts", "data", "kibanaReact", "savedObjects"],
   "configPath": ["xpack", "trigger_actions_ui"],
   "extraPublicDirs": ["public/common", "public/common/constants"],
-  "requiredBundles": ["home", "alerts", "esUiShared"]
+  "requiredBundles": ["home", "alerts", "esUiShared", "kibanaReact", "kibanaUtils"]
 }
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
index 5c1e0aa0100e8..fa38c4501379f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
@@ -15,6 +15,7 @@ import {
   ChromeBreadcrumb,
   CoreStart,
   ScopedHistory,
+  SavedObjectsClientContract,
 } from 'kibana/public';
 import { KibanaFeature } from '../../../features/common';
 import { Section, routeToAlertDetails } from './constants';
@@ -24,6 +25,7 @@ import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
 import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
 import { PluginStartContract as AlertingStart } from '../../../alerts/public';
 import { suspendedComponentWithProps } from './lib/suspended_component_with_props';
+import { Storage } from '../../../../../src/plugins/kibana_utils/public';
 
 const TriggersActionsUIHome = lazy(async () => import('./home'));
 const AlertDetailsRoute = lazy(
@@ -31,13 +33,14 @@ const AlertDetailsRoute = lazy(
 );
 
 export interface AppDeps {
-  dataPlugin: DataPublicPluginStart;
+  data: DataPublicPluginStart;
   charts: ChartsPluginStart;
   chrome: ChromeStart;
   alerts?: AlertingStart;
   navigateToApp: CoreStart['application']['navigateToApp'];
   docLinks: DocLinksStart;
   toastNotifications: ToastsSetup;
+  storage?: Storage;
   http: HttpSetup;
   uiSettings: IUiSettingsClient;
   setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void;
@@ -45,6 +48,9 @@ export interface AppDeps {
   actionTypeRegistry: ActionTypeRegistryContract;
   alertTypeRegistry: AlertTypeRegistryContract;
   history: ScopedHistory;
+  savedObjects?: {
+    client: SavedObjectsClientContract;
+  };
   kibanaFeatures: KibanaFeature[];
 }
 
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app_context.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app_context.tsx
index bf2e0c7274e7b..a4568d069c21c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app_context.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app_context.tsx
@@ -5,6 +5,7 @@
  */
 
 import React, { createContext, useContext } from 'react';
+import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
 import { AppDeps } from './app';
 
 const AppContext = createContext<AppDeps | null>(null);
@@ -16,7 +17,11 @@ export const AppContextProvider = ({
   appDeps: AppDeps | null;
   children: React.ReactNode;
 }) => {
-  return appDeps ? <AppContext.Provider value={appDeps}>{children}</AppContext.Provider> : null;
+  return appDeps ? (
+    <KibanaContextProvider services={appDeps}>
+      <AppContext.Provider value={appDeps}>{children}</AppContext.Provider>
+    </KibanaContextProvider>
+  ) : null;
 };
 
 export const useAppDependencies = (): AppDeps => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx
index bb46fd02a98a9..e18bf4ce84871 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx
@@ -6,21 +6,20 @@
 
 import React from 'react';
 import { render, unmountComponentAtNode } from 'react-dom';
-import { SavedObjectsClientContract } from 'src/core/public';
-
 import { App, AppDeps } from './app';
 import { setSavedObjectsClient } from '../common/lib/data_apis';
 
 interface BootDeps extends AppDeps {
   element: HTMLElement;
-  savedObjects: SavedObjectsClientContract;
   I18nContext: any;
 }
 
 export const boot = (bootDeps: BootDeps) => {
-  const { I18nContext, element, savedObjects, ...appDeps } = bootDeps;
+  const { I18nContext, element, ...appDeps } = bootDeps;
 
-  setSavedObjectsClient(savedObjects);
+  if (appDeps.savedObjects) {
+    setSavedObjectsClient(appDeps.savedObjects.client);
+  }
 
   render(
     <I18nContext>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
index 65d5389078880..71e1c60a92aed 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
@@ -55,7 +55,7 @@ describe('actions_connectors_list component empty', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -165,7 +165,7 @@ describe('actions_connectors_list component with items', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -256,7 +256,7 @@ describe('actions_connectors_list component empty with show only capability', ()
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -348,7 +348,7 @@ describe('actions_connectors_list with show only capability', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -452,7 +452,7 @@ describe('actions_connectors_list component with disabled items', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
       injectedMetadata: mockes.injectedMetadata,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
index 70b6fb0b750dd..c2a7635b4cf96 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
@@ -42,7 +42,7 @@ jest.mock('../../../app_context', () => ({
     toastNotifications: mockes.notifications.toasts,
     docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' },
     uiSettings: mockes.uiSettings,
-    dataPlugin: jest.fn(),
+    data: jest.fn(),
     charts: jest.fn(),
   })),
 }));
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
index abd8127962561..196ca603fa731 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
@@ -69,7 +69,7 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
     uiSettings,
     docLinks,
     charts,
-    dataPlugin,
+    data,
     setBreadcrumbs,
     chrome,
   } = useAppDependencies();
@@ -157,11 +157,11 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
                             uiSettings,
                             docLinks,
                             charts,
-                            dataFieldsFormats: dataPlugin.fieldFormats,
+                            dataFieldsFormats: data.fieldFormats,
                             reloadAlerts: setAlert,
                             capabilities,
-                            dataUi: dataPlugin.ui,
-                            dataIndexPatterns: dataPlugin.indexPatterns,
+                            dataUi: data.ui,
+                            dataIndexPatterns: data.indexPatterns,
                           }}
                         >
                           <AlertEdit
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
index aae0c6ea7e9e4..a69ee7102c13c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
@@ -82,7 +82,7 @@ describe('alert_add', () => {
       toastNotifications: mocks.notifications.toasts,
       http: mocks.http,
       uiSettings: mocks.uiSettings,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       actionTypeRegistry,
       alertTypeRegistry,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx
index 611846cf4a521..a29c112b536fb 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx
@@ -108,7 +108,7 @@ describe('alerts_list component empty', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -279,7 +279,7 @@ describe('alerts_list component with items', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -362,7 +362,7 @@ describe('alerts_list component empty with show only capability', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
@@ -483,7 +483,7 @@ describe('alerts_list with show only capability', () => {
     const deps = {
       chrome,
       docLinks,
-      dataPlugin: dataPluginMock.createStartContract(),
+      data: dataPluginMock.createStartContract(),
       charts: chartPluginMock.createStartContract(),
       alerting: alertingPluginMock.createStartContract(),
       toastNotifications: mockes.notifications.toasts,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index 75f359888a858..11d6f3470fec2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -83,7 +83,7 @@ export const AlertsList: React.FunctionComponent = () => {
     uiSettings,
     docLinks,
     charts,
-    dataPlugin,
+    data,
     kibanaFeatures,
   } = useAppDependencies();
   const canExecuteActions = hasExecuteActionsCapability(capabilities);
@@ -668,10 +668,10 @@ export const AlertsList: React.FunctionComponent = () => {
           uiSettings,
           docLinks,
           charts,
-          dataFieldsFormats: dataPlugin.fieldFormats,
+          dataFieldsFormats: data.fieldFormats,
           capabilities,
-          dataUi: dataPlugin.ui,
-          dataIndexPatterns: dataPlugin.indexPatterns,
+          dataUi: data.ui,
+          dataIndexPatterns: data.indexPatterns,
           kibanaFeatures,
         }}
       >
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/test_utils/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/test_utils/index.ts
index b5ab53d868cf1..061f3faaa6c0f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/test_utils/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/test_utils/index.ts
@@ -26,20 +26,20 @@ export async function getMockedAppDependencies() {
   const kibanaFeatures = await featuresPluginMock.createStart().getFeatures();
 
   return {
+    data: dataPluginMock.createStartContract(),
+    charts: chartPluginMock.createStartContract(),
     chrome,
+    navigateToApp,
     docLinks,
-    dataPlugin: dataPluginMock.createStartContract(),
-    charts: chartPluginMock.createStartContract(),
-    alerting: alertingPluginMock.createStartContract(),
     toastNotifications: coreSetupMock.notifications.toasts,
     http: coreSetupMock.http,
     uiSettings: coreSetupMock.uiSettings,
-    navigateToApp,
-    capabilities,
-    history: scopedHistoryMock.create(),
     setBreadcrumbs: jest.fn(),
+    capabilities,
     actionTypeRegistry,
     alertTypeRegistry,
+    history: scopedHistoryMock.create(),
+    alerting: alertingPluginMock.createStartContract(),
     kibanaFeatures,
   };
 }
diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts
index 3794112e1d502..3187451d2600e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/index.ts
@@ -17,6 +17,7 @@ export {
   AlertTypeModel,
   ActionType,
   ActionTypeRegistryContract,
+  AlertTypeRegistryContract,
   AlertTypeParamsExpressionProps,
   ValidationResult,
   ActionVariable,
diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
index 2d93d368ad8e5..a30747afe6914 100644
--- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
@@ -22,6 +22,7 @@ import {
 import { ChartsPluginStart } from '../../../../src/plugins/charts/public';
 import { PluginStartContract as AlertingStart } from '../../alerts/public';
 import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import { Storage } from '../../../../src/plugins/kibana_utils/public';
 
 export interface TriggersAndActionsUIPublicPluginSetup {
   actionTypeRegistry: TypeRegistry<ActionTypeModel>;
@@ -102,16 +103,17 @@ export class Plugin
         const { boot } = await import('./application/boot');
         const kibanaFeatures = await pluginsStart.features.getFeatures();
         return boot({
-          dataPlugin: pluginsStart.data,
+          data: pluginsStart.data,
           charts: pluginsStart.charts,
           alerts: pluginsStart.alerts,
           element: params.element,
           toastNotifications: coreStart.notifications.toasts,
+          storage: new Storage(window.localStorage),
           http: coreStart.http,
           uiSettings: coreStart.uiSettings,
           docLinks: coreStart.docLinks,
           chrome: coreStart.chrome,
-          savedObjects: coreStart.savedObjects.client,
+          savedObjects: coreStart.savedObjects,
           I18nContext: coreStart.i18n.Context,
           capabilities: coreStart.application.capabilities,
           navigateToApp: coreStart.application.navigateToApp,