From b74b93593cecec34e2745c30811c6929bf8a72c7 Mon Sep 17 00:00:00 2001
From: Dominique Clarke <dominique.clarke@elastic.co>
Date: Thu, 12 Dec 2024 09:41:03 -0500
Subject: [PATCH] [Synthetics] migrate first set of tests (#198950)

## Summary

Relates to #196229

Migrates Synthetics tests to deployment agnostic

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Shahzad <shahzad31comp@gmail.com>
---
 .github/CODEOWNERS                            |    3 +
 .../synthetics/README.md                      |   24 +
 .../synthetics_service/synthetics_service.ts  |    7 +-
 .../synthetics/create_monitor.ts              |  304 +++
 .../create_monitor_private_location.ts        |  560 +++++
 .../synthetics/create_monitor_project.ts      | 2194 +++++++++++++++++
 ...create_monitor_project_private_location.ts |  162 ++
 .../synthetics/create_monitor_public_api.ts   |  280 +++
 .../synthetics/create_update_params.ts        |  385 +++
 .../synthetics/delete_monitor.ts              |  164 ++
 .../synthetics/delete_monitor_project.ts      |  521 ++++
 .../observability/synthetics/edit_monitor.ts  |  370 +++
 .../synthetics/edit_monitor_public_api.ts     |  301 +++
 .../synthetics/enable_default_alerting.ts     |  330 +++
 .../synthetics/fixtures/browser_monitor.json  |   60 +
 .../synthetics/fixtures/http_monitor.json     |   83 +
 .../synthetics/fixtures/icmp_monitor.json     |   35 +
 .../fixtures/inspect_browser_monitor.json     |   85 +
 .../fixtures/project_browser_monitor.json     |   30 +
 .../fixtures/project_http_monitor.json        |   86 +
 .../fixtures/project_icmp_monitor.json        |   47 +
 .../fixtures/project_tcp_monitor.json         |   44 +
 .../synthetics/fixtures/tcp_monitor.json      |   43 +
 .../observability/synthetics/get_filters.ts   |   87 +
 .../observability/synthetics/get_monitor.ts   |  353 +++
 .../synthetics/get_monitor_project.ts         |  741 ++++++
 .../synthetics/helpers/get_fixture_json.ts    |   21 +
 .../synthetics/helpers/monitor.ts             |   21 +
 .../apis/observability/synthetics/index.ts    |   32 +
 .../synthetics/inspect_monitor.ts             |  246 ++
 .../synthetics/sample_data/test_policy.ts     |  575 +++++
 .../test_project_monitor_policy.ts            |  803 ++++++
 .../observability/synthetics/suggestions.ts   |  276 +++
 .../synthetics/sync_global_params.ts          |  354 +++
 .../synthetics/synthetics_enablement.ts       |  352 +++
 .../synthetics/test_now_monitor.ts            |   98 +
 .../configs/serverless/oblt.index.ts          |    1 +
 .../configs/stateful/oblt.index.ts            |    1 +
 .../default_configs/serverless.config.base.ts |    5 +
 .../default_configs/stateful.config.base.ts   |    4 +
 .../services/synthetics_monitor.ts            |  240 ++
 .../services/synthetics_private_location.ts   |   94 +
 42 files changed, 10419 insertions(+), 3 deletions(-)
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_private_location.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project_private_location.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_public_api.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_update_params.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor_project.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor_public_api.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/enable_default_alerting.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/browser_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/http_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/icmp_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/inspect_browser_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_browser_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_http_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_icmp_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_tcp_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/tcp_monitor.json
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_filters.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor_project.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/get_fixture_json.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/index.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/inspect_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_policy.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_project_monitor_policy.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/suggestions.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sync_global_params.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/synthetics_enablement.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/test_now_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/services/synthetics_monitor.ts
 create mode 100644 x-pack/test/api_integration/deployment_agnostic/services/synthetics_private_location.ts

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d1f134b0c0737..b69e21d8e5916 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1358,6 +1358,9 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql
 /x-pack/test/api_integration/deployment_agnostic/apis/observability/slo/ @elastic/obs-ux-management-team
 /x-pack/test/api_integration/deployment_agnostic/services/alerting_api @elastic/obs-ux-management-team
 /x-pack/test/api_integration/deployment_agnostic/services/slo_api @elastic/obs-ux-management-team
+/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/ @elastic/obs-ux-management-team
+/x-pack/test/api_integration/deployment_agnostic/services/synthetics_monitors @elastic/obs-ux-management-team
+/x-pack/test/api_integration/deployment_agnostic/services/synthetics_private_location @elastic/obs-ux-management-team
 
 # Elastic Stack Monitoring
 /x-pack/test/monitoring_api_integration @elastic/stack-monitoring
diff --git a/x-pack/plugins/observability_solution/synthetics/README.md b/x-pack/plugins/observability_solution/synthetics/README.md
index bdc078e8607d7..d921fc2eb167a 100644
--- a/x-pack/plugins/observability_solution/synthetics/README.md
+++ b/x-pack/plugins/observability_solution/synthetics/README.md
@@ -95,3 +95,27 @@ From the `~/x-pack` directory:
 Start the server: `node scripts/functional_tests_server --config test/accessibility/config.ts`
 
 Run the uptime `a11y` tests: `node scripts/functional_test_runner.js --config test/accessibility/config.ts --grep=uptime`
+
+
+## Deployment agnostic API Integration Tests
+The Synthetics tests are located under `x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics` folder. In order to run the SLO tests of your interest, you can grep accordingly. Use the commands below to run all SLO tests (`grep=SyntheticsAPITests`) on stateful or serverless.
+
+### Stateful
+
+```
+# start server
+node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts
+
+# run tests
+node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep=SyntheticsAPITests
+```
+
+### Serverless
+
+```
+# start server
+node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts
+
+# run tests
+node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep=SyntheticsAPITests
+```
diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/synthetics_service.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/synthetics_service.ts
index 9e4229aba0a0e..164515ad76c1b 100644
--- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/synthetics_service.ts
+++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/synthetics_service.ts
@@ -309,9 +309,10 @@ export class SyntheticsService {
     return this.server.coreStart?.elasticsearch.client.asInternalUser;
   }
 
-  async getOutput() {
+  async getOutput({ inspect }: { inspect: boolean } = { inspect: false }) {
     const { apiKey, isValid } = await getAPIKeyForSyntheticsService({ server: this.server });
-    if (!isValid) {
+    // do not check for api key validity if inspecting
+    if (!isValid && !inspect) {
       this.server.logger.error(
         'API key is not valid. Cannot push monitor configuration to synthetics public testing locations'
       );
@@ -332,7 +333,7 @@ export class SyntheticsService {
     const monitors = this.formatConfigs(config);
     const license = await this.getLicense();
 
-    const output = await this.getOutput();
+    const output = await this.getOutput({ inspect: true });
     if (output) {
       return await this.apiClient.inspect({
         monitors,
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor.ts
new file mode 100644
index 0000000000000..1e985975db1d4
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor.ts
@@ -0,0 +1,304 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import expect from '@kbn/expect';
+import { RoleCredentials, SamlAuthProviderType } from '@kbn/ftr-common-functional-services';
+import epct from 'expect';
+import moment from 'moment/moment';
+import { v4 as uuidv4 } from 'uuid';
+import { omit, omitBy } from 'lodash';
+import {
+  ConfigKey,
+  MonitorTypeEnum,
+  HTTPFields,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
+import {
+  removeMonitorEmptyValues,
+  transformPublicKeys,
+} from '@kbn/synthetics-plugin/server/routes/monitor_cruds/formatters/saved_object_to_monitor';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export const addMonitorAPIHelper = async (
+  supertestAPI: any,
+  monitor: any,
+  statusCode = 200,
+  roleAuthc: RoleCredentials,
+  samlAuth: SamlAuthProviderType
+) => {
+  const result = await supertestAPI
+    .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+    .set(roleAuthc.apiKeyHeader)
+    .set(samlAuth.getInternalRequestHeader())
+    .send(monitor)
+    .expect(statusCode);
+
+  if (statusCode === 200) {
+    const { created_at: createdAt, updated_at: updatedAt, id, config_id: configId } = result.body;
+    expect(id).not.empty();
+    expect(configId).not.empty();
+    expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+    return {
+      rawBody: result.body,
+      body: {
+        ...omit(result.body, ['created_at', 'updated_at', 'id', 'config_id', 'form_monitor_type']),
+      },
+    };
+  }
+  return result.body;
+};
+
+export const keyToOmitList = [
+  'created_at',
+  'updated_at',
+  'id',
+  'config_id',
+  'form_monitor_type',
+  'spaceId',
+  'private_locations',
+];
+
+export const omitMonitorKeys = (monitor: any) => {
+  return omitBy(omit(transformPublicKeys(monitor), keyToOmitList), removeMonitorEmptyValues);
+};
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('AddNewMonitorsUI', function () {
+    const supertestAPI = getService('supertestWithoutAuth');
+    const samlAuth = getService('samlAuth');
+    const kibanaServer = getService('kibanaServer');
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+    const privateLocationsService = new PrivateLocationTestService(getService);
+
+    let privateLocation: PrivateLocation;
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+    let editorRoleAuthc: RoleCredentials;
+
+    const addMonitorAPI = async (monitor: any, statusCode = 200) => {
+      return addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorRoleAuthc, samlAuth);
+    };
+
+    const deleteMonitor = async (
+      monitorId?: string | string[],
+      statusCode = 200,
+      spaceId?: string
+    ) => {
+      return monitorTestService.deleteMonitor(editorRoleAuthc, monitorId, statusCode, spaceId);
+    };
+
+    before(async () => {
+      _httpMonitorJson = getFixtureJson('http_monitor');
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorRoleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+    });
+
+    beforeEach(async () => {
+      privateLocation = await privateLocationsService.addTestPrivateLocation();
+      httpMonitorJson = {
+        ..._httpMonitorJson,
+        locations: [privateLocation],
+      };
+    });
+
+    it('returns the newly added monitor', async () => {
+      const newMonitor = httpMonitorJson;
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse).eql(omitMonitorKeys(newMonitor));
+    });
+
+    it('returns bad request if payload is invalid for HTTP monitor', async () => {
+      // Delete a required property to make payload invalid
+      const newMonitor = { ...httpMonitorJson, 'check.request.headers': null };
+      await addMonitorAPI(newMonitor, 400);
+    });
+
+    it('returns bad request if monitor type is invalid', async () => {
+      const newMonitor = { ...httpMonitorJson, type: 'invalid-data-steam' };
+
+      const apiResponse = await addMonitorAPI(newMonitor, 400);
+
+      expect(apiResponse.message).eql('Invalid value "invalid-data-steam" supplied to "type"');
+    });
+
+    it('can create valid monitors without all defaults', async () => {
+      // Delete a required property to make payload invalid
+      const newMonitor = {
+        name: 'Sample name',
+        type: 'http',
+        urls: 'https://elastic.co',
+        locations: [privateLocation],
+      };
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse).eql(
+        omitMonitorKeys({
+          ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP],
+          ...newMonitor,
+        })
+      );
+    });
+
+    it('can disable retries', async () => {
+      const maxAttempts = 1;
+      const newMonitor = {
+        max_attempts: maxAttempts,
+        urls: 'https://elastic.co',
+        name: `Sample name ${uuidv4()}`,
+        type: 'http',
+        locations: [privateLocation],
+      };
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: false }));
+    });
+
+    it('can enable retries with max attempts', async () => {
+      const maxAttempts = 2;
+      const newMonitor = {
+        max_attempts: maxAttempts,
+        urls: 'https://elastic.co',
+        name: `Sample name ${uuidv4()}`,
+        type: 'http',
+        locations: [privateLocation],
+      };
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: true }));
+    });
+
+    it('can enable retries', async () => {
+      const newMonitor = {
+        retest_on_failure: false,
+        urls: 'https://elastic.co',
+        name: `Sample name ${uuidv4()}`,
+        type: 'http',
+        locations: [privateLocation],
+      };
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: false }));
+    });
+
+    it('cannot create a invalid monitor without a monitor type', async () => {
+      // Delete a required property to make payload invalid
+      const newMonitor = {
+        name: 'Sample name',
+        url: 'https://elastic.co',
+        locations: [privateLocation],
+      };
+      await addMonitorAPI(newMonitor, 400);
+    });
+
+    it('omits unknown keys', async () => {
+      // Delete a required property to make payload invalid
+      const newMonitor = {
+        name: 'Sample name',
+        url: 'https://elastic.co',
+        unknownKey: 'unknownValue',
+        type: 'http',
+        locations: [privateLocation],
+      };
+      const apiResponse = await addMonitorAPI(newMonitor, 400);
+      expect(apiResponse.message).not.to.have.keys(
+        'Invalid monitor key(s) for http type:  unknownKey","attributes":{"details":"Invalid monitor key(s) for http type:  unknownKey'
+      );
+    });
+
+    it('sets namespace to Kibana space when not set to a custom namespace', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const EXPECTED_NAMESPACE = formatKibanaNamespace(SPACE_ID);
+      privateLocation = await privateLocationsService.addTestPrivateLocation(SPACE_ID);
+      const monitor = {
+        ...httpMonitorJson,
+        [ConfigKey.NAMESPACE]: 'default',
+        locations: [privateLocation],
+      };
+      let monitorId = '';
+
+      try {
+        await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+        const apiResponse = await supertestAPI
+          .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorRoleAuthc.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+        monitorId = apiResponse.body.id;
+        expect(apiResponse.body[ConfigKey.NAMESPACE]).eql(EXPECTED_NAMESPACE);
+      } finally {
+        await deleteMonitor(monitorId, 200, SPACE_ID);
+      }
+    });
+
+    it('preserves the passed namespace when preserve_namespace is passed', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      privateLocation = await privateLocationsService.addTestPrivateLocation(SPACE_ID);
+      const monitor = {
+        ...httpMonitorJson,
+        [ConfigKey.NAMESPACE]: 'default',
+        locations: [privateLocation],
+      };
+      let monitorId = '';
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      try {
+        const apiResponse = await supertestAPI
+          .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({ preserve_namespace: true })
+          .set(editorRoleAuthc.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+        monitorId = apiResponse.body.id;
+        expect(apiResponse.body[ConfigKey.NAMESPACE]).eql('default');
+      } finally {
+        await deleteMonitor(monitorId, 200, SPACE_ID);
+      }
+    });
+
+    it('sets namespace to custom namespace when set', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      privateLocation = await privateLocationsService.addTestPrivateLocation(SPACE_ID);
+      const monitor = {
+        ...httpMonitorJson,
+        locations: [privateLocation],
+      };
+      let monitorId = '';
+
+      try {
+        await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+        const apiResponse = await supertestAPI
+          .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorRoleAuthc.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+        monitorId = apiResponse.body.id;
+        expect(apiResponse.body[ConfigKey.NAMESPACE]).eql(monitor[ConfigKey.NAMESPACE]);
+      } finally {
+        await deleteMonitor(monitorId, 200, SPACE_ID);
+      }
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_private_location.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_private_location.ts
new file mode 100644
index 0000000000000..7141eab30d8f5
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_private_location.ts
@@ -0,0 +1,560 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import moment from 'moment';
+import semver from 'semver';
+import { v4 as uuidv4 } from 'uuid';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  ConfigKey,
+  HTTPFields,
+  PrivateLocation,
+  ServiceLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { omit } from 'lodash';
+import { PackagePolicy } from '@kbn/fleet-plugin/common';
+import expect from '@kbn/expect';
+import rawExpect from 'expect';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { comparePolicies, getTestSyntheticsPolicy } from './sample_data/test_policy';
+import {
+  INSTALLED_VERSION,
+  PrivateLocationTestService,
+} from '../../../services/synthetics_private_location';
+import { addMonitorAPIHelper, keyToOmitList, omitMonitorKeys } from './create_monitor';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('PrivateLocationAddMonitor', function () {
+    const kibanaServer = getService('kibanaServer');
+    const supertestAPI = getService('supertestWithoutAuth');
+    const supertestWithAuth = getService('supertest');
+    const samlAuth = getService('samlAuth');
+
+    let testFleetPolicyID: string;
+    let editorUser: RoleCredentials;
+    let privateLocations: PrivateLocation[] = [];
+    const testPolicyName = 'Fleet test server policy' + Date.now();
+
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+
+    const addMonitorAPI = async (monitor: any, statusCode = 200) => {
+      return addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorUser, samlAuth);
+    };
+
+    const deleteMonitor = async (
+      monitorId?: string | string[],
+      statusCode = 200,
+      spaceId?: string
+    ) => {
+      return monitorTestService.deleteMonitor(editorUser, monitorId, statusCode, spaceId);
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      await testPrivateLocations.installSyntheticsPackage();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+
+      _httpMonitorJson = getFixtureJson('http_monitor');
+    });
+
+    beforeEach(() => {
+      httpMonitorJson = {
+        ..._httpMonitorJson,
+        locations: privateLocations ? [privateLocations[0]] : [],
+      };
+    });
+
+    it('adds a test fleet policy', async () => {
+      const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
+      testFleetPolicyID = apiResponse.body.item.id;
+    });
+
+    it('add a test private location', async () => {
+      privateLocations = await testPrivateLocations.setTestLocations([testFleetPolicyID]);
+
+      const apiResponse = await supertestAPI
+        .get(SYNTHETICS_API_URLS.SERVICE_LOCATIONS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const testResponse: Array<PrivateLocation | ServiceLocation> = [
+        {
+          id: testFleetPolicyID,
+          isServiceManaged: false,
+          isInvalid: false,
+          label: privateLocations[0].label,
+          geo: {
+            lat: 0,
+            lon: 0,
+          },
+          agentPolicyId: testFleetPolicyID,
+        },
+      ];
+
+      rawExpect(apiResponse.body.locations).toEqual(rawExpect.arrayContaining(testResponse));
+    });
+
+    it('does not add a monitor if there is an error in creating integration', async () => {
+      const newMonitor = { ...httpMonitorJson };
+      const invalidName = 'invalid name';
+
+      const location = {
+        id: 'invalidLocation',
+        label: privateLocations[0].label,
+        isServiceManaged: false,
+        geo: {
+          lat: 0,
+          lon: 0,
+        },
+      };
+
+      newMonitor.name = invalidName;
+
+      const apiResponse = await supertestAPI
+        .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ ...newMonitor, locations: [location] })
+        .expect(400);
+
+      expect(apiResponse.body).eql({
+        statusCode: 400,
+        error: 'Bad Request',
+        message: `Invalid locations specified. Private Location(s) 'invalidLocation' not found. Available private locations are '${privateLocations[0].label}'`,
+      });
+
+      const apiGetResponse = await supertestAPI
+        .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + `?query="${invalidName}"`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      // verify that no monitor was added
+      expect(apiGetResponse.body.monitors?.length).eql(0);
+    });
+
+    let newMonitorId: string;
+
+    it('adds a monitor in private location', async () => {
+      const newMonitor = {
+        ...httpMonitorJson,
+        locations: [privateLocations[0]],
+      };
+
+      const { body, rawBody } = await addMonitorAPI(newMonitor);
+
+      expect(body).eql(omitMonitorKeys(newMonitor));
+      newMonitorId = rawBody.id;
+    });
+
+    it('added an integration for previously added monitor', async () => {
+      const apiResponse = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponse.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy?.policy_id).eql(testFleetPolicyID);
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: httpMonitorJson.name,
+          id: newMonitorId,
+          location: { id: testFleetPolicyID, name: privateLocations[0].label },
+        })
+      );
+    });
+
+    let testFleetPolicyID2: string;
+    let newLocations: PrivateLocation[] = [];
+
+    it('edits a monitor with additional private location', async () => {
+      const resPolicy = await testPrivateLocations.addFleetPolicy(testPolicyName + 1);
+      testFleetPolicyID2 = resPolicy.body.item.id;
+
+      newLocations = await testPrivateLocations.setTestLocations([
+        testFleetPolicyID,
+        testFleetPolicyID2,
+      ]);
+
+      httpMonitorJson.locations.push({
+        id: testFleetPolicyID2,
+        agentPolicyId: testFleetPolicyID2,
+        label: newLocations[1].label,
+        isServiceManaged: false,
+        geo: {
+          lat: 0,
+          lon: 0,
+        },
+      });
+
+      const apiResponse = await supertestAPI
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + newMonitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(httpMonitorJson);
+
+      const { created_at: createdAt, updated_at: updatedAt } = apiResponse.body;
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+      expect(omit(apiResponse.body, keyToOmitList)).eql(
+        omitMonitorKeys({
+          ...omit(httpMonitorJson, ['urls']),
+          url: httpMonitorJson.urls,
+          updated_at: updatedAt,
+          revision: 2,
+        })
+      );
+    });
+
+    it('added an integration for second location in edit monitor', async () => {
+      const apiResponsePolicy = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      let packagePolicy = apiResponsePolicy.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: httpMonitorJson.name,
+          id: newMonitorId,
+          location: { id: testFleetPolicyID, name: privateLocations[0].label },
+        })
+      );
+
+      packagePolicy = apiResponsePolicy.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID2 + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID2);
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: httpMonitorJson.name,
+          id: newMonitorId,
+          location: {
+            name: newLocations[1].label,
+            id: testFleetPolicyID2,
+          },
+        })
+      );
+    });
+
+    it('deletes integration for a removed location from monitor', async () => {
+      httpMonitorJson.locations = httpMonitorJson.locations.filter(
+        ({ id }) => id !== testFleetPolicyID2
+      );
+
+      await supertestAPI
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + newMonitorId + '?internal=true')
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(httpMonitorJson)
+        .expect(200);
+
+      const apiResponsePolicy = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      let packagePolicy = apiResponsePolicy.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: httpMonitorJson.name,
+          id: newMonitorId,
+          location: { id: testFleetPolicyID, name: privateLocations[0].label },
+        })
+      );
+
+      packagePolicy = apiResponsePolicy.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID2 + '-default'
+      );
+
+      expect(packagePolicy).eql(undefined);
+    });
+
+    it('deletes integration for a deleted monitor', async () => {
+      await deleteMonitor(newMonitorId);
+
+      const apiResponsePolicy = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponsePolicy.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy).eql(undefined);
+    });
+
+    // it('handles spaces', async () => {
+    //   const username = 'admin';
+    //   const password = `${username}-password`;
+    //   const roleName = 'uptime-role';
+    //   const SPACE_ID = `test-space-${uuidv4()}`;
+    //   const SPACE_NAME = `test-space-name ${uuidv4()}`;
+    //   let monitorId = '';
+    //   const monitor = {
+    //     ...httpMonitorJson,
+    //     name: `Test monitor ${uuidv4()}`,
+    //     [ConfigKey.NAMESPACE]: 'default',
+    //     locations: [
+    //       {
+    //         id: testFleetPolicyID,
+    //         agentPolicyId: testFleetPolicyID,
+    //         label: 'Test private location 0',
+    //         isServiceManaged: false,
+    //         geo: {
+    //           lat: 0,
+    //           lon: 0,
+    //         },
+    //       },
+    //     ],
+    //   };
+
+    //   try {
+    //     await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+    //     await security.role.create(roleName, {
+    //       kibana: [
+    //         {
+    //           feature: {
+    //             uptime: ['all'],
+    //             actions: ['all'],
+    //           },
+    //           spaces: ['*'],
+    //         },
+    //       ],
+    //     });
+    //     await security.user.create(username, {
+    //       password,
+    //       roles: [roleName],
+    //       full_name: 'a kibana user',
+    //     });
+    //     const apiResponse = await supertestWithoutAuth
+    //       .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+    //       .auth(username, password)
+    //       .set('kbn-xsrf', 'true')
+    //       .send(monitor)
+    //       .expect(200);
+
+    //     const { created_at: createdAt, updated_at: updatedAt } = apiResponse.body;
+    //     expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+    //     expect(omit(apiResponse.body, keyToOmitList)).eql(
+    //       omitMonitorKeys({
+    //         ...monitor,
+    //         [ConfigKey.NAMESPACE]: formatKibanaNamespace(SPACE_ID),
+    //         url: apiResponse.body.url,
+    //       })
+    //     );
+    //     monitorId = apiResponse.body.id;
+
+    //     const policyResponse = await supertestWithAuth.get(
+    //       '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+    //     );
+
+    //     const packagePolicy = policyResponse.body.items.find(
+    //       (pkgPolicy: PackagePolicy) =>
+    //         pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-${SPACE_ID}`
+    //     );
+
+    //     expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+    //     expect(packagePolicy.name).eql(`${monitor.name}-Test private location 0-${SPACE_ID}`);
+    //     comparePolicies(
+    //       packagePolicy,
+    //       getTestSyntheticsPolicy({
+    //         name: monitor.name,
+    //         id: monitorId,
+    //         location: { id: testFleetPolicyID },
+    //         namespace: formatKibanaNamespace(SPACE_ID),
+    //         spaceId: SPACE_ID,
+    //       })
+    //     );
+    //     await supertestWithoutAuth
+    //       .delete(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+    //       .auth(username, password)
+    //       .set('kbn-xsrf', 'true')
+    //       .send({ ids: [monitorId] })
+    //       .expect(200);
+    //   } finally {
+    //     await security.user.delete(username);
+    //     await security.role.delete(roleName);
+    //   }
+    // });
+
+    it('handles is_tls_enabled true', async () => {
+      let monitorId = '';
+
+      const monitor = {
+        ...httpMonitorJson,
+        locations: [
+          {
+            id: testFleetPolicyID,
+            label: privateLocations[0].label,
+            isServiceManaged: false,
+          },
+        ],
+        [ConfigKey.METADATA]: {
+          is_tls_enabled: true,
+        },
+      };
+
+      try {
+        const apiResponse = await supertestAPI
+          .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+
+        monitorId = apiResponse.body.id;
+
+        const policyResponse = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = policyResponse.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
+        );
+        comparePolicies(
+          packagePolicy,
+          getTestSyntheticsPolicy({
+            name: monitor.name,
+            id: monitorId,
+            location: { id: testFleetPolicyID, name: privateLocations[0].label },
+            isTLSEnabled: true,
+          })
+        );
+      } finally {
+        await deleteMonitor(monitorId);
+      }
+    });
+
+    it('handles is_tls_enabled false', async () => {
+      let monitorId = '';
+
+      const monitor = {
+        ...httpMonitorJson,
+        locations: [
+          {
+            id: testFleetPolicyID,
+            label: privateLocations[0].label,
+            isServiceManaged: false,
+          },
+        ],
+        [ConfigKey.METADATA]: {
+          is_tls_enabled: false,
+        },
+      };
+
+      try {
+        const apiResponse = await supertestAPI
+          .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+
+        monitorId = apiResponse.body.id;
+
+        const policyResponse = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = policyResponse.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
+        );
+        comparePolicies(
+          packagePolicy,
+          getTestSyntheticsPolicy({
+            name: monitor.name,
+            id: monitorId,
+            location: { id: testFleetPolicyID, name: privateLocations[0].label },
+          })
+        );
+      } finally {
+        await deleteMonitor(monitorId);
+      }
+    });
+
+    it('handles auto upgrading policies', async () => {
+      let monitorId = '';
+
+      const monitor = {
+        ...httpMonitorJson,
+        name: `Test monitor ${uuidv4()}`,
+        [ConfigKey.NAMESPACE]: 'default',
+        locations: [
+          {
+            id: testFleetPolicyID,
+            label: privateLocations[0].label,
+            isServiceManaged: false,
+          },
+        ],
+      };
+
+      try {
+        const apiResponse = await supertestAPI
+          .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitor)
+          .expect(200);
+        monitorId = apiResponse.body.id;
+
+        const policyResponse = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = policyResponse.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
+        );
+
+        expect(packagePolicy.package.version).eql(INSTALLED_VERSION);
+
+        await supertestWithAuth.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
+        const policyResponseAfterUpgrade = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+        const packagePolicyAfterUpgrade = policyResponseAfterUpgrade.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default`
+        );
+        expect(semver.gte(packagePolicyAfterUpgrade.package.version, INSTALLED_VERSION)).eql(true);
+      } finally {
+        await deleteMonitor(monitorId);
+      }
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project.ts
new file mode 100644
index 0000000000000..d8f7e78607293
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project.ts
@@ -0,0 +1,2194 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import expect from '@kbn/expect';
+import rawExpect from 'expect';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  ConfigKey,
+  ProjectMonitorsRequest,
+  PrivateLocation,
+  ServiceLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters';
+import { REQUEST_TOO_LARGE } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/add_monitor_project';
+import { PackagePolicy } from '@kbn/fleet-plugin/common';
+import {
+  PROFILE_VALUES_ENUM,
+  PROFILES_MAP,
+} from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
+import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { comparePolicies } from './sample_data/test_policy';
+import {
+  getTestProjectSyntheticsPolicy,
+  getTestProjectSyntheticsPolicyLightweight,
+} from './sample_data/test_project_monitor_policy';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('AddProjectMonitors', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const supertestWithAuth = getService('supertest');
+    const kibanaServer = getService('kibanaServer');
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    const samlAuth = getService('samlAuth');
+
+    let projectMonitors: ProjectMonitorsRequest;
+    let httpProjectMonitors: ProjectMonitorsRequest;
+    let tcpProjectMonitors: ProjectMonitorsRequest;
+    let icmpProjectMonitors: ProjectMonitorsRequest;
+    let editorUser: RoleCredentials;
+    let viewerUser: RoleCredentials;
+    let privateLocations: PrivateLocation[] = [];
+
+    let testPolicyId1 = '';
+    let testPolicyId2 = '';
+    const testPolicyName = 'Fleet test server policy' + Date.now();
+
+    const setUniqueIds = (request: ProjectMonitorsRequest) => {
+      return {
+        ...request,
+        monitors: request.monitors.map((monitor) => ({ ...monitor, id: uuidv4() })),
+      };
+    };
+
+    const deleteMonitor = async (
+      journeyId: string,
+      projectId: string,
+      space: string = 'default'
+    ) => {
+      try {
+        const response = await supertest
+          .get(`/s/${space}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: "${journeyId}" AND ${syntheticsMonitorType}.attributes.project_id: "${projectId}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const { monitors } = response.body;
+        if (monitors[0]?.config_id) {
+          await monitorTestService.deleteMonitor(editorUser, monitors[0].config_id, 200, space);
+        }
+      } catch (e) {
+        // eslint-disable-next-line no-console
+        console.error(e);
+      }
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      viewerUser = await samlAuth.createM2mApiKeyWithRoleScope('viewer');
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      await testPrivateLocations.installSyntheticsPackage();
+
+      const apiResponse1 = await testPrivateLocations.addFleetPolicy(testPolicyName);
+      const apiResponse2 = await testPrivateLocations.addFleetPolicy(`${testPolicyName}-2`);
+      testPolicyId1 = apiResponse1.body.item.id;
+      testPolicyId2 = apiResponse2.body.item.id;
+      privateLocations = await testPrivateLocations.setTestLocations([
+        testPolicyId1,
+        testPolicyId2,
+      ]);
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'testGlobalParam', value: 'testGlobalParamValue' })
+        .expect(200);
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'testGlobalParam2', value: 'testGlobalParamValue2' })
+        .expect(200);
+      const spaces = (await kibanaServer.spaces.list()) as Array<{
+        id: string;
+      }>;
+      for (let i = 0; i < spaces.length; i++) {
+        if (spaces[i].id !== 'default') await kibanaServer.spaces.delete(spaces[i].id);
+      }
+    });
+
+    beforeEach(async () => {
+      await kibanaServer.savedObjects.clean({
+        types: ['synthetics-monitor', 'ingest-package-policies'],
+      });
+      const formatLocations = (monitors: ProjectMonitorsRequest['monitors']) => {
+        return monitors.map((monitor) => {
+          return {
+            ...monitor,
+            /* cannot configure public locations for deployment agnostic tests
+             * they would fail in MKI and ESS due to missing mock location */
+            locations: [],
+            privateLocations: [privateLocations[0].label],
+          };
+        });
+      };
+      projectMonitors = setUniqueIds({
+        monitors: formatLocations(getFixtureJson('project_browser_monitor').monitors),
+      });
+      httpProjectMonitors = setUniqueIds({
+        monitors: formatLocations(getFixtureJson('project_http_monitor').monitors),
+      });
+      tcpProjectMonitors = setUniqueIds({
+        monitors: formatLocations(getFixtureJson('project_tcp_monitor').monitors),
+      });
+      icmpProjectMonitors = setUniqueIds({
+        monitors: formatLocations(getFixtureJson('project_icmp_monitor').monitors),
+      });
+    });
+
+    it('project monitors - returns 404 for non-existing spaces', async () => {
+      const project = `test-project-${uuidv4()}`;
+      await supertest
+        .put(
+          `/s/i_dont_exist${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+            '{projectName}',
+            project
+          )}`
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(projectMonitors)
+        .expect(404);
+    });
+
+    it('project monitors - handles browser monitors', async () => {
+      const successfulMonitors = [projectMonitors.monitors[0]];
+      const project = `test-project-${uuidv4()}`;
+
+      const { body } = await supertest
+        .put(
+          SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(projectMonitors)
+        .expect(200);
+      expect(body).eql({
+        updatedMonitors: [],
+        createdMonitors: successfulMonitors.map((monitor) => monitor.id),
+        failedMonitors: [],
+      });
+
+      for (const monitor of successfulMonitors) {
+        const journeyId = monitor.id;
+        const createdMonitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const decryptedCreatedMonitor = await monitorTestService.getMonitor(
+          createdMonitorsResponse.body.monitors[0].config_id,
+          {
+            internal: true,
+            user: editorUser,
+          }
+        );
+
+        expect(decryptedCreatedMonitor.rawBody).to.eql({
+          __ui: {
+            script_source: {
+              file_name: '',
+              is_generated_script: false,
+            },
+          },
+          config_id: decryptedCreatedMonitor.rawBody.config_id,
+          custom_heartbeat_id: `${journeyId}-${project}-default`,
+          enabled: true,
+          alert: {
+            status: {
+              enabled: true,
+            },
+            tls: {
+              enabled: true,
+            },
+          },
+          'filter_journeys.match': 'check if title is present',
+          'filter_journeys.tags': [],
+          form_monitor_type: 'multistep',
+          ignore_https_errors: false,
+          journey_id: journeyId,
+          locations: [
+            {
+              geo: {
+                lat: 0,
+                lon: 0,
+              },
+              id: testPolicyId1,
+              agentPolicyId: testPolicyId1,
+              isServiceManaged: false,
+              label: privateLocations[0].label,
+            },
+          ],
+          name: 'check if title is present',
+          namespace: 'default',
+          origin: 'project',
+          original_space: 'default',
+          playwright_options: '{"headless":true,"chromiumSandbox":false}',
+          playwright_text_assertion: '',
+          project_id: project,
+          params: '',
+          revision: 1,
+          schedule: {
+            number: '10',
+            unit: 'm',
+          },
+          screenshots: 'on',
+          'service.name': '',
+          synthetics_args: [],
+          tags: [],
+          throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT],
+          'ssl.certificate': '',
+          'ssl.certificate_authorities': '',
+          'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
+          'ssl.verification_mode': 'full',
+          'ssl.key': '',
+          'ssl.key_passphrase': '',
+          'source.inline.script': '',
+          'source.project.content':
+            'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
+          timeout: null,
+          type: 'browser',
+          'url.port': null,
+          urls: '',
+          id: `${journeyId}-${project}-default`,
+          hash: 'ekrjelkjrelkjre',
+          max_attempts: 2,
+          updated_at: decryptedCreatedMonitor.rawBody.updated_at,
+          created_at: decryptedCreatedMonitor.rawBody.created_at,
+          labels: {},
+        });
+      }
+    });
+
+    it('project monitors - allows throttling false for browser monitors', async () => {
+      const successfulMonitors = [projectMonitors.monitors[0]];
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            ...projectMonitors,
+            monitors: [{ ...projectMonitors.monitors[0], throttling: false }],
+          })
+          .expect(200);
+        expect(body).eql({
+          updatedMonitors: [],
+          createdMonitors: successfulMonitors.map((monitor) => monitor.id),
+          failedMonitors: [],
+        });
+
+        for (const monitor of successfulMonitors) {
+          const journeyId = monitor.id;
+          const createdMonitorsResponse = await supertest
+            .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+            .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+
+          const decryptedCreatedMonitor = await monitorTestService.getMonitor(
+            createdMonitorsResponse.body.monitors[0].config_id,
+            { user: editorUser }
+          );
+
+          expect(decryptedCreatedMonitor.body.throttling).to.eql({
+            value: null,
+            id: 'no-throttling',
+            label: 'No throttling',
+          });
+        }
+      } finally {
+        await Promise.all([
+          successfulMonitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - handles http monitors', async () => {
+      const kibanaVersion = await kibanaServer.version.get();
+      const successfulMonitors = [httpProjectMonitors.monitors[1]];
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(httpProjectMonitors)
+          .expect(200);
+
+        expect(body).eql({
+          updatedMonitors: [],
+          createdMonitors: successfulMonitors.map((monitor) => monitor.id),
+          failedMonitors: [
+            {
+              id: httpProjectMonitors.monitors[0].id,
+              details: `\`http\` project monitors must have exactly one value for field \`urls\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
+              reason: 'Invalid Heartbeat configuration',
+            },
+            {
+              id: httpProjectMonitors.monitors[0].id,
+              details: `The following Heartbeat options are not supported for ${httpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: check.response.body|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
+              reason: 'Unsupported Heartbeat option',
+            },
+          ],
+        });
+
+        for (const monitor of successfulMonitors) {
+          const journeyId = monitor.id;
+          const isTLSEnabled = Object.keys(monitor).some((key) => key.includes('ssl'));
+          const createdMonitorsResponse = await supertest
+            .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+            .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+
+          const { rawBody: decryptedCreatedMonitor } = await monitorTestService.getMonitor(
+            createdMonitorsResponse.body.monitors[0].config_id,
+            {
+              internal: true,
+              user: editorUser,
+            }
+          );
+
+          expect(decryptedCreatedMonitor).to.eql({
+            __ui: {
+              is_tls_enabled: isTLSEnabled,
+            },
+            'check.request.method': 'POST',
+            'check.response.status': ['200'],
+            config_id: decryptedCreatedMonitor.config_id,
+            custom_heartbeat_id: `${journeyId}-${project}-default`,
+            'check.response.body.negative': [],
+            'check.response.body.positive': ['${testLocal1}', 'saved'],
+            'check.response.json': [
+              { description: 'check status', expression: 'foo.bar == "myValue"' },
+            ],
+            'check.response.headers': {},
+            proxy_url: '${testGlobalParam2}',
+            'check.request.body': {
+              type: 'text',
+              value: '',
+            },
+            params: JSON.stringify({
+              testLocal1: 'testLocalParamsValue',
+              testGlobalParam2: 'testGlobalParamOverwrite',
+            }),
+            'check.request.headers': {
+              'Content-Type': 'application/x-www-form-urlencoded',
+            },
+            enabled: false,
+            alert: {
+              status: {
+                enabled: true,
+              },
+              tls: {
+                enabled: true,
+              },
+            },
+            form_monitor_type: 'http',
+            journey_id: journeyId,
+            locations: [
+              {
+                geo: {
+                  lat: 0,
+                  lon: 0,
+                },
+                id: testPolicyId1,
+                agentPolicyId: testPolicyId1,
+                isServiceManaged: false,
+                label: privateLocations[0].label,
+              },
+            ],
+            max_redirects: '0',
+            name: monitor.name,
+            namespace: 'default',
+            origin: 'project',
+            original_space: 'default',
+            project_id: project,
+            username: '',
+            password: '',
+            proxy_headers: {},
+            'response.include_body': 'always',
+            'response.include_headers': false,
+            'response.include_body_max_bytes': '900',
+            revision: 1,
+            schedule: {
+              number: '60',
+              unit: 'm',
+            },
+            'service.name': '',
+            'ssl.certificate': '',
+            'ssl.certificate_authorities': '',
+            'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
+            'ssl.verification_mode': isTLSEnabled ? 'strict' : 'full',
+            'ssl.key': '',
+            'ssl.key_passphrase': '',
+            tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
+            timeout: '80',
+            type: 'http',
+            urls: Array.isArray(monitor.urls) ? monitor.urls?.[0] : monitor.urls,
+            'url.port': null,
+            id: `${journeyId}-${project}-default`,
+            hash: 'ekrjelkjrelkjre',
+            mode: 'any',
+            ipv6: true,
+            ipv4: true,
+            max_attempts: 2,
+            labels: {},
+            updated_at: decryptedCreatedMonitor.updated_at,
+            created_at: decryptedCreatedMonitor.created_at,
+          });
+        }
+      } finally {
+        await Promise.all([
+          successfulMonitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - handles tcp monitors', async () => {
+      const successfulMonitors = [tcpProjectMonitors.monitors[0], tcpProjectMonitors.monitors[1]];
+      const kibanaVersion = await kibanaServer.version.get();
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(tcpProjectMonitors)
+          .expect(200);
+
+        expect(body).eql({
+          updatedMonitors: [],
+          createdMonitors: successfulMonitors.map((monitor) => monitor.id),
+          failedMonitors: [
+            {
+              id: tcpProjectMonitors.monitors[2].id,
+              details: `\`tcp\` project monitors must have exactly one value for field \`hosts\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
+              reason: 'Invalid Heartbeat configuration',
+            },
+            {
+              id: tcpProjectMonitors.monitors[2].id,
+              details: `The following Heartbeat options are not supported for ${tcpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: ports|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
+              reason: 'Unsupported Heartbeat option',
+            },
+          ],
+        });
+
+        for (const monitor of successfulMonitors) {
+          const journeyId = monitor.id;
+          const isTLSEnabled = Object.keys(monitor).some((key) => key.includes('ssl'));
+          const createdMonitorsResponse = await supertest
+            .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+            .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+
+          const { rawBody: decryptedCreatedMonitor } = await monitorTestService.getMonitor(
+            createdMonitorsResponse.body.monitors[0].config_id,
+            {
+              internal: true,
+              user: editorUser,
+            }
+          );
+
+          expect(decryptedCreatedMonitor).to.eql({
+            __ui: {
+              is_tls_enabled: isTLSEnabled,
+            },
+            config_id: decryptedCreatedMonitor.config_id,
+            custom_heartbeat_id: `${journeyId}-${project}-default`,
+            'check.receive': '',
+            'check.send': '',
+            enabled: true,
+            alert: {
+              status: {
+                enabled: true,
+              },
+              tls: {
+                enabled: true,
+              },
+            },
+            form_monitor_type: 'tcp',
+            journey_id: journeyId,
+            locations: [
+              {
+                geo: {
+                  lat: 0,
+                  lon: 0,
+                },
+                id: testPolicyId1,
+                agentPolicyId: testPolicyId1,
+                isServiceManaged: false,
+                label: privateLocations[0].label,
+              },
+            ],
+            name: monitor.name,
+            namespace: 'default',
+            origin: 'project',
+            original_space: 'default',
+            project_id: project,
+            revision: 1,
+            schedule: {
+              number: '1',
+              unit: 'm',
+            },
+            proxy_url: '',
+            proxy_use_local_resolver: false,
+            'service.name': '',
+            'ssl.certificate': '',
+            'ssl.certificate_authorities': '',
+            'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
+            'ssl.verification_mode': isTLSEnabled ? 'strict' : 'full',
+            'ssl.key': '',
+            'ssl.key_passphrase': '',
+            tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
+            timeout: '16',
+            type: 'tcp',
+            hosts: Array.isArray(monitor.hosts) ? monitor.hosts?.[0] : monitor.hosts,
+            'url.port': null,
+            urls: '',
+            id: `${journeyId}-${project}-default`,
+            hash: 'ekrjelkjrelkjre',
+            mode: 'any',
+            ipv6: true,
+            ipv4: true,
+            params: '',
+            max_attempts: 2,
+            labels: {},
+            updated_at: decryptedCreatedMonitor.updated_at,
+            created_at: decryptedCreatedMonitor.created_at,
+          });
+        }
+      } finally {
+        await Promise.all([
+          successfulMonitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - handles icmp monitors', async () => {
+      const successfulMonitors = [icmpProjectMonitors.monitors[0], icmpProjectMonitors.monitors[1]];
+      const kibanaVersion = await kibanaServer.version.get();
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(icmpProjectMonitors)
+          .expect(200);
+        expect(body).eql({
+          updatedMonitors: [],
+          createdMonitors: successfulMonitors.map((monitor) => monitor.id),
+          failedMonitors: [
+            {
+              id: icmpProjectMonitors.monitors[2].id,
+              details: `\`icmp\` project monitors must have exactly one value for field \`hosts\` in version \`${kibanaVersion}\`. Your monitor was not created or updated.`,
+              reason: 'Invalid Heartbeat configuration',
+            },
+            {
+              id: icmpProjectMonitors.monitors[2].id,
+              details: `The following Heartbeat options are not supported for ${icmpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`,
+              reason: 'Unsupported Heartbeat option',
+            },
+          ],
+        });
+
+        for (const monitor of successfulMonitors) {
+          const journeyId = monitor.id;
+          const createdMonitorsResponse = await supertest
+            .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+            .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` })
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+
+          const { rawBody: decryptedCreatedMonitor } = await monitorTestService.getMonitor(
+            createdMonitorsResponse.body.monitors[0].config_id,
+            {
+              internal: true,
+              user: editorUser,
+            }
+          );
+
+          expect(decryptedCreatedMonitor).to.eql({
+            config_id: decryptedCreatedMonitor.config_id,
+            custom_heartbeat_id: `${journeyId}-${project}-default`,
+            enabled: true,
+            alert: {
+              status: {
+                enabled: true,
+              },
+              tls: {
+                enabled: true,
+              },
+            },
+            form_monitor_type: 'icmp',
+            journey_id: journeyId,
+            locations: [
+              {
+                geo: {
+                  lat: 0,
+                  lon: 0,
+                },
+                id: testPolicyId1,
+                agentPolicyId: testPolicyId1,
+                isServiceManaged: false,
+                label: privateLocations[0].label,
+              },
+            ],
+            name: monitor.name,
+            namespace: 'default',
+            origin: 'project',
+            original_space: 'default',
+            project_id: project,
+            revision: 1,
+            schedule: {
+              number: '1',
+              unit: 'm',
+            },
+            'service.name': '',
+            tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','),
+            timeout: '16',
+            type: 'icmp',
+            hosts: Array.isArray(monitor.hosts) ? monitor.hosts?.[0] : monitor.hosts,
+            wait:
+              monitor.wait?.slice(-1) === 's'
+                ? monitor.wait?.slice(0, -1)
+                : `${parseInt(monitor.wait?.slice(0, -1) || '1', 10) * 60}`,
+            id: `${journeyId}-${project}-default`,
+            hash: 'ekrjelkjrelkjre',
+            mode: 'any',
+            ipv4: true,
+            ipv6: true,
+            params: '',
+            max_attempts: 2,
+            updated_at: decryptedCreatedMonitor.updated_at,
+            created_at: decryptedCreatedMonitor.created_at,
+            labels: {},
+          });
+        }
+      } finally {
+        await Promise.all([
+          successfulMonitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - returns a list of successfully created monitors', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+
+        expect(body).eql({
+          updatedMonitors: [],
+          failedMonitors: [],
+          createdMonitors: projectMonitors.monitors.map((monitor) => monitor.id),
+        });
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - returns a list of successfully updated monitors', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+
+        expect(body).eql({
+          createdMonitors: [],
+          failedMonitors: [],
+          updatedMonitors: projectMonitors.monitors.map((monitor) => monitor.id),
+        });
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - validates monitor type', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: [{ ...projectMonitors.monitors[0], schedule: '3m', tags: '' }] })
+          .expect(200);
+
+        expect(body).eql({
+          updatedMonitors: [],
+          failedMonitors: [
+            {
+              details: 'Invalid value "3m" supplied to "schedule"',
+              id: projectMonitors.monitors[0].id,
+              payload: {
+                content:
+                  'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
+                filter: {
+                  match: 'check if title is present',
+                },
+                id: projectMonitors.monitors[0].id,
+                locations: [],
+                privateLocations: [privateLocations[0].label],
+                name: 'check if title is present',
+                params: {},
+                playwrightOptions: {
+                  chromiumSandbox: false,
+                  headless: true,
+                },
+                schedule: '3m',
+                tags: '',
+                throttling: {
+                  download: 5,
+                  latency: 20,
+                  upload: 3,
+                },
+                type: 'browser',
+                hash: 'ekrjelkjrelkjre',
+                max_attempts: 2,
+              },
+              reason: "Couldn't save or update monitor because of an invalid configuration.",
+            },
+          ],
+          createdMonitors: [],
+        });
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - saves space as data stream namespace', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      try {
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                privateLocations: [spaceScopedPrivateLocation.label],
+              },
+            ],
+          })
+          .expect(200);
+        // expect monitor not to have been deleted
+        const getResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(monitors[0][ConfigKey.NAMESPACE]).eql(formatKibanaNamespace(SPACE_ID));
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project, SPACE_ID);
+      }
+    });
+
+    it('project monitors - browser - handles custom namespace', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const customNamespace = 'custom.namespace';
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      try {
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                namespace: customNamespace,
+                privateLocations: [spaceScopedPrivateLocation.label],
+              },
+            ],
+          })
+          .expect(200);
+        // expect monitor not to have been deleted
+        const getResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .expect(200);
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(monitors[0][ConfigKey.NAMESPACE]).eql(customNamespace);
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project, SPACE_ID);
+      }
+    });
+
+    it('project monitors - lightweight - handles custom namespace', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const customNamespace = 'custom.namespace';
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      try {
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                namespace: customNamespace,
+                privateLocations: [spaceScopedPrivateLocation.label],
+              },
+            ],
+          })
+          .expect(200);
+
+        // expect monitor not to have been deleted
+        const getResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${httpProjectMonitors.monitors[1].id}`,
+          })
+          .set('kbn-xsrf', 'true')
+          .expect(200);
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(monitors[0][ConfigKey.NAMESPACE]).eql(customNamespace);
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project, SPACE_ID);
+      }
+    });
+
+    it('project monitors - browser - handles custom namespace errors', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const customNamespace = 'custom-namespace';
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      const { body } = await supertest
+        .put(
+          `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+            '{projectName}',
+            project
+          )}`
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          monitors: [
+            {
+              ...projectMonitors.monitors[0],
+              namespace: customNamespace,
+              privateLocations: [spaceScopedPrivateLocation.label],
+            },
+          ],
+        })
+        .expect(200);
+      // expect monitor not to have been deleted
+      expect(body).to.eql({
+        createdMonitors: [],
+        failedMonitors: [
+          {
+            details: 'Namespace contains invalid characters',
+            id: projectMonitors.monitors[0].id,
+            reason: 'Invalid namespace',
+          },
+        ],
+        updatedMonitors: [],
+      });
+    });
+
+    it('project monitors - lightweight - handles custom namespace errors', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const customNamespace = 'custom-namespace';
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      const { body } = await supertest
+        .put(
+          `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+            '{projectName}',
+            project
+          )}`
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          monitors: [
+            {
+              ...httpProjectMonitors.monitors[1],
+              namespace: customNamespace,
+              privateLocations: [spaceScopedPrivateLocation.label],
+            },
+          ],
+        })
+        .expect(200);
+      // expect monitor not to have been deleted
+      expect(body).to.eql({
+        createdMonitors: [],
+        failedMonitors: [
+          {
+            details: 'Namespace contains invalid characters',
+            id: httpProjectMonitors.monitors[1].id,
+            reason: 'Invalid namespace',
+          },
+        ],
+        updatedMonitors: [],
+      });
+    });
+
+    it('project monitors - handles editing with spaces', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      try {
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: projectMonitors.monitors.map((monitor) => ({
+              ...monitor,
+              privateLocations: [spaceScopedPrivateLocation.label],
+            })),
+          })
+          .expect(200);
+        // expect monitor not to have been deleted
+        const getResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set('kbn-xsrf', 'true')
+          .expect(200);
+
+        const decryptedCreatedMonitor = await monitorTestService.getMonitor(
+          getResponse.body.monitors[0].config_id,
+          { internal: true, space: SPACE_ID, user: editorUser }
+        );
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(decryptedCreatedMonitor.body[ConfigKey.SOURCE_PROJECT_CONTENT]).eql(
+          projectMonitors.monitors[0].content
+        );
+
+        const updatedSource = 'updatedSource';
+        // update monitor
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            ...projectMonitors,
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                content: updatedSource,
+                privateLocations: [spaceScopedPrivateLocation.label],
+              },
+            ],
+          })
+          .expect(200);
+        const getResponseUpdated = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .expect(200);
+        const { monitors: monitorsUpdated } = getResponseUpdated.body;
+        expect(monitorsUpdated.length).eql(1);
+
+        const decryptedUpdatedMonitor = await monitorTestService.getMonitor(
+          monitorsUpdated[0].config_id,
+          { internal: true, space: SPACE_ID, user: editorUser }
+        );
+        expect(decryptedUpdatedMonitor.body[ConfigKey.SOURCE_PROJECT_CONTENT]).eql(updatedSource);
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project, SPACE_ID);
+      }
+    });
+
+    it('project monitors - formats custom id appropriately', async () => {
+      const project = `test project ${uuidv4()}`;
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      try {
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                privateLocations: [spaceScopedPrivateLocation.label],
+              },
+            ],
+          })
+          .expect(200);
+        const getResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .expect(200);
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]).eql(
+          `${projectMonitors.monitors[0].id}-${project}-${SPACE_ID}`
+        );
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project, SPACE_ID);
+      }
+    });
+
+    it('project monitors - is able to decrypt monitor when updated after hydration', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+
+        const response = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const { monitors } = response.body;
+
+        // update project monitor via push api
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+
+        expect(body).eql({
+          updatedMonitors: [projectMonitors.monitors[0].id],
+          createdMonitors: [],
+          failedMonitors: [],
+        });
+
+        // ensure that monitor can still be decrypted
+        await monitorTestService.getMonitor(monitors[0]?.config_id, { user: editorUser });
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => deleteMonitor(monitor.id, project)),
+        ]);
+      }
+    });
+
+    it('project monitors - is able to enable and disable monitors', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            ...projectMonitors,
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                enabled: false,
+              },
+            ],
+          })
+          .expect(200);
+        const response = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { monitors } = response.body;
+        expect(monitors[0].enabled).eql(false);
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - cannot update project monitors with read only privileges', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      const secondMonitor = {
+        ...projectMonitors.monitors[0],
+        id: 'test-id-2',
+        privateLocations: [privateLocations[0].label],
+      };
+      const testMonitors = [projectMonitors.monitors[0], secondMonitor];
+      await supertest
+        .put(
+          SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+        )
+        .set(viewerUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ monitors: testMonitors })
+        .expect(403);
+    });
+
+    it('creates integration policies for project monitors with private locations', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            ...projectMonitors,
+            monitors: [
+              { ...projectMonitors.monitors[0], privateLocations: [privateLocations[0].label] },
+            ],
+          })
+          .expect(200);
+
+        const monitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+        expect(packagePolicy.name).eql(
+          `${projectMonitors.monitors[0].id}-${project}-default-${privateLocations[0].label}`
+        );
+        expect(packagePolicy.policy_id).eql(testPolicyId1);
+
+        const configId = monitorsResponse.body.monitors[0].config_id;
+        const id = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
+
+        comparePolicies(
+          packagePolicy,
+          getTestProjectSyntheticsPolicy({
+            inputs: {},
+            name: `check if title is present-${privateLocations[0].label}`,
+            id,
+            configId,
+            projectId: project,
+            locationId: testPolicyId1,
+            locationName: privateLocations[0].label,
+          })
+        );
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project);
+
+        const packagesResponse = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+        expect(packagesResponse.body.items.length).eql(0);
+      }
+    });
+
+    it('creates integration policies for project monitors with private locations - lightweight', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            ...httpProjectMonitors,
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                'check.request.body': '${testGlobalParam}',
+                privateLocations: [privateLocations[0].label],
+              },
+            ],
+          })
+          .expect(200);
+
+        const monitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${httpProjectMonitors.monitors[1].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+        expect(packagePolicy.name).eql(
+          `${httpProjectMonitors.monitors[1].id}-${project}-default-${privateLocations[0].label}`
+        );
+        expect(packagePolicy.policy_id).eql(testPolicyId1);
+
+        const configId = monitorsResponse.body.monitors[0].config_id;
+        const id = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
+
+        comparePolicies(
+          packagePolicy,
+          getTestProjectSyntheticsPolicyLightweight({
+            inputs: {},
+            name: 'My Monitor 3',
+            id,
+            configId,
+            projectId: project,
+            locationName: privateLocations[0].label,
+            locationId: testPolicyId1,
+          })
+        );
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project);
+
+        const packagesResponse = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+        expect(packagesResponse.body.items.length).eql(0);
+      }
+    });
+
+    it('deletes integration policies for project monitors when private location is removed from the monitor - lightweight', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      const monitorRequest = {
+        monitors: [
+          {
+            ...httpProjectMonitors.monitors[1],
+            privateLocations: [privateLocations[0].label, privateLocations[1].label],
+          },
+        ],
+      };
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(monitorRequest)
+          .expect(200);
+
+        const monitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitorRequest.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+
+        expect(packagePolicy.policy_id).eql(testPolicyId1);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              { ...monitorRequest.monitors[0], privateLocations: [privateLocations[1].label] },
+            ],
+          })
+          .expect(200);
+
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy2 = apiResponsePolicy2.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+
+        expect(packagePolicy2).eql(undefined);
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project);
+      }
+    });
+
+    it('deletes integration policies for project monitors when private location is removed from the monitor', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                privateLocations: [privateLocations[0].label, privateLocations[1].label],
+              },
+            ],
+          })
+          .expect(200);
+
+        const monitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+
+        expect(packagePolicy.policy_id).eql(testPolicyId1);
+
+        const configId = monitorsResponse.body.monitors[0].config_id;
+        const id = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
+
+        comparePolicies(
+          packagePolicy,
+          getTestProjectSyntheticsPolicy({
+            inputs: {},
+            name: `check if title is present-${privateLocations[0].label}`,
+            id,
+            configId,
+            projectId: project,
+            locationId: testPolicyId1,
+            locationName: privateLocations[0].label,
+          })
+        );
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              { ...projectMonitors.monitors[0], privateLocations: [privateLocations[1].label] },
+            ],
+          })
+          .expect(200);
+
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy2 = apiResponsePolicy2.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            `${monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID]}-${testPolicyId1}`
+        );
+
+        expect(packagePolicy2).eql(undefined);
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project);
+
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+        expect(apiResponsePolicy2.body.items.length).eql(0);
+      }
+    });
+
+    it('handles updating package policies when project monitors are updated', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                privateLocations: [privateLocations[0].label],
+              },
+            ],
+          });
+
+        const monitorsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const configId = monitorsResponse.body.monitors[0].id;
+        const id = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
+        const policyId = `${id}-${testPolicyId1}`;
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId
+        );
+
+        expect(packagePolicy.policy_id).eql(testPolicyId1);
+
+        comparePolicies(
+          packagePolicy,
+          getTestProjectSyntheticsPolicy({
+            inputs: {},
+            name: `check if title is present-${privateLocations[0].label}`,
+            id,
+            configId,
+            projectId: project,
+            locationId: testPolicyId1,
+            locationName: privateLocations[0].label,
+          })
+        );
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...projectMonitors.monitors[0],
+                namespace: 'custom_namespace',
+                privateLocations: [privateLocations[0].label],
+                enabled: false,
+              },
+            ],
+          });
+
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const configId2 = monitorsResponse.body.monitors[0].id;
+        const id2 = monitorsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID];
+        const policyId2 = `${id}-${testPolicyId1}`;
+
+        const packagePolicy2 = apiResponsePolicy2.body.items.find(
+          (pkgPolicy: PackagePolicy) => pkgPolicy.id === policyId2
+        );
+
+        comparePolicies(
+          packagePolicy2,
+          getTestProjectSyntheticsPolicy({
+            inputs: { enabled: { value: false, type: 'bool' } },
+            name: `check if title is present-${privateLocations[0].label}`,
+            id: id2,
+            configId: configId2,
+            projectId: project,
+            locationId: testPolicyId1,
+            locationName: privateLocations[0].label,
+            namespace: 'custom_namespace',
+          })
+        );
+      } finally {
+        await deleteMonitor(projectMonitors.monitors[0].id, project);
+
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+        expect(apiResponsePolicy2.body.items.length).eql(0);
+      }
+    });
+
+    // this test is skipped because it would fail in MKI due to public locations not being available
+    it.skip('handles location formatting for both private and public locations', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              { ...projectMonitors.monitors[0], privateLocations: [privateLocations[0].label] },
+            ],
+          });
+
+        const updatedMonitorsResponse = await Promise.all(
+          projectMonitors.monitors.map((monitor) => {
+            return supertest
+              .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+              .query({
+                filter: `${syntheticsMonitorType}.attributes.journey_id: ${monitor.id}`,
+                internal: true,
+              })
+              .set(editorUser.apiKeyHeader)
+              .set(samlAuth.getInternalRequestHeader())
+              .expect(200);
+          })
+        );
+
+        updatedMonitorsResponse.forEach(
+          (response: {
+            body: { monitors: Array<{ locations: Array<PrivateLocation | ServiceLocation> }> };
+          }) => {
+            expect(response.body.monitors[0].locations).eql([
+              {
+                id: 'dev',
+                label: 'Dev Service',
+                geo: { lat: 0, lon: 0 },
+                isServiceManaged: true,
+              },
+              {
+                label: privateLocations[0].label,
+                isServiceManaged: false,
+                agentPolicyId: testPolicyId1,
+                id: testPolicyId1,
+                geo: {
+                  lat: 0,
+                  lon: 0,
+                },
+              },
+            ]);
+          }
+        );
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('only allows 250 requests at a time', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const monitors = [];
+      for (let i = 0; i < 251; i++) {
+        monitors.push({
+          ...projectMonitors.monitors[0],
+          id: `test-id-${i}`,
+          name: `test-name-${i}`,
+        });
+      }
+
+      try {
+        const {
+          body: { message },
+        } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors,
+          })
+          .expect(400);
+
+        expect(message).to.eql(REQUEST_TOO_LARGE);
+      } finally {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ ...projectMonitors, project });
+      }
+    });
+
+    it('project monitors - cannot update a monitor of one type to another type', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send(projectMonitors)
+          .expect(200);
+        const { body } = await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [{ ...httpProjectMonitors.monitors[1], id: projectMonitors.monitors[0].id }],
+          })
+          .expect(200);
+        expect(body).eql({
+          createdMonitors: [],
+          updatedMonitors: [],
+          failedMonitors: [
+            {
+              details: `Monitor ${projectMonitors.monitors[0].id} of type browser cannot be updated to type http. Please delete the monitor first and try again.`,
+              payload: {
+                'check.request': {
+                  headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded',
+                  },
+                  method: 'POST',
+                },
+                'check.response': {
+                  body: {
+                    positive: ['${testLocal1}', 'saved'],
+                  },
+                  status: [200],
+                  json: [{ description: 'check status', expression: 'foo.bar == "myValue"' }],
+                },
+                enabled: false,
+                hash: 'ekrjelkjrelkjre',
+                id: projectMonitors.monitors[0].id,
+                locations: [],
+                privateLocations: [privateLocations[0].label],
+                name: 'My Monitor 3',
+                response: {
+                  include_body: 'always',
+                  include_body_max_bytes: 900,
+                },
+                'response.include_headers': false,
+                schedule: 60,
+                timeout: '80s',
+                type: 'http',
+                tags: 'tag2,tag2',
+                urls: ['http://localhost:9200'],
+                'ssl.verification_mode': 'strict',
+                params: {
+                  testGlobalParam2: 'testGlobalParamOverwrite',
+                  testLocal1: 'testLocalParamsValue',
+                },
+                proxy_url: '${testGlobalParam2}',
+                max_attempts: 2,
+              },
+              reason: 'Cannot update monitor to different type.',
+            },
+          ],
+        });
+      } finally {
+        await Promise.all([
+          projectMonitors.monitors.map((monitor) => {
+            return deleteMonitor(monitor.id, project);
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - handles alert config without adding arbitrary fields', async () => {
+      const project = `test-project-${uuidv4()}`;
+      const testAlert = {
+        status: {
+          enabled: false,
+          doesnotexit: true,
+          tls: {
+            enabled: true,
+          },
+        },
+      };
+      try {
+        await supertest
+          .put(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                alert: testAlert,
+              },
+            ],
+          })
+          .expect(200);
+        const getResponse = await supertest
+          .get(`${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.journey_id: ${httpProjectMonitors.monitors[1].id}`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { monitors } = getResponse.body;
+        expect(monitors.length).eql(1);
+        expect(monitors[0][ConfigKey.ALERT_CONFIG]).eql({
+          status: {
+            enabled: testAlert.status.enabled,
+          },
+          tls: {
+            enabled: true,
+          },
+        });
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project);
+      }
+    });
+
+    it('project monitors - handles sending invalid public location', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        const response = await supertest
+          .put(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                locations: ['does not exist'],
+              },
+            ],
+          })
+          .expect(200);
+        rawExpect(response.body).toEqual({
+          createdMonitors: [],
+          failedMonitors: [
+            {
+              details: rawExpect.stringContaining(
+                "Invalid locations specified. Elastic managed Location(s) 'does not exist' not found."
+              ),
+              id: httpProjectMonitors.monitors[1].id,
+              payload: {
+                'check.request': {
+                  headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded',
+                  },
+                  method: 'POST',
+                },
+                'check.response': {
+                  body: {
+                    positive: ['${testLocal1}', 'saved'],
+                  },
+                  status: [200],
+                  json: [
+                    {
+                      description: 'check status',
+                      expression: 'foo.bar == "myValue"',
+                    },
+                  ],
+                },
+                enabled: false,
+                hash: 'ekrjelkjrelkjre',
+                id: httpProjectMonitors.monitors[1].id,
+                locations: ['does not exist'],
+                privateLocations: [privateLocations[0].label],
+                name: 'My Monitor 3',
+                response: {
+                  include_body: 'always',
+                  include_body_max_bytes: 900,
+                },
+                'response.include_headers': false,
+                schedule: 60,
+                'ssl.verification_mode': 'strict',
+                tags: 'tag2,tag2',
+                timeout: '80s',
+                type: 'http',
+                urls: ['http://localhost:9200'],
+                params: {
+                  testGlobalParam2: 'testGlobalParamOverwrite',
+                  testLocal1: 'testLocalParamsValue',
+                },
+                proxy_url: '${testGlobalParam2}',
+                max_attempts: 2,
+              },
+              reason: "Couldn't save or update monitor because of an invalid configuration.",
+            },
+          ],
+          updatedMonitors: [],
+        });
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project);
+      }
+    });
+
+    it('project monitors - handles sending invalid private locations', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        const response = await supertest
+          .put(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                locations: [],
+                privateLocations: ['does not exist'],
+              },
+            ],
+          })
+          .expect(200);
+        expect(response.body).eql({
+          createdMonitors: [],
+          failedMonitors: [
+            {
+              details: `Invalid locations specified. Private Location(s) 'does not exist' not found. Available private locations are '${privateLocations[0].label}|${privateLocations[1].label}'`,
+              id: httpProjectMonitors.monitors[1].id,
+              payload: {
+                'check.request': {
+                  headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded',
+                  },
+                  method: 'POST',
+                },
+                'check.response': {
+                  body: {
+                    positive: ['${testLocal1}', 'saved'],
+                  },
+                  status: [200],
+                  json: [
+                    {
+                      description: 'check status',
+                      expression: 'foo.bar == "myValue"',
+                    },
+                  ],
+                },
+                enabled: false,
+                hash: 'ekrjelkjrelkjre',
+                id: httpProjectMonitors.monitors[1].id,
+                privateLocations: ['does not exist'],
+                name: 'My Monitor 3',
+                response: {
+                  include_body: 'always',
+                  include_body_max_bytes: 900,
+                },
+                'response.include_headers': false,
+                schedule: 60,
+                'ssl.verification_mode': 'strict',
+                tags: 'tag2,tag2',
+                timeout: '80s',
+                type: 'http',
+                urls: ['http://localhost:9200'],
+                locations: [],
+                params: {
+                  testGlobalParam2: 'testGlobalParamOverwrite',
+                  testLocal1: 'testLocalParamsValue',
+                },
+                proxy_url: '${testGlobalParam2}',
+                max_attempts: 2,
+              },
+              reason: "Couldn't save or update monitor because of an invalid configuration.",
+            },
+          ],
+          updatedMonitors: [],
+        });
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project);
+      }
+    });
+
+    it('project monitors - handles no locations specified', async () => {
+      const project = `test-project-${uuidv4()}`;
+      try {
+        const response = await supertest
+          .put(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: [
+              {
+                ...httpProjectMonitors.monitors[1],
+                privateLocations: [],
+                locations: [],
+              },
+            ],
+          })
+          .expect(200);
+        expect(response.body).eql({
+          createdMonitors: [],
+          failedMonitors: [
+            {
+              details: 'You must add at least one location or private location to this monitor.',
+              id: httpProjectMonitors.monitors[1].id,
+              payload: {
+                'check.request': {
+                  headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded',
+                  },
+                  method: 'POST',
+                },
+                'check.response': {
+                  body: {
+                    positive: ['${testLocal1}', 'saved'],
+                  },
+                  status: [200],
+                  json: [
+                    {
+                      description: 'check status',
+                      expression: 'foo.bar == "myValue"',
+                    },
+                  ],
+                },
+                enabled: false,
+                hash: 'ekrjelkjrelkjre',
+                id: httpProjectMonitors.monitors[1].id,
+                privateLocations: [],
+                name: 'My Monitor 3',
+                response: {
+                  include_body: 'always',
+                  include_body_max_bytes: 900,
+                },
+                'response.include_headers': false,
+                schedule: 60,
+                'ssl.verification_mode': 'strict',
+                tags: 'tag2,tag2',
+                timeout: '80s',
+                type: 'http',
+                urls: ['http://localhost:9200'],
+                locations: [],
+                params: {
+                  testGlobalParam2: 'testGlobalParamOverwrite',
+                  testLocal1: 'testLocalParamsValue',
+                },
+                proxy_url: '${testGlobalParam2}',
+                max_attempts: 2,
+              },
+              reason: "Couldn't save or update monitor because of an invalid configuration.",
+            },
+          ],
+          updatedMonitors: [],
+        });
+      } finally {
+        await deleteMonitor(httpProjectMonitors.monitors[1].id, project);
+      }
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project_private_location.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project_private_location.ts
new file mode 100644
index 0000000000000..a8f98dac2bf61
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_project_private_location.ts
@@ -0,0 +1,162 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import expect from '@kbn/expect';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { ProjectMonitorsRequest } from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('AddProjectMonitorsPrivateLocations', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    let projectMonitors: ProjectMonitorsRequest;
+    let editorUser: RoleCredentials;
+
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+
+    let testPolicyId;
+    let testPrivateLocationName: string;
+    const testPolicyName = `Fleet test server policy ${uuidv4()}`;
+    const testPrivateLocationsService = new PrivateLocationTestService(getService);
+
+    const setUniqueIds = (request: ProjectMonitorsRequest) => {
+      return {
+        ...request,
+        monitors: request.monitors.map((monitor) => ({ ...monitor, id: uuidv4() })),
+      };
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      await testPrivateLocationsService.installSyntheticsPackage();
+
+      const apiResponse = await testPrivateLocationsService.addFleetPolicy(testPolicyName);
+      testPolicyId = apiResponse.body.item.id;
+      const testPrivateLocations = await testPrivateLocationsService.setTestLocations([
+        testPolicyId,
+      ]);
+      testPrivateLocationName = testPrivateLocations[0].label;
+    });
+
+    after(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+    });
+
+    beforeEach(() => {
+      projectMonitors = setUniqueIds({
+        monitors: getFixtureJson('project_browser_monitor').monitors.map(
+          (monitor: Record<string, unknown>) => {
+            return {
+              ...monitor,
+              name: `test-monitor-${uuidv4()}`,
+              type: 'browser',
+              locations: [],
+              privateLocations: [testPrivateLocationName],
+            };
+          }
+        ),
+      });
+    });
+
+    it('project monitors - returns a failed monitor when creating integration fails', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      const secondMonitor = {
+        ...projectMonitors.monitors[0],
+        id: 'test-id-2',
+        privateLocations: ['Test private location 7'],
+      };
+      const testMonitors = [
+        projectMonitors.monitors[0],
+        {
+          ...secondMonitor,
+          name: '!@#$%^&*()_++[\\-\\]- wow name',
+        },
+      ];
+      try {
+        const { body, status } = await monitorTestService.addProjectMonitors(
+          project,
+          testMonitors,
+          editorUser
+        );
+        expect(status).eql(200);
+        expect(body.createdMonitors.length).eql(1);
+        expect(body.failedMonitors[0].reason).eql(
+          "Couldn't save or update monitor because of an invalid configuration."
+        );
+      } finally {
+        await Promise.all([
+          testMonitors.map((monitor) => {
+            return monitorTestService.deleteMonitorByJourney(
+              monitor.id,
+              project,
+              'default',
+              editorUser
+            );
+          }),
+        ]);
+      }
+    });
+
+    it('project monitors - returns a failed monitor when editing integration fails', async () => {
+      const project = `test-project-${uuidv4()}`;
+
+      const secondMonitor = {
+        ...projectMonitors.monitors[0],
+        id: 'test-id-2',
+        privateLocations: [testPrivateLocationName],
+      };
+      const testMonitors = [projectMonitors.monitors[0], secondMonitor];
+      const { body, status: status0 } = await monitorTestService.addProjectMonitors(
+        project,
+        testMonitors,
+        editorUser
+      );
+      expect(status0).eql(200);
+
+      expect(body.createdMonitors.length).eql(2);
+      const { body: editedBody, status: editedStatus } =
+        await monitorTestService.addProjectMonitors(project, testMonitors, editorUser);
+      expect(editedStatus).eql(200);
+
+      expect(editedBody.createdMonitors.length).eql(0);
+      expect(editedBody.updatedMonitors.length).eql(2);
+
+      testMonitors[1].name = '!@#$%^&*()_++[\\-\\]- wow name';
+      testMonitors[1].privateLocations = ['Test private location 8'];
+
+      const { body: editedBodyError, status } = await monitorTestService.addProjectMonitors(
+        project,
+        testMonitors,
+        editorUser
+      );
+      expect(status).eql(200);
+      expect(editedBodyError.createdMonitors.length).eql(0);
+      expect(editedBodyError.updatedMonitors.length).eql(1);
+      expect(editedBodyError.failedMonitors.length).eql(1);
+      expect(editedBodyError.failedMonitors[0].details).eql(
+        `Invalid locations specified. Private Location(s) 'Test private location 8' not found. Available private locations are '${testPrivateLocationName}'`
+      );
+      expect(editedBodyError.failedMonitors[0].reason).eql(
+        "Couldn't save or update monitor because of an invalid configuration."
+      );
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_public_api.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_public_api.ts
new file mode 100644
index 0000000000000..2c41d5c58f298
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_monitor_public_api.ts
@@ -0,0 +1,280 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import expect from '@kbn/expect';
+import rawExpect from 'expect';
+import { v4 as uuidv4 } from 'uuid';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { PrivateLocation } from '@kbn/synthetics-plugin/common/runtime_types';
+import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
+import { LOCATION_REQUIRED_ERROR } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/monitor_validation';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { addMonitorAPIHelper, omitMonitorKeys } from './create_monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('AddNewMonitorsPublicAPI', function () {
+    const supertestAPI = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+    const privateLocationTestService = new PrivateLocationTestService(getService);
+
+    async function addMonitorAPI(monitor: any, statusCode: number = 200) {
+      return await addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorUser, samlAuth);
+    }
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      privateLocation = await privateLocationTestService.addTestPrivateLocation();
+    });
+
+    after(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+    });
+
+    it('should return error for empty monitor', async function () {
+      const { message } = await addMonitorAPI({}, 400);
+      expect(message).eql('Invalid value "undefined" supplied to "type"');
+    });
+
+    it('return error if no location specified', async () => {
+      const { message } = await addMonitorAPI({ type: 'http' }, 400);
+      expect(message).eql(LOCATION_REQUIRED_ERROR);
+    });
+
+    it('return error if invalid location specified', async () => {
+      const { message } = await addMonitorAPI({ type: 'http', locations: ['mars'] }, 400);
+      rawExpect(message).toContain(
+        "Invalid locations specified. Elastic managed Location(s) 'mars' not found."
+      );
+    });
+
+    it('return error if invalid private location specified', async () => {
+      const { message } = await addMonitorAPI(
+        {
+          type: 'http',
+          locations: ['mars'],
+          privateLocations: ['moon'],
+        },
+        400
+      );
+      expect(message).eql('Invalid monitor key(s) for http type:  privateLocations');
+
+      const result = await addMonitorAPI(
+        {
+          type: 'http',
+          locations: ['mars'],
+          private_locations: ['moon'],
+        },
+        400
+      );
+      rawExpect(result.message).toContain("Private Location(s) 'moon' not found.");
+    });
+
+    it('return error for origin project', async () => {
+      const { message } = await addMonitorAPI(
+        {
+          type: 'http',
+          locations: ['dev'],
+          url: 'https://www.google.com',
+          origin: 'project',
+        },
+        400
+      );
+      expect(message).eql('Unsupported origin type project, only ui type is supported via API.');
+    });
+
+    describe('HTTP Monitor', () => {
+      const defaultFields = DEFAULT_FIELDS.http;
+      it('return error empty http', async () => {
+        const { message, attributes } = await addMonitorAPI(
+          {
+            type: 'http',
+            locations: [],
+            private_locations: [privateLocation.id],
+          },
+          400
+        );
+
+        expect(message).eql('Monitor is not a valid monitor of type http');
+        expect(attributes).eql({
+          details:
+            'Invalid field "url", must be a non-empty string. | Invalid value "undefined" supplied to "name"',
+          payload: { type: 'http' },
+        });
+      });
+
+      it('base http monitor', async () => {
+        const monitor = {
+          type: 'http',
+          private_locations: [privateLocation.id],
+          url: 'https://www.google.com',
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+            name: 'https://www.google.com',
+          })
+        );
+      });
+
+      it('can enable retries', async () => {
+        const name = `test name ${uuidv4()}`;
+        const monitor = {
+          type: 'http',
+          private_locations: [privateLocation.id],
+          url: 'https://www.google.com',
+          name,
+          retest_on_failure: true,
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+            name,
+            retest_on_failure: true,
+          })
+        );
+      });
+
+      it('can disable retries', async () => {
+        const name = `test name ${uuidv4()}`;
+        const monitor = {
+          type: 'http',
+          private_locations: [privateLocation.id],
+          url: 'https://www.google.com',
+          name,
+          retest_on_failure: false,
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+            name,
+            max_attempts: 1,
+            retest_on_failure: undefined, // this key is not part of the SO and should not be defined
+          })
+        );
+      });
+    });
+
+    describe('TCP Monitor', () => {
+      const defaultFields = DEFAULT_FIELDS.tcp;
+
+      it('base tcp monitor', async () => {
+        const monitor = {
+          type: 'tcp',
+          private_locations: [privateLocation.id],
+          host: 'https://www.google.com/',
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+            name: 'https://www.google.com/',
+          })
+        );
+      });
+    });
+
+    describe('ICMP Monitor', () => {
+      const defaultFields = DEFAULT_FIELDS.icmp;
+
+      it('base icmp monitor', async () => {
+        const monitor = {
+          type: 'icmp',
+          private_locations: [privateLocation.id],
+          host: 'https://8.8.8.8',
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+            name: 'https://8.8.8.8',
+          })
+        );
+      });
+    });
+
+    describe('Browser Monitor', () => {
+      const defaultFields = DEFAULT_FIELDS.browser;
+
+      it('empty browser monitor', async () => {
+        const monitor = {
+          type: 'browser',
+          private_locations: [privateLocation.id],
+          name: 'simple journey',
+        };
+        const result = await addMonitorAPI(monitor, 400);
+
+        expect(result).eql({
+          statusCode: 400,
+          error: 'Bad Request',
+          message: 'Monitor is not a valid monitor of type browser',
+          attributes: {
+            details: 'source.inline.script: Script is required for browser monitor.',
+            payload: { type: 'browser', name: 'simple journey' },
+          },
+        });
+      });
+
+      it('base browser monitor', async () => {
+        const monitor = {
+          type: 'browser',
+          private_locations: [privateLocation.id],
+          name: 'simple journey',
+          'source.inline.script': 'step("simple journey", async () => {});',
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+          })
+        );
+      });
+
+      it('base browser monitor with inline_script', async () => {
+        const monitor = {
+          type: 'browser',
+          private_locations: [privateLocation.id],
+          name: 'simple journey inline_script',
+          inline_script: 'step("simple journey", async () => {});',
+        };
+        const { body: result } = await addMonitorAPI(monitor);
+
+        expect(result).eql(
+          omitMonitorKeys({
+            ...defaultFields,
+            ...monitor,
+            locations: [privateLocation],
+          })
+        );
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_update_params.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_update_params.ts
new file mode 100644
index 0000000000000..4f4068008cd40
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/create_update_params.ts
@@ -0,0 +1,385 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { v4 as uuidv4 } from 'uuid';
+import { pick } from 'lodash';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { syntheticsParamType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+function assertHas(actual: unknown, expected: object) {
+  expect(pick(actual, Object.keys(expected))).eql(expected);
+}
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe.skip('AddEditParams', function () {
+    const samlAuth = getService('samlAuth');
+    const supertest = getService('supertestWithoutAuth');
+    let adminRoleAuthc: RoleCredentials;
+
+    const kServer = getService('kibanaServer');
+    const testParam = {
+      key: 'test',
+      value: 'test',
+    };
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+
+    before(async () => {
+      await testPrivateLocations.installSyntheticsPackage();
+      adminRoleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
+      await kServer.savedObjects.clean({ types: [syntheticsParamType] });
+    });
+
+    it('adds a test param', async () => {
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      assertHas(getResponse.body[0], testParam);
+    });
+
+    it('handles tags and description', async () => {
+      const tagsAndDescription = {
+        tags: ['a tag'],
+        description: 'test description',
+      };
+      const testParam2 = {
+        ...testParam,
+        ...tagsAndDescription,
+      };
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .send(testParam2)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      assertHas(getResponse.body[0], testParam2);
+    });
+
+    it('handles editing a param', async () => {
+      const expectedUpdatedParam = {
+        key: 'testUpdated',
+        value: 'testUpdated',
+        tags: ['a tag'],
+        description: 'test description',
+      };
+
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const param = getResponse.body[0];
+      assertHas(param, testParam);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.PARAMS + '/' + param.id)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({})
+        .expect(400);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.PARAMS + '/' + param.id)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const updatedGetResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const actualUpdatedParam = updatedGetResponse.body[0];
+      assertHas(actualUpdatedParam, expectedUpdatedParam);
+    });
+
+    it('handles partial editing a param', async () => {
+      const newParam = {
+        key: 'testUpdated',
+        value: 'testUpdated',
+        tags: ['a tag'],
+        description: 'test description',
+      };
+
+      const response = await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(newParam)
+        .expect(200);
+      const paramId = response.body.id;
+
+      const getResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS + '/' + paramId)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      assertHas(getResponse.body, newParam);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.PARAMS + '/' + paramId)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          key: 'testUpdated',
+        })
+        .expect(200);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.PARAMS + '/' + paramId)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          key: 'testUpdatedAgain',
+          value: 'testUpdatedAgain',
+        })
+        .expect(200);
+
+      const updatedGetResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS + '/' + paramId)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      assertHas(updatedGetResponse.body, {
+        ...newParam,
+        key: 'testUpdatedAgain',
+        value: 'testUpdatedAgain',
+      });
+    });
+
+    it('handles spaces', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+      await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(getResponse.body[0].namespaces).eql([SPACE_ID]);
+      assertHas(getResponse.body[0], testParam);
+    });
+
+    it('handles editing a param in spaces', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+      await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      const expectedUpdatedParam = {
+        key: 'testUpdated',
+        value: 'testUpdated',
+        tags: ['a tag'],
+        description: 'test description',
+      };
+
+      await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const param = getResponse.body[0];
+      assertHas(param, testParam);
+
+      await supertest
+        .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}/${param.id}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(expectedUpdatedParam)
+        .expect(200);
+
+      const updatedGetResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const actualUpdatedParam = updatedGetResponse.body[0];
+      assertHas(actualUpdatedParam, expectedUpdatedParam);
+    });
+
+    it('does not allow editing a param in created in one space in a different space', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      const SPACE_ID_TWO = `test-space-${uuidv4()}-two`;
+      const SPACE_NAME_TWO = `test-space-name ${uuidv4()} 2`;
+
+      await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      await kServer.spaces.create({ id: SPACE_ID_TWO, name: SPACE_NAME_TWO });
+
+      const updatedParam = {
+        key: 'testUpdated',
+        value: 'testUpdated',
+        tags: ['a tag'],
+        description: 'test description',
+      };
+
+      await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const param = getResponse.body[0];
+      assertHas(param, testParam);
+
+      // space does exist so get request should be 200
+      await supertest
+        .get(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      await supertest
+        .put(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}/${param.id}}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(updatedParam)
+        .expect(404);
+
+      const updatedGetResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const actualUpdatedParam = updatedGetResponse.body[0];
+      assertHas(actualUpdatedParam, testParam);
+    });
+
+    it('handles invalid spaces', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+      await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      await supertest
+        .post(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(404);
+    });
+
+    it('handles editing with invalid spaces', async () => {
+      const updatedParam = {
+        key: 'testUpdated',
+        value: 'testUpdated',
+        tags: ['a tag'],
+        description: 'test description',
+      };
+
+      await supertest
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(testParam)
+        .expect(200);
+      const getResponse = await supertest
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      const param = getResponse.body[0];
+      assertHas(param, testParam);
+
+      await supertest
+        .put(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}/${param.id}}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(updatedParam)
+        .expect(404);
+    });
+
+    it('handles share across spaces', async () => {
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+      await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ ...testParam, share_across_spaces: true })
+        .expect(200);
+
+      const getResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(getResponse.body[0].namespaces).eql(['*']);
+      assertHas(getResponse.body[0], testParam);
+    });
+
+    it('should not return values for non admin user', async () => {
+      const resp = await supertest
+        .get(`${SYNTHETICS_API_URLS.PARAMS}`)
+        .set(adminRoleAuthc.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      const params = resp.body;
+      expect(params.length).to.eql(6);
+      params.forEach((param: any) => {
+        expect(param.value).to.eql(undefined);
+        expect(param.key).to.not.empty();
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor.ts
new file mode 100644
index 0000000000000..3e1582ea3ec2b
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor.ts
@@ -0,0 +1,164 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  EncryptedSyntheticsSavedMonitor,
+  HTTPFields,
+  MonitorFields,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('DeleteMonitorRoute', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+    let editorUser: RoleCredentials;
+    let testPolicyId = '';
+    let privateLocations: PrivateLocation[];
+
+    const saveMonitor = async (
+      monitor: MonitorFields
+    ): Promise<EncryptedSyntheticsSavedMonitor> => {
+      const res = await supertest
+        .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(monitor);
+
+      expect(res.status).to.eql(200, JSON.stringify(res.body));
+
+      return res.body;
+    };
+
+    const deleteMonitor = async (monitorId?: string | string[], statusCode = 200) => {
+      return monitorTestService.deleteMonitor(editorUser, monitorId, statusCode, 'default');
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      await testPrivateLocations.installSyntheticsPackage();
+      const testPolicyName = 'Fleet test server policy' + Date.now();
+      const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
+      testPolicyId = apiResponse.body.item.id;
+      privateLocations = await testPrivateLocations.setTestLocations([testPolicyId]);
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+
+      _httpMonitorJson = getFixtureJson('http_monitor');
+    });
+
+    beforeEach(() => {
+      httpMonitorJson = {
+        ..._httpMonitorJson,
+        locations: [privateLocations[0]],
+      };
+    });
+
+    it('deletes monitor by id', async () => {
+      const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields);
+
+      const deleteResponse = await deleteMonitor(monitorId);
+
+      expect(deleteResponse.body).eql([{ id: monitorId, deleted: true }]);
+
+      // Hit get endpoint and expect 404 as well
+      await supertest
+        .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(404);
+    });
+
+    it('deletes monitor by param id', async () => {
+      const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields);
+
+      const deleteResponse = await monitorTestService.deleteMonitorByIdParam(
+        editorUser,
+        monitorId,
+        200,
+        'default'
+      );
+
+      expect(deleteResponse.body).eql([{ id: monitorId, deleted: true }]);
+
+      // Hit get endpoint and expect 404 as well
+      await supertest
+        .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(404);
+    });
+
+    it('throws error if both body and param are missing', async () => {
+      await supertest
+        .delete(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+        .send()
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(400);
+    });
+
+    it('deletes multiple monitors by id', async () => {
+      const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields);
+      const { id: monitorId2 } = await saveMonitor({
+        ...httpMonitorJson,
+        name: 'another -2',
+      } as MonitorFields);
+
+      const deleteResponse = await deleteMonitor([monitorId2, monitorId]);
+
+      expect(
+        deleteResponse.body.sort((a: { id: string }, b: { id: string }) => (a.id > b.id ? 1 : -1))
+      ).eql(
+        [
+          { id: monitorId2, deleted: true },
+          { id: monitorId, deleted: true },
+        ].sort((a, b) => (a.id > b.id ? 1 : -1))
+      );
+
+      // Hit get endpoint and expect 404 as well
+      await supertest
+        .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(404);
+    });
+
+    it('returns 404 if monitor id is not found', async () => {
+      const invalidMonitorId = 'invalid-id';
+      const expected404Message = `Monitor id ${invalidMonitorId} not found!`;
+
+      const deleteResponse = await deleteMonitor(invalidMonitorId);
+
+      expect(deleteResponse.status).eql(200);
+      expect(deleteResponse.body).eql([
+        {
+          id: invalidMonitorId,
+          deleted: false,
+          error: expected404Message,
+        },
+      ]);
+    });
+
+    it('validates empty monitor id', async () => {
+      await deleteMonitor(undefined, 400);
+      await deleteMonitor([], 400);
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor_project.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor_project.ts
new file mode 100644
index 0000000000000..e2eecb91cb154
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/delete_monitor_project.ts
@@ -0,0 +1,521 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  ConfigKey,
+  ProjectMonitorsRequest,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { REQUEST_TOO_LARGE } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/delete_monitor_project';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { PackagePolicy } from '@kbn/fleet-plugin/common';
+import expect from '@kbn/expect';
+import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('DeleteProjectMonitors', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const supertestWithAuth = getService('supertest');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    let projectMonitors: ProjectMonitorsRequest;
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+
+    const testPrivateLocationsService = new PrivateLocationTestService(getService);
+
+    const setUniqueIdsAndLocations = (
+      request: ProjectMonitorsRequest,
+      privateLocations: PrivateLocation[] = []
+    ) => {
+      return {
+        ...request,
+        monitors: request.monitors.map((monitor) => ({
+          ...monitor,
+          id: uuidv4(),
+          locations: [],
+          privateLocations: privateLocations.map((location) => location.label),
+        })),
+      };
+    };
+
+    before(async () => {
+      await testPrivateLocationsService.installSyntheticsPackage();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+    });
+
+    beforeEach(async () => {
+      await kibanaServer.savedObjects.clean({ types: ['synthetics-private-location'] });
+      privateLocation = await testPrivateLocationsService.addTestPrivateLocation();
+      projectMonitors = setUniqueIdsAndLocations(getFixtureJson('project_browser_monitor'), [
+        privateLocation,
+      ]);
+    });
+
+    it('only allows 250 requests at a time', async () => {
+      const project = 'test-brower-suite';
+      const monitors = [];
+      for (let i = 0; i < 251; i++) {
+        monitors.push({
+          ...projectMonitors.monitors[0],
+          id: `test-id-${i}`,
+          name: `test-name-${i}`,
+        });
+      }
+      const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitors.slice(0, 250) })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitors.slice(250, 251) })
+          .expect(200);
+
+        const savedObjectsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total } = savedObjectsResponse.body;
+        expect(total).to.eql(251);
+
+        const response = await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(400);
+        const { message } = response.body;
+        expect(message).to.eql(REQUEST_TOO_LARGE);
+      } finally {
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 251) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - handles browser monitors', async () => {
+      const monitorToDelete = 'second-monitor-id';
+      const monitors = [
+        projectMonitors.monitors[0],
+        {
+          ...projectMonitors.monitors[0],
+          id: monitorToDelete,
+        },
+      ];
+      const project = 'test-brower-suite';
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors })
+          .expect(200);
+
+        const savedObjectsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total } = savedObjectsResponse.body;
+        expect(total).to.eql(2);
+        const monitorsToDelete = [monitorToDelete];
+
+        const response = await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+
+        expect(response.body.deleted_monitors).to.eql(monitorsToDelete);
+
+        const responseAfterDeletion = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total: totalAfterDeletion } = responseAfterDeletion.body;
+        expect(totalAfterDeletion).to.eql(1);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+      }
+    });
+
+    it('does not delete monitors from a different project', async () => {
+      const monitors = [...projectMonitors.monitors];
+      const project = 'test-brower-suite';
+      const secondProject = 'second-project';
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              secondProject
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors })
+          .expect(200);
+
+        const savedObjectsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const secondProjectSavedObjectResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${secondProject}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total } = savedObjectsResponse.body;
+        const { total: secondProjectTotal } = secondProjectSavedObjectResponse.body;
+        expect(total).to.eql(monitors.length);
+        expect(secondProjectTotal).to.eql(monitors.length);
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        const response = await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+
+        expect(response.body.deleted_monitors).to.eql(monitorsToDelete);
+
+        const responseAfterDeletion = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const secondResponseAfterDeletion = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${secondProject}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total: totalAfterDeletion } = responseAfterDeletion.body;
+        const { total: secondProjectTotalAfterDeletion } = secondResponseAfterDeletion.body;
+        expect(totalAfterDeletion).to.eql(0);
+        expect(secondProjectTotalAfterDeletion).to.eql(monitors.length);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              secondProject
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+      }
+    });
+
+    it('does not delete monitors from the same project in a different space project', async () => {
+      const monitors = [...projectMonitors.monitors];
+      const project = 'test-brower-suite';
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      const spaceScopedPrivateLocation = await testPrivateLocationsService.addTestPrivateLocation(
+        SPACE_ID
+      );
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.map((monitor) => ({
+              ...monitor,
+              privateLocations: [privateLocation.label],
+            })),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.map((monitor) => ({
+              ...monitor,
+              privateLocations: [spaceScopedPrivateLocation.label],
+            })),
+          })
+          .expect(200);
+
+        const savedObjectsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const secondSpaceProjectSavedObjectResponse = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total } = savedObjectsResponse.body;
+        const { total: secondSpaceTotal } = secondSpaceProjectSavedObjectResponse.body;
+
+        expect(total).to.eql(monitors.length);
+        expect(secondSpaceTotal).to.eql(monitors.length);
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        const response = await supertest
+          .delete(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+
+        expect(response.body.deleted_monitors).to.eql(monitorsToDelete);
+
+        const responseAfterDeletion = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const secondSpaceResponseAfterDeletion = await supertest
+          .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total: totalAfterDeletion } = responseAfterDeletion.body;
+        const { total: secondProjectTotalAfterDeletion } = secondSpaceResponseAfterDeletion.body;
+        expect(totalAfterDeletion).to.eql(monitors.length);
+        expect(secondProjectTotalAfterDeletion).to.eql(0);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              project
+            )}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+      }
+    });
+
+    it('deletes integration policies when project monitors are deleted', async () => {
+      const monitors = [
+        { ...projectMonitors.monitors[0], privateLocations: [privateLocation.label] },
+      ];
+      const project = 'test-brower-suite';
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors })
+          .expect(200);
+
+        const savedObjectsResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total } = savedObjectsResponse.body;
+        expect(total).to.eql(monitors.length);
+        const apiResponsePolicy = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy = apiResponsePolicy.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] +
+              '-' +
+              privateLocation.id
+        );
+        expect(packagePolicy.policy_id).to.be(privateLocation.id);
+
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        const response = await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+
+        expect(response.body.deleted_monitors).to.eql(monitorsToDelete);
+
+        const responseAfterDeletion = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+          .query({
+            filter: `${syntheticsMonitorType}.attributes.project_id: "${project}"`,
+          })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const { total: totalAfterDeletion } = responseAfterDeletion.body;
+        expect(totalAfterDeletion).to.eql(0);
+        const apiResponsePolicy2 = await supertestWithAuth.get(
+          '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+        );
+
+        const packagePolicy2 = apiResponsePolicy2.body.items.find(
+          (pkgPolicy: PackagePolicy) =>
+            pkgPolicy.id ===
+            savedObjectsResponse.body.monitors[0][ConfigKey.CUSTOM_HEARTBEAT_ID] +
+              '-' +
+              privateLocation.id
+        );
+        expect(packagePolicy2).to.be(undefined);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete })
+          .expect(200);
+      }
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor.ts
new file mode 100644
index 0000000000000..755fdad3aa196
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor.ts
@@ -0,0 +1,370 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import moment from 'moment';
+import { v4 as uuidv4 } from 'uuid';
+import { omit } from 'lodash';
+import {
+  ConfigKey,
+  EncryptedSyntheticsSavedMonitor,
+  HTTPFields,
+  MonitorFields,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { omitResponseTimestamps, omitEmptyValues } from './helpers/monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('EditMonitorAPI', function () {
+    const supertestWithAuth = getService('supertest');
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+    let testPolicyId = '';
+    let editorUser: RoleCredentials;
+    let privateLocations: PrivateLocation[];
+
+    const saveMonitor = async (monitor: MonitorFields, spaceId?: string) => {
+      const apiURL = spaceId
+        ? `/s/${spaceId}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`
+        : SYNTHETICS_API_URLS.SYNTHETICS_MONITORS;
+      const res = await supertest
+        .post(apiURL + '?internal=true')
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(monitor)
+        .expect(200);
+
+      const { url, created_at: createdAt, updated_at: updatedAt, ...rest } = res.body;
+
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+      return rest as EncryptedSyntheticsSavedMonitor;
+    };
+
+    const editMonitor = async (modifiedMonitor: MonitorFields, monitorId: string) => {
+      const res = await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId + '?internal=true')
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(modifiedMonitor);
+
+      expect(res.status).eql(200, JSON.stringify(res.body));
+
+      const { created_at: createdAt, updated_at: updatedAt } = res.body;
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+      return omit(res.body, ['created_at', 'updated_at']);
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      await supertestWithAuth.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const testPolicyName = 'Fleet test server policy' + Date.now();
+      const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
+      testPolicyId = apiResponse.body.item.id;
+      privateLocations = await testPrivateLocations.setTestLocations([testPolicyId]);
+      _httpMonitorJson = getFixtureJson('http_monitor');
+    });
+
+    after(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+    });
+
+    beforeEach(() => {
+      httpMonitorJson = { ..._httpMonitorJson, locations: [privateLocations[0]] };
+    });
+
+    it('edits the monitor', async () => {
+      const newMonitor = httpMonitorJson;
+
+      const savedMonitor = await saveMonitor(newMonitor as MonitorFields);
+      const monitorId = savedMonitor[ConfigKey.CONFIG_ID];
+
+      expect(omitResponseTimestamps(savedMonitor)).eql(
+        omitEmptyValues({
+          ...newMonitor,
+          [ConfigKey.MONITOR_QUERY_ID]: monitorId,
+          [ConfigKey.CONFIG_ID]: monitorId,
+        })
+      );
+
+      const updates: Partial<HTTPFields> = {
+        [ConfigKey.URLS]: 'https://modified-host.com',
+        [ConfigKey.NAME]: 'Modified name',
+        [ConfigKey.LOCATIONS]: [privateLocations[0]],
+        [ConfigKey.REQUEST_HEADERS_CHECK]: {
+          sampleHeader2: 'sampleValue2',
+        },
+        [ConfigKey.METADATA]: {
+          script_source: {
+            is_generated_script: false,
+            file_name: 'test-file.name',
+          },
+        },
+      };
+
+      const modifiedMonitor = {
+        ...savedMonitor,
+        ...updates,
+        [ConfigKey.METADATA]: {
+          ...newMonitor[ConfigKey.METADATA],
+          ...updates[ConfigKey.METADATA],
+        },
+      } as any;
+
+      const editResponse = await editMonitor(modifiedMonitor, monitorId);
+
+      expect(editResponse).eql(
+        omitEmptyValues({
+          ...modifiedMonitor,
+          revision: 2,
+        })
+      );
+    });
+
+    it('strips unknown keys from monitor edits', async () => {
+      const newMonitor = { ...httpMonitorJson, name: 'yet another' };
+
+      const savedMonitor = await saveMonitor(newMonitor as MonitorFields);
+      const monitorId = savedMonitor[ConfigKey.CONFIG_ID];
+
+      const { created_at: createdAt, updated_at: updatedAt } = savedMonitor;
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+      expect(omitResponseTimestamps(savedMonitor)).eql(
+        omitEmptyValues({
+          ...newMonitor,
+          [ConfigKey.MONITOR_QUERY_ID]: monitorId,
+          [ConfigKey.CONFIG_ID]: monitorId,
+        })
+      );
+
+      const updates: Partial<HTTPFields> = {
+        [ConfigKey.URLS]: 'https://modified-host.com',
+        [ConfigKey.NAME]: 'Modified name like that',
+        [ConfigKey.LOCATIONS]: [privateLocations[0]],
+        [ConfigKey.REQUEST_HEADERS_CHECK]: {
+          sampleHeader2: 'sampleValue2',
+        },
+        [ConfigKey.METADATA]: {
+          script_source: {
+            is_generated_script: false,
+            file_name: 'test-file.name',
+          },
+        },
+        unknownkey: 'unknownvalue',
+      } as Partial<HTTPFields>;
+
+      const modifiedMonitor = omit(
+        {
+          ...updates,
+          [ConfigKey.METADATA]: {
+            ...newMonitor[ConfigKey.METADATA],
+            ...updates[ConfigKey.METADATA],
+          },
+        },
+        ['unknownkey']
+      );
+
+      const editResponse = await editMonitor(modifiedMonitor as MonitorFields, monitorId);
+
+      expect(editResponse).eql(
+        omitEmptyValues({
+          ...savedMonitor,
+          ...modifiedMonitor,
+          revision: 2,
+        })
+      );
+      expect(editResponse).not.to.have.keys('unknownkey');
+    });
+
+    it('returns 404 if monitor id is not present', async () => {
+      const invalidMonitorId = 'invalid-id';
+      const expected404Message = `Monitor id ${invalidMonitorId} not found!`;
+
+      const editResponse = await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + invalidMonitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(httpMonitorJson)
+        .expect(404);
+
+      expect(editResponse.body.message).eql(expected404Message);
+    });
+
+    it('returns bad request if payload is invalid for HTTP monitor', async () => {
+      const { id: monitorId, ...savedMonitor } = await saveMonitor(
+        httpMonitorJson as MonitorFields
+      );
+
+      // Delete a required property to make payload invalid
+      const toUpdate = { ...savedMonitor, 'check.request.headers': null };
+
+      const apiResponse = await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(toUpdate);
+
+      expect(apiResponse.status).eql(400);
+    });
+
+    it('returns bad request if monitor type is invalid', async () => {
+      const { id: monitorId, ...savedMonitor } = await saveMonitor({
+        ...httpMonitorJson,
+        name: 'test monitor - 11',
+      } as MonitorFields);
+
+      const toUpdate = { ...savedMonitor, type: 'invalid-data-steam' };
+
+      const apiResponse = await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(toUpdate);
+
+      expect(apiResponse.status).eql(400);
+      expect(apiResponse.body.message).eql(
+        'Monitor type cannot be changed from http to invalid-data-steam.'
+      );
+    });
+
+    it('sets config hash to empty string on edits', async () => {
+      const newMonitor = httpMonitorJson;
+      const configHash = 'djrhefje';
+
+      const savedMonitor = await saveMonitor({
+        ...(newMonitor as MonitorFields),
+        [ConfigKey.CONFIG_HASH]: configHash,
+        name: 'test monitor - 12',
+      });
+      const monitorId = savedMonitor[ConfigKey.CONFIG_ID];
+      const { created_at: createdAt, updated_at: updatedAt } = savedMonitor;
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+
+      expect(savedMonitor).eql(
+        omitEmptyValues({
+          ...newMonitor,
+          [ConfigKey.CONFIG_ID]: monitorId,
+          [ConfigKey.MONITOR_QUERY_ID]: monitorId,
+          name: 'test monitor - 12',
+          hash: configHash,
+        })
+      );
+
+      const updates: Partial<HTTPFields> = {
+        [ConfigKey.URLS]: 'https://modified-host.com',
+        name: 'test monitor - 12',
+      } as Partial<HTTPFields>;
+
+      const modifiedMonitor = {
+        ...newMonitor,
+        ...updates,
+        [ConfigKey.METADATA]: {
+          ...newMonitor[ConfigKey.METADATA],
+          ...updates[ConfigKey.METADATA],
+        },
+      };
+
+      const editResponse = await editMonitor(modifiedMonitor as MonitorFields, monitorId);
+
+      expect(editResponse).eql(
+        omitEmptyValues({
+          ...modifiedMonitor,
+          [ConfigKey.CONFIG_ID]: monitorId,
+          [ConfigKey.MONITOR_QUERY_ID]: monitorId,
+          [ConfigKey.CONFIG_HASH]: '',
+          revision: 2,
+        })
+      );
+      expect(editResponse).not.to.have.keys('unknownkey');
+    });
+
+    it('handles spaces', async () => {
+      const name = 'Monitor with private location';
+
+      const SPACE_ID = `test-space-${uuidv4()}`;
+      const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+      const spaceScopedPrivateLocation = await testPrivateLocations.addTestPrivateLocation(
+        SPACE_ID
+      );
+      const newMonitor = {
+        name,
+        type: 'http',
+        urls: 'https://elastic.co',
+        locations: [spaceScopedPrivateLocation],
+      };
+
+      const savedMonitor = await saveMonitor(newMonitor as MonitorFields, SPACE_ID);
+
+      const monitorId = savedMonitor[ConfigKey.CONFIG_ID];
+      const toUpdate = {
+        ...savedMonitor,
+        urls: 'https://google.com',
+      };
+      await supertest
+        .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}/${monitorId}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(toUpdate)
+        .expect(200);
+
+      const updatedResponse = await monitorTestService.getMonitor(monitorId, {
+        space: SPACE_ID,
+        internal: true,
+        user: editorUser,
+      });
+
+      // ensure monitor was updated
+      expect(updatedResponse.body.urls).eql(toUpdate.urls);
+
+      // update a second time, ensures AAD was not corrupted
+      const toUpdate2 = {
+        ...savedMonitor,
+        urls: 'https://google.com',
+      };
+
+      await supertest
+        .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}/${monitorId}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(toUpdate2)
+        .expect(200);
+
+      const updatedResponse2 = await monitorTestService.getMonitor(monitorId, {
+        space: SPACE_ID,
+        internal: true,
+        user: editorUser,
+      });
+
+      // ensure monitor was updated
+      expect(updatedResponse2.body.urls).eql(toUpdate2.urls);
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor_public_api.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor_public_api.ts
new file mode 100644
index 0000000000000..bb659523a5132
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/edit_monitor_public_api.ts
@@ -0,0 +1,301 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import expect from '@kbn/expect';
+import rawExpect from 'expect';
+import { v4 as uuidv4 } from 'uuid';
+import { omit } from 'lodash';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import moment from 'moment';
+import { PrivateLocation } from '@kbn/synthetics-plugin/common/runtime_types';
+import { LOCATION_REQUIRED_ERROR } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/monitor_validation';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { addMonitorAPIHelper, omitMonitorKeys } from './create_monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('EditMonitorsPublicAPI', function () {
+    const supertestAPI = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    let editorUser: RoleCredentials;
+    let privateLocation1: PrivateLocation;
+    let privateLocation2: PrivateLocation;
+
+    async function addMonitorAPI(monitor: any, statusCode: number = 200) {
+      return await addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorUser, samlAuth);
+    }
+
+    async function editMonitorAPI(id: string, monitor: any, statusCode: number = 200) {
+      return await editMonitorAPIHelper(id, monitor, statusCode);
+    }
+
+    async function editMonitorAPIHelper(monitorId: string, monitor: any, statusCode = 200) {
+      const result = await supertestAPI
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + `/${monitorId}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(monitor);
+
+      expect(result.status).eql(statusCode, JSON.stringify(result.body));
+
+      if (statusCode === 200) {
+        const {
+          created_at: createdAt,
+          updated_at: updatedAt,
+          id,
+          config_id: configId,
+        } = result.body;
+        expect(id).not.empty();
+        expect(configId).not.empty();
+        expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+        return {
+          rawBody: result.body,
+          body: {
+            ...omit(result.body, [
+              'created_at',
+              'updated_at',
+              'id',
+              'config_id',
+              'form_monitor_type',
+            ]),
+          },
+        };
+      }
+      return result.body;
+    }
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      await testPrivateLocations.installSyntheticsPackage();
+      privateLocation1 = await testPrivateLocations.addTestPrivateLocation();
+      privateLocation2 = await testPrivateLocations.addTestPrivateLocation();
+    });
+
+    after(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+    });
+    let monitorId = 'test-id';
+
+    const defaultFields = DEFAULT_FIELDS.http;
+
+    it('adds test monitor', async () => {
+      const monitor = {
+        type: 'http',
+        private_locations: [privateLocation1.id],
+        url: 'https://www.google.com',
+      };
+      const { body: result, rawBody } = await addMonitorAPI(monitor);
+      monitorId = rawBody.id;
+
+      expect(result).eql(
+        omitMonitorKeys({
+          ...defaultFields,
+          ...monitor,
+          locations: [privateLocation1],
+          name: 'https://www.google.com',
+        })
+      );
+    });
+
+    it('should return error for empty monitor', async function () {
+      const errMessage = 'Monitor must be a non-empty object';
+      const testCases = [{}, null, undefined, false, [], ''];
+      for (const testCase of testCases) {
+        const { message } = await editMonitorAPI(monitorId, testCase, 400);
+        expect(message).eql(errMessage);
+      }
+    });
+
+    it('return error if type is being changed', async () => {
+      const { message } = await editMonitorAPI(monitorId, { type: 'tcp' }, 400);
+      expect(message).eql('Monitor type cannot be changed from http to tcp.');
+    });
+
+    it('return error if monitor not found', async () => {
+      const { message } = await editMonitorAPI('invalid-monitor-id', { type: 'tcp' }, 404);
+      expect(message).eql('Monitor id invalid-monitor-id not found!');
+    });
+
+    it('return error if invalid location specified', async () => {
+      const { message } = await editMonitorAPI(
+        monitorId,
+        { type: 'http', locations: ['mars'] },
+        400
+      );
+      rawExpect(message).toContain(
+        "Invalid locations specified. Elastic managed Location(s) 'mars' not found."
+      );
+    });
+
+    it('return error if invalid private location specified', async () => {
+      const { message } = await editMonitorAPI(
+        monitorId,
+        {
+          type: 'http',
+          locations: ['mars'],
+          privateLocations: ['moon'],
+        },
+        400
+      );
+      expect(message).eql('Invalid monitor key(s) for http type:  privateLocations');
+
+      const result = await editMonitorAPI(
+        monitorId,
+        {
+          type: 'http',
+          locations: ['mars'],
+          private_locations: ['moon'],
+        },
+        400
+      );
+      rawExpect(result.message).toContain("Private Location(s) 'moon' not found.");
+    });
+
+    it('throws an error if empty locations', async () => {
+      const monitor = {
+        locations: [],
+        private_locations: [],
+      };
+      const { message } = await editMonitorAPI(monitorId, monitor, 400);
+
+      expect(message).eql(LOCATION_REQUIRED_ERROR);
+    });
+
+    it('cannot change origin type', async () => {
+      const monitor = {
+        origin: 'project',
+      };
+      const result = await editMonitorAPI(monitorId, monitor, 400);
+
+      expect(result).eql({
+        statusCode: 400,
+        error: 'Bad Request',
+        message: 'Unsupported origin type project, only ui type is supported via API.',
+        attributes: {
+          details: 'Unsupported origin type project, only ui type is supported via API.',
+          payload: { origin: 'project' },
+        },
+      });
+    });
+
+    const updates: any = {};
+
+    it('can change name of monitor', async () => {
+      updates.name = `updated name ${uuidv4()}`;
+      const monitor = {
+        name: updates.name,
+      };
+      const { body: result } = await editMonitorAPI(monitorId, monitor);
+
+      expect(result).eql(
+        omitMonitorKeys({
+          ...defaultFields,
+          ...monitor,
+          ...updates,
+          locations: [privateLocation1],
+          revision: 2,
+          url: 'https://www.google.com',
+        })
+      );
+    });
+
+    it('prevents duplicate name of monitor', async () => {
+      const name = `test name ${uuidv4()}`;
+      const monitor = {
+        name,
+        type: 'http',
+        private_locations: [privateLocation1.id],
+        url: 'https://www.google.com',
+      };
+      // create one monitor with one name
+      await addMonitorAPI(monitor);
+      // create another monitor with a different name
+      const { body: result, rawBody } = await addMonitorAPI({
+        ...monitor,
+        name: 'test name',
+      });
+      const newMonitorId = rawBody.id;
+
+      expect(result).eql(
+        omitMonitorKeys({
+          ...defaultFields,
+          ...monitor,
+          locations: [privateLocation1],
+          name: 'test name',
+        })
+      );
+
+      const editResult = await editMonitorAPI(
+        newMonitorId,
+        {
+          name,
+        },
+        400
+      );
+
+      expect(editResult).eql({
+        statusCode: 400,
+        error: 'Bad Request',
+        message: `Monitor name must be unique, "${name}" already exists.`,
+        attributes: {
+          details: `Monitor name must be unique, "${name}" already exists.`,
+        },
+      });
+    });
+
+    it('can add a second private location to existing monitor', async () => {
+      const monitor = {
+        private_locations: [privateLocation1.id, privateLocation2.id],
+      };
+
+      const { body: result } = await editMonitorAPI(monitorId, monitor);
+
+      expect(result).eql(
+        omitMonitorKeys({
+          ...defaultFields,
+          ...updates,
+          revision: 3,
+          url: 'https://www.google.com',
+          locations: [privateLocation1, privateLocation2],
+        })
+      );
+    });
+
+    it('can remove private location from existing monitor', async () => {
+      const monitor = {
+        private_locations: [privateLocation2.id],
+      };
+
+      const { body: result } = await editMonitorAPI(monitorId, monitor);
+
+      expect(result).eql(
+        omitMonitorKeys({
+          ...defaultFields,
+          ...updates,
+          revision: 4,
+          url: 'https://www.google.com',
+          locations: [privateLocation2],
+        })
+      );
+    });
+
+    it('can not remove all locations', async () => {
+      const monitor = {
+        locations: [],
+        private_locations: [],
+      };
+
+      const { message } = await editMonitorAPI(monitorId, monitor, 400);
+
+      expect(message).eql(LOCATION_REQUIRED_ERROR);
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/enable_default_alerting.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/enable_default_alerting.ts
new file mode 100644
index 0000000000000..231195be88e44
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/enable_default_alerting.ts
@@ -0,0 +1,330 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import expect from '@kbn/expect';
+import rawExpect from 'expect';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { omit } from 'lodash';
+import { HTTPFields, PrivateLocation } from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { DYNAMIC_SETTINGS_DEFAULTS } from '@kbn/synthetics-plugin/common/constants/settings_defaults';
+
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { addMonitorAPIHelper, omitMonitorKeys } from './create_monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('EnableDefaultAlerting', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const retry = getService('retry');
+    const samlAuth = getService('samlAuth');
+
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+
+    const privateLocationTestService = new PrivateLocationTestService(getService);
+
+    const addMonitorAPI = async (monitor: any, statusCode = 200) => {
+      return addMonitorAPIHelper(supertest, monitor, statusCode, editorUser, samlAuth);
+    };
+
+    after(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+    });
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      _httpMonitorJson = getFixtureJson('http_monitor');
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+    });
+
+    beforeEach(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      privateLocation = await privateLocationTestService.addTestPrivateLocation();
+      httpMonitorJson = {
+        ..._httpMonitorJson,
+        locations: [privateLocation],
+      };
+      await supertest
+        .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(DYNAMIC_SETTINGS_DEFAULTS)
+        .expect(200);
+    });
+
+    it('returns the created alerted when called', async () => {
+      const apiResponse = await supertest
+        .post(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      const omitFields = [
+        'apiKeyOwner',
+        'createdBy',
+        'updatedBy',
+        'id',
+        'updatedAt',
+        'createdAt',
+        'scheduledTaskId',
+        'executionStatus',
+        'monitoring',
+        'nextRun',
+        'lastRun',
+        'snoozeSchedule',
+        'viewInAppRelativeUrl',
+      ];
+
+      const statusRule = apiResponse.body.statusRule;
+      const tlsRule = apiResponse.body.tlsRule;
+
+      rawExpect(omit(statusRule, omitFields)).toEqual(
+        omit(defaultAlertRules.statusRule, omitFields)
+      );
+      rawExpect(omit(tlsRule, omitFields)).toEqual(omit(defaultAlertRules.tlsRule, omitFields));
+    });
+
+    it('enables alert when new monitor is added', async () => {
+      const newMonitor = httpMonitorJson;
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse).eql(omitMonitorKeys({ ...newMonitor, spaceId: 'default' }));
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule.ruleTypeId).eql('xpack.synthetics.alerts.monitorStatus');
+        expect(res.body.tlsRule.ruleTypeId).eql('xpack.synthetics.alerts.tls');
+      });
+    });
+
+    it('deletes (and recreates) the default rule when settings are updated', async () => {
+      const newMonitor = httpMonitorJson;
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse).eql(omitMonitorKeys(newMonitor));
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule.ruleTypeId).eql('xpack.synthetics.alerts.monitorStatus');
+        expect(res.body.tlsRule.ruleTypeId).eql('xpack.synthetics.alerts.tls');
+      });
+      const settings = await supertest
+        .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          defaultStatusRuleEnabled: false,
+          defaultTLSRuleEnabled: false,
+        });
+
+      expect(settings.body.defaultStatusRuleEnabled).eql(false);
+      expect(settings.body.defaultTLSRuleEnabled).eql(false);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule).eql(null);
+        expect(res.body.tlsRule).eql(null);
+      });
+
+      const settings2 = await supertest
+        .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          defaultStatusRuleEnabled: true,
+          defaultTLSRuleEnabled: true,
+        })
+        .expect(200);
+
+      expect(settings2.body.defaultStatusRuleEnabled).eql(true);
+      expect(settings2.body.defaultTLSRuleEnabled).eql(true);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule.ruleTypeId).eql('xpack.synthetics.alerts.monitorStatus');
+        expect(res.body.tlsRule.ruleTypeId).eql('xpack.synthetics.alerts.tls');
+      });
+    });
+
+    it('doesnt throw errors when rule has already been deleted', async () => {
+      const newMonitor = httpMonitorJson;
+
+      const { body: apiResponse } = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse).eql(omitMonitorKeys(newMonitor));
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule.ruleTypeId).eql('xpack.synthetics.alerts.monitorStatus');
+        expect(res.body.tlsRule.ruleTypeId).eql('xpack.synthetics.alerts.tls');
+      });
+
+      const settings = await supertest
+        .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({
+          defaultStatusRuleEnabled: false,
+          defaultTLSRuleEnabled: false,
+        })
+        .expect(200);
+
+      expect(settings.body.defaultStatusRuleEnabled).eql(false);
+      expect(settings.body.defaultTLSRuleEnabled).eql(false);
+
+      await supertest
+        .put(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule).eql(null);
+        expect(res.body.tlsRule).eql(null);
+      });
+
+      // call api again with the same settings, make sure its 200
+      await supertest
+        .put(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send()
+        .expect(200);
+
+      await retry.tryForTime(30 * 1000, async () => {
+        const res = await supertest
+          .get(SYNTHETICS_API_URLS.ENABLE_DEFAULT_ALERTING)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        expect(res.body.statusRule).eql(null);
+        expect(res.body.tlsRule).eql(null);
+      });
+    });
+  });
+}
+
+const defaultAlertRules = {
+  statusRule: {
+    id: '574e82f0-1672-11ee-8e7d-c985c0ef6c2e',
+    notifyWhen: null,
+    consumer: 'uptime',
+    alertTypeId: 'xpack.synthetics.alerts.monitorStatus',
+    tags: ['SYNTHETICS_DEFAULT_ALERT'],
+    name: 'Synthetics status internal rule',
+    enabled: true,
+    throttle: null,
+    apiKeyOwner: 'any',
+    apiKeyCreatedByUser: true,
+    createdBy: 'any',
+    updatedBy: 'any',
+    muteAll: false,
+    mutedInstanceIds: [],
+    revision: 0,
+    running: false,
+    schedule: { interval: '1m' },
+    actions: [],
+    params: {},
+    snoozeSchedule: [],
+    updatedAt: '2023-06-29T11:44:44.488Z',
+    createdAt: '2023-06-29T11:44:44.488Z',
+    scheduledTaskId: '574e82f0-1672-11ee-8e7d-c985c0ef6c2e',
+    executionStatus: {
+      status: 'ok',
+      lastExecutionDate: '2023-06-29T11:47:55.331Z',
+      lastDuration: 64,
+    },
+    ruleTypeId: 'xpack.synthetics.alerts.monitorStatus',
+    viewInAppRelativeUrl: '/app/observability/alerts/rules/574e82f0-1672-11ee-8e7d-c985c0ef6c2e',
+  },
+  tlsRule: {
+    id: '574eaa00-1672-11ee-8e7d-c985c0ef6c2e',
+    notifyWhen: null,
+    consumer: 'uptime',
+    alertTypeId: 'xpack.synthetics.alerts.tls',
+    tags: ['SYNTHETICS_DEFAULT_ALERT'],
+    name: 'Synthetics internal TLS rule',
+    enabled: true,
+    throttle: null,
+    apiKeyOwner: 'elastic_admin',
+    apiKeyCreatedByUser: true,
+    createdBy: 'elastic_admin',
+    updatedBy: 'elastic_admin',
+    muteAll: false,
+    mutedInstanceIds: [],
+    revision: 0,
+    running: false,
+    schedule: { interval: '1m' },
+    actions: [],
+    params: {},
+    snoozeSchedule: [],
+    updatedAt: '2023-06-29T11:44:44.489Z',
+    createdAt: '2023-06-29T11:44:44.489Z',
+    scheduledTaskId: '574eaa00-1672-11ee-8e7d-c985c0ef6c2e',
+    executionStatus: {
+      status: 'ok',
+      lastExecutionDate: '2023-06-29T11:44:46.214Z',
+      lastDuration: 193,
+    },
+    ruleTypeId: 'xpack.synthetics.alerts.tls',
+    viewInAppRelativeUrl: '/app/observability/alerts/rules/574e82f0-1672-11ee-8e7d-c985c0ef6c2e',
+  },
+};
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/browser_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/browser_monitor.json
new file mode 100644
index 0000000000000..1cb2d39685bf2
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/browser_monitor.json
@@ -0,0 +1,60 @@
+{
+  "type": "browser",
+  "enabled": true,
+  "alert": {
+    "status": {
+      "enabled": true
+    }
+  },
+  "journey_id": "",
+  "project_id": "",
+  "schedule": {
+    "number": "3",
+    "unit": "m"
+  },
+  "service.name": "",
+  "config_id": "",
+  "tags": ["cookie-test", "browser"],
+  "timeout": "16",
+  "__ui": {
+    "script_source": {
+      "is_generated_script": false,
+      "file_name": ""
+    },
+    "is_tls_enabled": false
+  },
+  "source.inline.script": "step(\"Visit /users api route\", async () => {\\n  const response = await page.goto('https://nextjs-test-synthetics.vercel.app/api/users');\\n  expect(response.status()).toEqual(200);\\n});",
+  "source.project.content": "",
+  "params": "",
+  "screenshots": "on",
+  "synthetics_args": [],
+  "filter_journeys.match": "",
+  "filter_journeys.tags": [],
+  "ignore_https_errors": false,
+  "throttling": {
+    "value": {
+      "download": "5",
+      "latency": "20",
+      "upload": "3"
+    },
+    "id": "default",
+    "label": "Default"
+  },
+  "locations": ["dev"],
+  "name": "Test HTTP Monitor 03",
+  "namespace": "testnamespace",
+  "origin": "ui",
+  "form_monitor_type": "multistep",
+  "url.port": null,
+  "id": "",
+  "hash": "",
+  "playwright_options": "",
+  "playwright_text_assertion": "",
+  "ssl.certificate": "",
+  "ssl.certificate_authorities": "",
+  "ssl.supported_protocols": ["TLSv1.1", "TLSv1.2", "TLSv1.3"],
+  "ssl.verification_mode": "full",
+  "revision": 1,
+  "max_attempts": 2,
+  "labels": {}
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/http_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/http_monitor.json
new file mode 100644
index 0000000000000..47d0637a7cd91
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/http_monitor.json
@@ -0,0 +1,83 @@
+{
+  "type": "http",
+  "enabled": true,
+  "alert": {
+    "status": {
+      "enabled": true
+    }
+  },
+  "tags": [
+    "tag1",
+    "tag2"
+  ],
+  "schedule": {
+    "number": "5",
+    "unit": "m"
+  },
+  "service.name": "",
+  "config_id": "",
+  "timeout": "180",
+  "__ui": {
+    "is_tls_enabled": false
+  },
+  "max_attempts": 2,
+  "max_redirects": "3",
+  "password": "test",
+  "urls": "https://nextjs-test-synthetics.vercel.app/api/users",
+  "url.port": null,
+  "proxy_url": "http://proxy.com",
+  "proxy_headers": {},
+  "check.response.body.negative": [],
+  "check.response.body.positive": [],
+  "check.response.json": [],
+  "response.include_body": "never",
+  "response.include_body_max_bytes": "1024",
+  "check.request.headers": {
+    "sampleHeader": "sampleHeaderValue"
+  },
+  "response.include_headers": true,
+  "check.response.status": [
+    "200",
+    "201"
+  ],
+  "check.request.body": {
+    "value": "testValue",
+    "type": "json"
+  },
+  "check.response.headers": {},
+  "check.request.method": "",
+  "username": "test-username",
+  "ssl.certificate_authorities": "t.string",
+  "ssl.certificate": "t.string",
+  "ssl.key": "t.string",
+  "ssl.key_passphrase": "t.string",
+  "ssl.verification_mode": "certificate",
+  "ssl.supported_protocols": [
+    "TLSv1.1",
+    "TLSv1.2"
+  ],
+  "name": "test-monitor-name",
+  "locations": [
+    {
+      "id": "dev",
+      "label": "Dev Service",
+      "geo": {
+        "lat": 0,
+        "lon": 0
+      },
+      "isServiceManaged": true
+    }
+  ],
+  "namespace": "testnamespace",
+  "revision": 1,
+  "origin": "ui",
+  "form_monitor_type": "http",
+  "journey_id": "",
+  "id": "",
+  "hash": "",
+  "mode": "any",
+  "ipv4": true,
+  "ipv6": true,
+  "params": "",
+  "labels": {}
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/icmp_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/icmp_monitor.json
new file mode 100644
index 0000000000000..8f97e8de7424d
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/icmp_monitor.json
@@ -0,0 +1,35 @@
+{
+  "type": "icmp",
+  "locations": ["dev"],
+  "journey_id": "",
+  "enabled": true,
+  "alert": {
+    "status": {
+      "enabled": true
+    }
+  },
+  "schedule": {
+    "number": "3",
+    "unit": "m"
+  },
+  "config_id": "",
+  "service.name": "example-service-name",
+  "tags": [
+    "tagT1",
+    "tagT2"
+  ],
+  "timeout": "16",
+  "hosts": "192.33.22.111:3333",
+  "wait": "1",
+  "name": "Test HTTP Monitor 04",
+  "namespace": "testnamespace",
+  "origin": "ui",
+  "form_monitor_type": "icmp",
+  "id": "",
+  "hash": "",
+  "mode": "any",
+  "ipv4": true,
+  "ipv6": true,
+  "params": "",
+  "max_attempts": 2
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/inspect_browser_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/inspect_browser_monitor.json
new file mode 100644
index 0000000000000..2307e4dcbfaf8
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/inspect_browser_monitor.json
@@ -0,0 +1,85 @@
+{
+  "type": "browser",
+  "form_monitor_type": "multistep",
+  "enabled": true,
+  "alert": {
+    "status": {
+      "enabled": true
+    }
+  },
+  "schedule": {
+    "number": "10",
+    "unit": "m"
+  },
+  "service.name": "",
+  "config_id": "0088b13c-9bb0-4fc6-a0b5-63b9b024eabb",
+  "tags": [],
+  "timeout": null,
+  "name": "check if title is present",
+  "locations": [
+    {
+      "id": "dev",
+      "label": "Dev Service",
+      "geo": {
+        "lat": 0,
+        "lon": 0
+      },
+      "isServiceManaged": true
+    }
+  ],
+  "namespace": "default",
+  "origin": "project",
+  "journey_id": "bb82f7de-d832-4b14-8097-38a464d5fe49",
+  "hash": "ekrjelkjrelkjre",
+  "id": "bb82f7de-d832-4b14-8097-38a464d5fe49-test-project-cb47c83a-45e7-416a-9301-cb476b5bff01-default",
+  "params": "",
+  "project_id": "test-project-cb47c83a-45e7-416a-9301-cb476b5bff01",
+  "playwright_options": "{\"headless\":true,\"chromiumSandbox\":false}",
+  "__ui": {
+    "script_source": {
+      "is_generated_script": false,
+      "file_name": ""
+    }
+  },
+  "url.port": null,
+  "source.inline.script": "",
+  "source.project.content": "UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA",
+  "playwright_text_assertion": "",
+  "urls": "",
+  "screenshots": "on",
+  "synthetics_args": [],
+  "filter_journeys.match": "check if title is present",
+  "filter_journeys.tags": [],
+  "ignore_https_errors": false,
+  "throttling": {
+    "value": {
+      "download": "5",
+      "upload": "3",
+      "latency": "20"
+    },
+    "id": "default",
+    "label": "Default"
+  },
+  "ssl.certificate_authorities": "",
+  "ssl.certificate": "",
+  "ssl.key": "",
+  "ssl.key_passphrase": "",
+  "ssl.verification_mode": "full",
+  "ssl.supported_protocols": [
+    "TLSv1.1",
+    "TLSv1.2",
+    "TLSv1.3"
+  ],
+  "original_space": "default",
+  "custom_heartbeat_id": "bb82f7de-d832-4b14-8097-38a464d5fe49-test-project-cb47c83a-45e7-416a-9301-cb476b5bff01-default",
+  "revision": 1,
+  "source.inline": {
+    "type": "inline",
+    "script": "",
+    "fileName": ""
+  },
+  "service": {
+    "name": ""
+  },
+  "max_attempts": 2
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_browser_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_browser_monitor.json
new file mode 100644
index 0000000000000..18cea933e7974
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_browser_monitor.json
@@ -0,0 +1,30 @@
+{
+  "keep_stale": true,
+  "project": "test-suite",
+  "monitors": [{
+    "throttling": {
+      "download": 5,
+      "upload": 3,
+      "latency": 20
+    },
+    "schedule": 10,
+    "locations": [
+      "dev"
+    ],
+    "params": {},
+    "playwrightOptions": {
+      "headless": true,
+      "chromiumSandbox": false
+    },
+    "name": "check if title is present",
+    "id": "check-if-title-is-present",
+    "tags": [],
+    "content": "UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA",
+    "filter": {
+      "match": "check if title is present"
+    },
+    "hash": "ekrjelkjrelkjre",
+    "max_attempts": 2,
+    "type": "browser"
+  }]
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_http_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_http_monitor.json
new file mode 100644
index 0000000000000..05e1ebed01aec
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_http_monitor.json
@@ -0,0 +1,86 @@
+{
+  "project": "test-suite",
+  "keep_stale": false,
+  "monitors": [
+    {
+      "locations": ["dev"],
+      "type": "http",
+      "enabled": false,
+      "id": "my-monitor-2",
+      "name": "My Monitor 2",
+      "urls": [
+        "http://localhost:9200",
+        "http://anotherurl:9200"
+      ],
+      "schedule": 60,
+      "timeout": "80s",
+      "check.request": {
+        "method": "POST",
+        "headers": {
+          "Content-Type": "application/x-www-form-urlencoded"
+        }
+      },
+      "response": {
+        "include_body": "always"
+      },
+      "response.include_headers": false,
+      "check.response": {
+        "status": [
+          200
+        ],
+        "body": [
+          "Saved",
+          "saved"
+        ]
+      },
+      "unsupportedKey": {
+        "nestedUnsupportedKey": "unsupportedValue"
+      },
+      "hash": "ekrjelkjrelkjre"
+    },
+    {
+      "locations": ["dev"],
+      "type": "http",
+      "enabled": false,
+      "id": "my-monitor-3",
+      "name": "My Monitor 3",
+      "proxy_url": "${testGlobalParam2}",
+      "urls": [
+        "http://localhost:9200"
+      ],
+      "schedule": 60,
+      "timeout": "80s",
+      "check.request": {
+        "method": "POST",
+        "headers": {
+          "Content-Type": "application/x-www-form-urlencoded"
+        }
+      },
+      "response": {
+        "include_body": "always",
+        "include_body_max_bytes": 900
+      },
+      "tags": "tag2,tag2",
+      "response.include_headers": false,
+      "check.response": {
+        "status": [
+          200
+        ],
+        "body":{
+          "positive": [
+            "${testLocal1}",
+            "saved"
+          ]
+        },
+        "json": [{"description":"check status","expression":"foo.bar == \"myValue\""}]
+      },
+      "hash": "ekrjelkjrelkjre",
+      "ssl.verification_mode": "strict",
+      "params": {
+        "testLocal1": "testLocalParamsValue",
+        "testGlobalParam2": "testGlobalParamOverwrite"
+      },
+      "max_attempts": 2
+    }
+  ]
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_icmp_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_icmp_monitor.json
new file mode 100644
index 0000000000000..63e4215e46cca
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_icmp_monitor.json
@@ -0,0 +1,47 @@
+
+
+
+{
+  "project": "test-suite",
+  "keep_stale": true,
+  "monitors": [
+    {
+      "locations": [ "dev" ],
+      "type": "icmp",
+      "id": "Cloudflare-DNS",
+      "name": "Cloudflare DNS",
+      "hosts": [ "1.1.1.1" ],
+      "schedule": 1,
+      "tags": [ "service:smtp", "org:google" ],
+      "privateLocations": [ "Test private location 0" ],
+      "wait": "30s",
+      "hash": "ekrjelkjrelkjre"
+    },
+    {
+      "locations": [ "dev" ],
+      "type": "icmp",
+      "id": "Cloudflare-DNS-2",
+      "name": "Cloudflare DNS 2",
+      "hosts": "1.1.1.1",
+      "schedule": 1,
+      "tags": "tag1,tag2",
+      "privateLocations": [ "Test private location 0" ],
+      "wait": "1m",
+      "hash": "ekrjelkjrelkjre"
+    },
+    {
+      "locations": [ "dev" ],
+      "type": "icmp",
+      "id": "Cloudflare-DNS-3",
+      "name": "Cloudflare DNS 3",
+      "hosts": "1.1.1.1,2.2.2.2",
+      "schedule": 1,
+      "tags": "tag1,tag2",
+      "privateLocations": [ "Test private location 0" ],
+      "unsupportedKey": {
+        "nestedUnsupportedKey": "unnsuportedValue"
+      },
+      "hash": "ekrjelkjrelkjre"
+    }
+  ]
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_tcp_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_tcp_monitor.json
new file mode 100644
index 0000000000000..26382b010ec3e
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/project_tcp_monitor.json
@@ -0,0 +1,44 @@
+{
+  "project": "test-suite",
+  "keep_stale": true,
+  "monitors": [
+    {
+      "locations": [ "dev" ],
+      "type": "tcp",
+      "id": "gmail-smtp",
+      "name": "GMail SMTP",
+      "hosts": [ "smtp.gmail.com:587" ],
+      "schedule": 1,
+      "tags": [ "service:smtp", "org:google" ],
+      "privateLocations": [ ],
+      "hash": "ekrjelkjrelkjre",
+      "ssl.verification_mode": "strict"
+    },
+    {
+      "locations": [ "dev" ],
+      "type": "tcp",
+      "id": "always-down",
+      "name": "Always Down",
+      "hosts": "localhost:18278",
+      "schedule": 1,
+      "tags": "tag1,tag2",
+      "privateLocations": [  ],
+      "hash": "ekrjelkjrelkjre"
+    },
+    {
+      "locations": [ "dev" ],
+      "type": "tcp",
+      "id": "always-down",
+      "name": "Always Down",
+      "hosts": ["localhost", "anotherhost"],
+      "ports": ["5698"],
+      "schedule": 1,
+      "tags": "tag1,tag2",
+      "privateLocations": [ ],
+      "unsupportedKey": {
+        "nestedUnsupportedKey": "unnsuportedValue"
+      },
+      "hash": "ekrjelkjrelkjre"
+    }
+  ]
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/tcp_monitor.json b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/tcp_monitor.json
new file mode 100644
index 0000000000000..9cc646a36c943
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/fixtures/tcp_monitor.json
@@ -0,0 +1,43 @@
+{
+  "type": "tcp",
+  "locations": ["dev"],
+  "enabled": true,
+  "config_id": "",
+  "schedule": {
+    "number": "3",
+    "unit": "m"
+  },
+  "service.name": "",
+  "tags": [],
+  "timeout": "16",
+  "__ui": {
+    "is_tls_enabled": true
+  },
+  "hosts": "example-host:40",
+  "urls": "example-host:40",
+  "url.port": null,
+  "proxy_url": "",
+  "proxy_use_local_resolver": false,
+  "check.receive": "",
+  "check.send": "",
+  "ssl.certificate_authorities": "",
+  "ssl.certificate": "",
+  "ssl.key": "",
+  "ssl.key_passphrase": "examplepassphrase",
+  "ssl.verification_mode": "full",
+  "ssl.supported_protocols": [
+    "TLSv1.1",
+    "TLSv1.3"
+  ],
+  "name": "Test HTTP Monitor 04",
+  "namespace": "testnamespace",
+  "origin": "ui",
+  "form_monitor_type": "tcp",
+  "id": "",
+  "hash": "",
+  "mode": "any",
+  "ipv4": true,
+  "ipv6": true,
+  "params": "",
+  "max_attempts": 2
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_filters.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_filters.ts
new file mode 100644
index 0000000000000..cd6f8ff2f7275
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_filters.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import expect from '@kbn/expect';
+import { PrivateLocation } from '@kbn/synthetics-plugin/common/runtime_types';
+import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('getMonitorFilters', function () {
+    const kibanaServer = getService('kibanaServer');
+    const supertest = getService('supertestWithoutAuth');
+    const samlAuth = getService('samlAuth');
+
+    const privateLocationTestService = new PrivateLocationTestService(getService);
+
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+
+    after(async () => {
+      await kibanaServer.savedObjects.clean({ types: [syntheticsMonitorType] });
+    });
+
+    before(async () => {
+      await kibanaServer.savedObjects.clean({ types: [syntheticsMonitorType] });
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      privateLocation = await privateLocationTestService.addTestPrivateLocation();
+    });
+
+    it('get list of filters', async () => {
+      const apiResponse = await supertest
+        .get(SYNTHETICS_API_URLS.FILTERS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(apiResponse.body).eql({
+        monitorTypes: [],
+        tags: [],
+        locations: [],
+        projects: [],
+        schedules: [],
+      });
+    });
+
+    it('get list of filters with monitorTypes', async () => {
+      const newMonitor = {
+        name: 'Sample name',
+        type: 'http',
+        urls: 'https://elastic.co',
+        tags: ['apm', 'synthetics'],
+        locations: [privateLocation],
+      };
+
+      await supertest
+        .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(newMonitor)
+        .expect(200);
+
+      const apiResponse = await supertest
+        .get(SYNTHETICS_API_URLS.FILTERS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(apiResponse.body).eql({
+        monitorTypes: [{ label: 'http', count: 1 }],
+        tags: [
+          { label: 'apm', count: 1 },
+          { label: 'synthetics', count: 1 },
+        ],
+        locations: [{ label: privateLocation.id, count: 1 }],
+        projects: [],
+        schedules: [{ label: '3', count: 1 }],
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor.ts
new file mode 100644
index 0000000000000..4957ac0d2e688
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor.ts
@@ -0,0 +1,353 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { omit } from 'lodash';
+import moment from 'moment';
+import { v4 as uuidv4 } from 'uuid';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  ConfigKey,
+  EncryptedSyntheticsSavedMonitor,
+  MonitorFields,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { secretKeys } from '@kbn/synthetics-plugin/common/constants/monitor_management';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+import { omitMonitorKeys } from './create_monitor';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { getFixtureJson } from './helpers/get_fixture_json';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('getSyntheticsMonitors', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const retry = getService('retry');
+    const samlAuth = getService('samlAuth');
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+    const privateLocationTestService = new PrivateLocationTestService(getService);
+
+    let _monitors: MonitorFields[];
+    let monitors: MonitorFields[];
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+
+    const saveMonitor = async (monitor: MonitorFields, spaceId?: string) => {
+      let url = SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '?internal=true';
+      if (spaceId) {
+        url = '/s/' + spaceId + url;
+      }
+      const res = await supertest
+        .post(url)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(monitor);
+
+      expect(res.status).eql(200, JSON.stringify(res.body));
+
+      return res.body as EncryptedSyntheticsSavedMonitor;
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      privateLocation = await privateLocationTestService.addTestPrivateLocation();
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      _monitors = [
+        getFixtureJson('icmp_monitor'),
+        getFixtureJson('tcp_monitor'),
+        getFixtureJson('http_monitor'),
+        getFixtureJson('browser_monitor'),
+      ].map((mon) => ({
+        ...mon,
+        locations: [privateLocation],
+      }));
+    });
+
+    beforeEach(() => {
+      monitors = _monitors;
+    });
+
+    describe('get many monitors', () => {
+      it('without params', async () => {
+        const uuid = uuidv4();
+        const [mon1, mon2] = await Promise.all(
+          monitors.map((mon, i) => saveMonitor({ ...mon, name: `${mon.name}-${uuid}-${i}` }))
+        );
+
+        const apiResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '?perPage=1000&internal=true') // 1000 to sort of load all saved monitors
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const found: MonitorFields[] = apiResponse.body.monitors.filter(({ id }: MonitorFields) =>
+          [mon1.id, mon2.id].includes(id)
+        );
+        found.sort(({ id: a }) => (a === mon2.id ? 1 : a === mon1.id ? -1 : 0));
+        const foundMonitors = found.map(
+          (fields) => fields as unknown as EncryptedSyntheticsSavedMonitor
+        );
+
+        const expected = [mon1, mon2];
+
+        /**
+         * These dates are dynamically generated by the server, so we can't
+         * compare them directly. Instead, we'll just check that they're valid.
+         */
+        foundMonitors.forEach(({ updated_at: updatedAt, created_at: createdAt }) => {
+          expect(moment(createdAt).isValid()).to.be(true);
+          expect(moment(updatedAt).isValid()).to.be(true);
+        });
+
+        expect(foundMonitors.map((fm) => omit(fm, 'updated_at', 'created_at', 'spaceId'))).eql(
+          expected.map((expectedMon) =>
+            omit(expectedMon, ['updated_at', 'created_at', ...secretKeys])
+          )
+        );
+      });
+
+      it('with page params', async () => {
+        const allMonitors = [...monitors, ...monitors];
+        for (const mon of allMonitors) {
+          await saveMonitor({ ...mon, name: mon.name + Date.now() });
+        }
+
+        await retry.try(async () => {
+          const firstPageResp = await supertest
+            .get(`${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=2`)
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+          const secondPageResp = await supertest
+            .get(`${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=2&perPage=3`)
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .expect(200);
+
+          expect(firstPageResp.body.total).greaterThan(6);
+          expect(firstPageResp.body.monitors.length).eql(2);
+          expect(secondPageResp.body.monitors.length).eql(3);
+
+          expect(firstPageResp.body.monitors[0].id).not.eql(secondPageResp.body.monitors[0].id);
+        });
+      });
+
+      it('with single monitorQueryId filter', async () => {
+        const uuid = uuidv4();
+        const [_, { id: id2 }] = await Promise.all(
+          monitors
+            .map((mon, i) => ({ ...mon, name: `mon.name-${uuid}-${i}` }))
+            .map((mon) => saveMonitor(mon))
+        );
+
+        const resp = await supertest
+          .get(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=10&monitorQueryIds=${id2}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const resultMonitorIds = resp.body.monitors.map(({ id }: Partial<MonitorFields>) => id);
+        expect(resultMonitorIds.length).eql(1);
+        expect(resultMonitorIds).eql([id2]);
+      });
+
+      it('with multiple monitorQueryId filter', async () => {
+        const uuid = uuidv4();
+        const [_, { id: id2 }, { id: id3 }] = await Promise.all(
+          monitors
+            .map((mon, i) => ({ ...mon, name: `${mon.name}-${uuid}-${i}` }))
+            .map((monT) => saveMonitor(monT))
+        );
+
+        const resp = await supertest
+          .get(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=10&sortField=name.keyword&sortOrder=asc&monitorQueryIds=${id2}&monitorQueryIds=${id3}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const resultMonitorIds = resp.body.monitors.map(({ id }: Partial<MonitorFields>) => id);
+
+        expect(resultMonitorIds.length).eql(2);
+        expect(resultMonitorIds).eql([id2, id3]);
+      });
+
+      it('monitorQueryId respects custom_heartbeat_id while filtering', async () => {
+        const customHeartbeatId0 = 'custom-heartbeat-id-test-01';
+        const customHeartbeatId1 = 'custom-heartbeat-id-test-02';
+        await Promise.all(
+          [
+            {
+              ...monitors[0],
+              [ConfigKey.CUSTOM_HEARTBEAT_ID]: customHeartbeatId0,
+              [ConfigKey.NAME]: `NAME-${customHeartbeatId0}`,
+            },
+            {
+              ...monitors[1],
+              [ConfigKey.CUSTOM_HEARTBEAT_ID]: customHeartbeatId1,
+              [ConfigKey.NAME]: `NAME-${customHeartbeatId1}`,
+            },
+          ].map((monT) => saveMonitor(monT))
+        );
+
+        const resp = await supertest
+          .get(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=10&sortField=name.keyword&sortOrder=asc&monitorQueryIds=${customHeartbeatId0}&monitorQueryIds=${customHeartbeatId1}`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const resultMonitorIds = resp.body.monitors
+          .map(({ id }: Partial<MonitorFields>) => id)
+          .filter((id: string, index: number, arr: string[]) => arr.indexOf(id) === index); // Filter only unique
+        expect(resultMonitorIds.length).eql(2);
+        expect(resultMonitorIds).eql([customHeartbeatId0, customHeartbeatId1]);
+      });
+
+      it('gets monitors from all spaces', async () => {
+        const SPACE_ID = `test-space-${uuidv4()}`;
+        const SPACE_NAME = `test-space-name ${uuidv4()}`;
+        await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+        const spaceScopedPrivateLocation = await privateLocationTestService.addTestPrivateLocation(
+          SPACE_ID
+        );
+
+        const allMonitors = [...monitors, ...monitors];
+        for (const mon of allMonitors) {
+          await saveMonitor(
+            { ...mon, name: mon.name + Date.now(), locations: [spaceScopedPrivateLocation] },
+            SPACE_ID
+          );
+        }
+
+        const firstPageResp = await supertest
+          .get(`${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=1000`)
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+        const defaultSpaceMons = firstPageResp.body.monitors.filter(
+          ({ spaceId }: { spaceId: string }) => spaceId === 'default'
+        );
+        const testSpaceMons = firstPageResp.body.monitors.filter(
+          ({ spaceId }: { spaceId: string }) => spaceId === SPACE_ID
+        );
+
+        expect(defaultSpaceMons.length).to.eql(22);
+        expect(testSpaceMons.length).to.eql(0);
+
+        const res = await supertest
+          .get(
+            `${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=1000&showFromAllSpaces=true`
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(200);
+
+        const defaultSpaceMons1 = res.body.monitors.filter(
+          ({ spaceId }: { spaceId: string }) => spaceId === 'default'
+        );
+        const testSpaceMons1 = res.body.monitors.filter(
+          ({ spaceId }: { spaceId: string }) => spaceId === SPACE_ID
+        );
+
+        expect(defaultSpaceMons1.length).to.eql(22);
+        expect(testSpaceMons1.length).to.eql(8);
+      });
+    });
+
+    describe('get one monitor', () => {
+      it('should get by id', async () => {
+        const uuid = uuidv4();
+        const [{ id: id1 }] = await Promise.all(
+          monitors
+            .map((mon, i) => ({ ...mon, name: `${mon.name}-${uuid}-${i}` }))
+            .map((monT) => saveMonitor(monT))
+        );
+
+        const apiResponse = await monitorTestService.getMonitor(id1, { user: editorUser });
+
+        expect(apiResponse.body).eql(
+          omitMonitorKeys({
+            ...monitors[0],
+            [ConfigKey.MONITOR_QUERY_ID]: apiResponse.body.id,
+            [ConfigKey.CONFIG_ID]: apiResponse.body.id,
+            revision: 1,
+            locations: [privateLocation],
+            name: `${monitors[0].name}-${uuid}-0`,
+          })
+        );
+      });
+
+      it('should get by id with ui query param', async () => {
+        const uuid = uuidv4();
+        const [{ id: id1 }] = await Promise.all(
+          monitors
+            .map((mon, i) => ({ ...mon, name: `${mon.name}-${uuid}-${i}` }))
+            .map((monT) => saveMonitor(monT))
+        );
+
+        const apiResponse = await monitorTestService.getMonitor(id1, {
+          internal: true,
+          user: editorUser,
+        });
+
+        expect(apiResponse.body).eql(
+          omit(
+            {
+              ...monitors[0],
+              form_monitor_type: 'icmp',
+              revision: 1,
+              locations: [privateLocation],
+              name: `${monitors[0].name}-${uuid}-0`,
+              hosts: '192.33.22.111:3333',
+              hash: '',
+              journey_id: '',
+              max_attempts: 2,
+              labels: {},
+            },
+            ['config_id', 'id', 'form_monitor_type']
+          )
+        );
+      });
+
+      it('returns 404 if monitor id is not found', async () => {
+        const invalidMonitorId = 'invalid-id';
+        const expected404Message = `Monitor id ${invalidMonitorId} not found!`;
+
+        const getResponse = await supertest
+          .get(SYNTHETICS_API_URLS.GET_SYNTHETICS_MONITOR.replace('{monitorId}', invalidMonitorId))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(404);
+
+        expect(getResponse.body.message).eql(expected404Message);
+      });
+
+      it('validates param length', async () => {
+        const veryLargeMonId = new Array(1050).fill('1').join('');
+
+        await supertest
+          .get(SYNTHETICS_API_URLS.GET_SYNTHETICS_MONITOR.replace('{monitorId}', veryLargeMonId))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .expect(400);
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor_project.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor_project.ts
new file mode 100644
index 0000000000000..0678731a4202e
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/get_monitor_project.ts
@@ -0,0 +1,741 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import type SuperTest from 'supertest';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  LegacyProjectMonitorsRequest,
+  ProjectMonitor,
+  ProjectMonitorMetaData,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('GetProjectMonitors', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const samlAuth = getService('samlAuth');
+
+    let projectMonitors: LegacyProjectMonitorsRequest;
+    let httpProjectMonitors: LegacyProjectMonitorsRequest;
+    let tcpProjectMonitors: LegacyProjectMonitorsRequest;
+    let icmpProjectMonitors: LegacyProjectMonitorsRequest;
+    let testPolicyId = '';
+    let editorUser: RoleCredentials;
+    let testPrivateLocations: PrivateLocation[] = [];
+
+    const testPrivateLocationsService = new PrivateLocationTestService(getService);
+
+    const setUniqueIds = (
+      request: LegacyProjectMonitorsRequest,
+      privateLocations: PrivateLocation[] = []
+    ) => {
+      return {
+        ...request,
+        monitors: request.monitors.map((monitor) => ({
+          ...monitor,
+          id: uuidv4(),
+          locations: [],
+          privateLocations: privateLocations.map((location) => location.label),
+        })),
+      };
+    };
+
+    before(async () => {
+      await testPrivateLocationsService.installSyntheticsPackage();
+
+      const testPolicyName = 'Fleet test server policy' + Date.now();
+      const apiResponse = await testPrivateLocationsService.addFleetPolicy(testPolicyName);
+      testPolicyId = apiResponse.body.item.id;
+      testPrivateLocations = await testPrivateLocationsService.setTestLocations([testPolicyId]);
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+    });
+
+    beforeEach(() => {
+      projectMonitors = setUniqueIds(
+        getFixtureJson('project_browser_monitor'),
+        testPrivateLocations
+      );
+      httpProjectMonitors = setUniqueIds(
+        getFixtureJson('project_http_monitor'),
+        testPrivateLocations
+      );
+      tcpProjectMonitors = setUniqueIds(
+        getFixtureJson('project_tcp_monitor'),
+        testPrivateLocations
+      );
+      icmpProjectMonitors = setUniqueIds(
+        getFixtureJson('project_icmp_monitor'),
+        testPrivateLocations
+      );
+    });
+
+    it('project monitors - fetches all monitors - browser', async () => {
+      const monitors = [];
+      const project = 'test-brower-suite';
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...projectMonitors.monitors[0],
+          id: `test browser id ${i}`,
+          name: `test name ${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+
+        const firstPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({ per_page: 500 })
+          .send()
+          .expect(200);
+
+        const { monitors: firstPageMonitors, total, after_key: afterKey } = firstPageResponse.body;
+        expect(firstPageMonitors.length).to.eql(500);
+        expect(total).to.eql(600);
+        expect(afterKey).to.eql('test browser id 548');
+
+        const secondPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            search_after: afterKey,
+            per_page: 500,
+          })
+          .send()
+          .expect(200);
+        const { monitors: secondPageMonitors } = secondPageResponse.body;
+        expect(secondPageMonitors.length).to.eql(100);
+        checkFields([...firstPageMonitors, ...secondPageMonitors], monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - fetches all monitors - http', async () => {
+      const monitors = [];
+      const project = 'test-http-suite';
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...httpProjectMonitors.monitors[1],
+          id: `test http id ${i}`,
+          name: `test name ${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+
+        const firstPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({ per_page: 500 })
+          .send()
+          .expect(200);
+
+        const {
+          monitors: firstPageProjectMonitors,
+          after_key: afterKey,
+          total,
+        } = firstPageResponse.body;
+        expect(firstPageProjectMonitors.length).to.eql(500);
+        expect(total).to.eql(600);
+        expect(afterKey).to.eql('test http id 548');
+
+        const secondPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            search_after: afterKey,
+            per_page: 500,
+          })
+          .send()
+          .expect(200);
+        const { monitors: secondPageProjectMonitors } = secondPageResponse.body;
+        expect(secondPageProjectMonitors.length).to.eql(100);
+        checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - fetches all monitors - tcp', async () => {
+      const monitors = [];
+      const project = 'test-tcp-suite';
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...tcpProjectMonitors.monitors[0],
+          id: `test tcp id ${i}`,
+          name: `test name ${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+
+        const firstPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .query({ per_page: 500 })
+          .set(samlAuth.getInternalRequestHeader())
+          .send()
+          .expect(200);
+
+        const {
+          monitors: firstPageProjectMonitors,
+          after_key: afterKey,
+          total,
+        } = firstPageResponse.body;
+        expect(firstPageProjectMonitors.length).to.eql(500);
+        expect(total).to.eql(600);
+        expect(afterKey).to.eql('test tcp id 548');
+
+        const secondPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            search_after: afterKey,
+            per_page: 500,
+          })
+          .send()
+          .expect(200);
+        const { monitors: secondPageProjectMonitors } = secondPageResponse.body;
+        expect(secondPageProjectMonitors.length).to.eql(100);
+        checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - fetches all monitors - icmp', async () => {
+      const monitors = [];
+      const project = 'test-icmp-suite';
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...icmpProjectMonitors.monitors[0],
+          id: `test icmp id ${i}`,
+          name: `test name ${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+        const firstPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .query({ per_page: 500 })
+          .set(samlAuth.getInternalRequestHeader())
+          .send()
+          .expect(200);
+
+        const {
+          monitors: firstPageProjectMonitors,
+          after_key: afterKey,
+          total,
+        } = firstPageResponse.body;
+        expect(firstPageProjectMonitors.length).to.eql(500);
+        expect(total).to.eql(600);
+        expect(afterKey).to.eql('test icmp id 548');
+
+        const secondPageResponse = await supertest
+          .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            search_after: afterKey,
+            per_page: 500,
+          })
+          .send()
+          .expect(200);
+        const { monitors: secondPageProjectMonitors } = secondPageResponse.body;
+        expect(secondPageProjectMonitors.length).to.eql(100);
+
+        checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - handles url ecoded project names', async () => {
+      const monitors = [];
+      const projectName = 'Test project';
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...icmpProjectMonitors.monitors[0],
+          id: `test url id ${i}`,
+          name: `test name ${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              projectName
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              projectName
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+              '{projectName}',
+              projectName
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+
+        const firstPageResponse = await supertest
+          .get(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace(
+              '{projectName}',
+              encodeURI(projectName)
+            )
+          )
+          .query({ per_page: 500 })
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send()
+          .expect(200);
+
+        const {
+          monitors: firstPageProjectMonitors,
+          after_key: afterKey,
+          total,
+        } = firstPageResponse.body;
+        expect(firstPageProjectMonitors.length).to.eql(500);
+        expect(total).to.eql(600);
+        expect(afterKey).to.eql('test url id 548');
+
+        const secondPageResponse = await supertest
+          .get(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace(
+              '{projectName}',
+              encodeURI(projectName)
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .query({
+            search_after: afterKey,
+            per_page: 500,
+          })
+          .send()
+          .expect(200);
+        const { monitors: secondPageProjectMonitors } = secondPageResponse.body;
+        expect(secondPageProjectMonitors.length).to.eql(100);
+
+        checkFields([...firstPageProjectMonitors, ...secondPageProjectMonitors], monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              encodeURI(projectName)
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              encodeURI(projectName)
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace(
+              '{projectName}',
+              encodeURI(projectName)
+            )
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+
+    it('project monitors - handles per_page parameter', async () => {
+      const monitors = [];
+      const project = 'test-suite';
+      const perPage = 250;
+      for (let i = 0; i < 600; i++) {
+        monitors.push({
+          ...icmpProjectMonitors.monitors[0],
+          id: `test-id-${i}`,
+          name: `test-name-${i}`,
+        });
+      }
+
+      try {
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(0, 250),
+          })
+          .expect(200);
+
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(250, 500),
+          })
+          .expect(200);
+        await supertest
+          .put(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({
+            monitors: monitors.slice(500, 600),
+          })
+          .expect(200);
+
+        let count = Number.MAX_VALUE;
+        let afterId;
+        const fullResponse: ProjectMonitorMetaData[] = [];
+        let page = 1;
+        while (count >= 250) {
+          const response: SuperTest.Response = await supertest
+            .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
+            .set(editorUser.apiKeyHeader)
+            .set(samlAuth.getInternalRequestHeader())
+            .query({
+              per_page: perPage,
+              search_after: afterId,
+            })
+            .send()
+            .expect(200);
+
+          const { monitors: monitorsResponse, after_key: afterKey, total } = response.body;
+          expect(total).to.eql(600);
+          count = monitorsResponse.length;
+          fullResponse.push(...monitorsResponse);
+          if (page < 3) {
+            expect(count).to.eql(perPage);
+          } else {
+            expect(count).to.eql(100);
+          }
+          page++;
+
+          afterId = afterKey;
+        }
+        // expect(fullResponse.length).to.eql(600);
+        // checkFields(fullResponse, monitors);
+      } finally {
+        const monitorsToDelete = monitors.map((monitor) => monitor.id);
+
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(0, 250) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(250, 500) })
+          .expect(200);
+        await supertest
+          .delete(
+            SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_DELETE.replace('{projectName}', project)
+          )
+          .set(editorUser.apiKeyHeader)
+          .set(samlAuth.getInternalRequestHeader())
+          .send({ monitors: monitorsToDelete.slice(500, 600) })
+          .expect(200);
+      }
+    });
+  });
+}
+
+const checkFields = (monitorMetaData: ProjectMonitorMetaData[], monitors: ProjectMonitor[]) => {
+  monitors.forEach((monitor) => {
+    const configIsCorrect = monitorMetaData.some((ndjson: Record<string, unknown>) => {
+      return ndjson.journey_id === monitor.id && ndjson.hash === monitor.hash;
+    });
+    expect(configIsCorrect).to.eql(true);
+  });
+};
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/get_fixture_json.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/get_fixture_json.ts
new file mode 100644
index 0000000000000..9cc1640b7a583
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/get_fixture_json.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import fs from 'fs';
+import { join } from 'path';
+
+const fixturesDir = join(__dirname, '..', 'fixtures');
+
+export function getFixtureJson(fixtureName: string) {
+  try {
+    const fixturePath = join(fixturesDir, `${fixtureName}.json`);
+    const fileContents = fs.readFileSync(fixturePath, 'utf8');
+    return JSON.parse(fileContents);
+  } catch (e) {
+    return {};
+  }
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/monitor.ts
new file mode 100644
index 0000000000000..8c10fa78d9834
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/helpers/monitor.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { omit } from 'lodash';
+
+export function omitResponseTimestamps(monitor: object) {
+  return omit(monitor, ['created_at', 'updated_at']);
+}
+
+export function omitEmptyValues(monitor: object) {
+  const { url, ...rest } = omit(monitor, ['created_at', 'updated_at']) as any;
+
+  return {
+    ...rest,
+    ...(url ? { url } : {}),
+  };
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/index.ts
new file mode 100644
index 0000000000000..c15f73cf4e6db
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/index.ts
@@ -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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) {
+  describe('SyntheticsAPITests', () => {
+    loadTestFile(require.resolve('./create_monitor'));
+    loadTestFile(require.resolve('./create_monitor_private_location'));
+    loadTestFile(require.resolve('./create_monitor_project'));
+    loadTestFile(require.resolve('./create_monitor_project_private_location'));
+    loadTestFile(require.resolve('./create_monitor_public_api'));
+    loadTestFile(require.resolve('./create_update_params'));
+    loadTestFile(require.resolve('./delete_monitor_project'));
+    loadTestFile(require.resolve('./delete_monitor'));
+    loadTestFile(require.resolve('./edit_monitor'));
+    loadTestFile(require.resolve('./edit_monitor_public_api'));
+    loadTestFile(require.resolve('./enable_default_alerting'));
+    loadTestFile(require.resolve('./get_filters'));
+    loadTestFile(require.resolve('./get_monitor_project'));
+    loadTestFile(require.resolve('./get_monitor'));
+    loadTestFile(require.resolve('./synthetics_enablement'));
+    loadTestFile(require.resolve('./inspect_monitor'));
+    loadTestFile(require.resolve('./suggestions.ts'));
+    loadTestFile(require.resolve('./sync_global_params'));
+    loadTestFile(require.resolve('./test_now_monitor'));
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/inspect_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/inspect_monitor.ts
new file mode 100644
index 0000000000000..99788e2b0d0fc
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/inspect_monitor.ts
@@ -0,0 +1,246 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import rawExpect from 'expect';
+import expect from '@kbn/expect';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('inspectSyntheticsMonitor', function () {
+    const supertest = getService('supertestWithoutAuth');
+
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    let _monitors: MonitorFields[];
+    let editorUser: RoleCredentials;
+    let adminUser: RoleCredentials;
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      await kibanaServer.savedObjects.clean({ types: ['synthetics-param'] });
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      adminUser = await samlAuth.createM2mApiKeyWithRoleScope('admin');
+      await testPrivateLocations.installSyntheticsPackage();
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      _monitors = [getFixtureJson('http_monitor'), getFixtureJson('inspect_browser_monitor')];
+    });
+
+    // tests public locations which fails in MKI
+    it.skip('inspect http monitor', async () => {
+      const apiResponse = await monitorTestService.inspectMonitor(adminUser, {
+        ..._monitors[0],
+        locations: [
+          {
+            id: 'dev',
+            label: 'Dev Service',
+            isServiceManaged: true,
+          },
+        ],
+      });
+
+      rawExpect(apiResponse).toEqual({
+        result: {
+          publicConfigs: [
+            rawExpect.objectContaining({
+              monitors: [
+                {
+                  type: 'http',
+                  schedule: '@every 5m',
+                  enabled: true,
+                  data_stream: { namespace: 'testnamespace' },
+                  streams: [
+                    {
+                      data_stream: { dataset: 'http', type: 'synthetics' },
+                      type: 'http',
+                      enabled: true,
+                      schedule: '@every 5m',
+                      tags: ['tag1', 'tag2'],
+                      timeout: '180s',
+                      name: 'test-monitor-name',
+                      namespace: 'testnamespace',
+                      origin: 'ui',
+                      urls: 'https://nextjs-test-synthetics.vercel.app/api/users',
+                      max_redirects: '3',
+                      max_attempts: 2,
+                      password: 'test',
+                      proxy_url: 'http://proxy.com',
+                      'response.include_body': 'never',
+                      'response.include_headers': true,
+                      'check.response.status': ['200', '201'],
+                      'check.request.body': 'testValue',
+                      'check.request.headers': { sampleHeader: 'sampleHeaderValue' },
+                      username: 'test-username',
+                      mode: 'any',
+                      'response.include_body_max_bytes': '1024',
+                      ipv4: true,
+                      ipv6: true,
+                      fields: {
+                        meta: { space_id: 'default' },
+                      },
+                      fields_under_root: true,
+                    },
+                  ],
+                },
+              ],
+              output: { hosts: [] },
+            }),
+          ],
+          privateConfig: null,
+        },
+        decodedCode: '',
+      });
+    });
+
+    // tests public locations which fails in MKI
+    it.skip('inspect project browser monitor', async () => {
+      const apiResponse = await monitorTestService.inspectMonitor(editorUser, {
+        ..._monitors[1],
+        params: JSON.stringify({
+          username: 'elastic',
+          password: 'changeme',
+        }),
+        locations: [
+          {
+            id: 'dev',
+            label: 'Dev Service',
+            isServiceManaged: true,
+          },
+        ],
+      });
+      rawExpect(apiResponse).toEqual({
+        result: {
+          publicConfigs: [
+            rawExpect.objectContaining({
+              monitors: [
+                {
+                  type: 'browser',
+                  schedule: '@every 10m',
+                  enabled: true,
+                  data_stream: { namespace: 'default' },
+                  streams: [
+                    {
+                      data_stream: { dataset: 'browser', type: 'synthetics' },
+                      type: 'browser',
+                      enabled: true,
+                      schedule: '@every 10m',
+                      name: 'check if title is present',
+                      namespace: 'default',
+                      origin: 'project',
+                      params: {
+                        username: '"********"',
+                        password: '"********"',
+                      },
+                      playwright_options: { headless: true, chromiumSandbox: false },
+                      'source.project.content':
+                        'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
+                      screenshots: 'on',
+                      'filter_journeys.match': 'check if title is present',
+                      ignore_https_errors: false,
+                      throttling: { download: 5, upload: 3, latency: 20 },
+                      original_space: 'default',
+                      fields: {
+                        meta: { space_id: 'default' },
+                        'monitor.project.name': 'test-project-cb47c83a-45e7-416a-9301-cb476b5bff01',
+                        'monitor.project.id': 'test-project-cb47c83a-45e7-416a-9301-cb476b5bff01',
+                      },
+                      fields_under_root: true,
+                      max_attempts: 2,
+                    },
+                  ],
+                },
+              ],
+              license_level: rawExpect.any(String),
+              cloud_id: 'ftr_fake_cloud_id',
+              output: { hosts: [] },
+            }),
+          ],
+          privateConfig: null,
+        },
+        decodedCode:
+          '// asset:/Users/vigneshh/elastic/synthetics/examples/todos/basic.journey.ts\nimport { journey, step, expect } from "@elastic/synthetics";\njourney("check if title is present", ({ page, params }) => {\n  step("launch app", async () => {\n    await page.goto(params.url);\n  });\n  step("assert title", async () => {\n    const header = await page.$("h1");\n    expect(await header.textContent()).toBe("todos");\n  });\n});\n',
+      });
+    });
+
+    it('inspect http monitor in private location', async () => {
+      const location = await testPrivateLocations.addTestPrivateLocation();
+      const apiResponse = await monitorTestService.inspectMonitor(editorUser, {
+        ..._monitors[0],
+        locations: [
+          {
+            id: location.id,
+            label: location.label,
+            isServiceManaged: false,
+          },
+        ],
+      });
+
+      const privateConfig = apiResponse.result.privateConfig!;
+
+      const enabledStream = privateConfig.inputs
+        .find((input) => input.enabled)
+        ?.streams.find((stream) => stream.enabled);
+
+      const compiledStream = enabledStream?.compiled_stream;
+
+      delete compiledStream.id;
+      delete compiledStream.processors[0].add_fields.fields.config_id;
+
+      expect(enabledStream?.compiled_stream).eql({
+        __ui: { is_tls_enabled: false },
+        type: 'http',
+        name: 'test-monitor-name',
+        origin: 'ui',
+        'run_from.id': location.id,
+        'run_from.geo.name': location.label,
+        enabled: true,
+        urls: 'https://nextjs-test-synthetics.vercel.app/api/users',
+        schedule: '@every 5m',
+        timeout: '180s',
+        max_redirects: 3,
+        max_attempts: 2,
+        proxy_url: 'http://proxy.com',
+        tags: ['tag1', 'tag2'],
+        username: 'test-username',
+        password: 'test',
+        'response.include_headers': true,
+        'response.include_body': 'never',
+        'response.include_body_max_bytes': 1024,
+        'check.request.method': null,
+        'check.request.headers': { sampleHeader: 'sampleHeaderValue' },
+        'check.request.body': 'testValue',
+        'check.response.status': ['200', '201'],
+        mode: 'any',
+        ipv4: true,
+        ipv6: true,
+        processors: [
+          {
+            add_fields: {
+              target: '',
+              fields: {
+                meta: { space_id: 'default' },
+                'monitor.fleet_managed': true,
+              },
+            },
+          },
+        ],
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_policy.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_policy.ts
new file mode 100644
index 0000000000000..338d666d35517
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_policy.ts
@@ -0,0 +1,575 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import expect from 'expect';
+import { omit, sortBy } from 'lodash';
+import { PackagePolicy, PackagePolicyConfigRecord } from '@kbn/fleet-plugin/common';
+import { INSTALLED_VERSION } from '../../../../services/synthetics_private_location';
+import { commonVars } from './test_project_monitor_policy';
+
+interface PolicyProps {
+  name?: string;
+  id: string;
+  configId?: string;
+  projectId?: string;
+  location: { name?: string; id?: string };
+  namespace?: string;
+  isTLSEnabled?: boolean;
+  proxyUrl?: string;
+  params?: Record<string, any>;
+  isBrowser?: boolean;
+  spaceId?: string;
+}
+
+export const getTestSyntheticsPolicy = (props: PolicyProps): PackagePolicy => {
+  const { namespace } = props;
+  return {
+    id: '2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+    version: 'WzE2MjYsMV0=',
+    name: 'test-monitor-name-Test private location 0-default',
+    namespace: namespace ?? 'testnamespace',
+    package: { name: 'synthetics', title: 'Elastic Synthetics', version: INSTALLED_VERSION },
+    enabled: true,
+    policy_id: '5347cd10-0368-11ed-8df7-a7424c6f5167',
+    policy_ids: ['5347cd10-0368-11ed-8df7-a7424c6f5167'],
+    inputs: [
+      getHttpInput(props),
+      {
+        type: 'synthetics/tcp',
+        policy_template: 'synthetics',
+        enabled: false,
+        streams: [
+          {
+            enabled: false,
+            data_stream: {
+              type: 'synthetics',
+              dataset: 'tcp',
+            },
+            vars: {
+              __ui: { type: 'yaml' },
+              enabled: { value: true, type: 'bool' },
+              type: { value: 'tcp', type: 'text' },
+              name: { type: 'text' },
+              schedule: { value: '"@every 3m"', type: 'text' },
+              hosts: { type: 'text' },
+              'service.name': { type: 'text' },
+              timeout: { type: 'text' },
+              proxy_url: { type: 'text' },
+              processors: { type: 'yaml' },
+              proxy_use_local_resolver: { value: false, type: 'bool' },
+              tags: { type: 'yaml' },
+              'check.send': { type: 'text' },
+              'check.receive': { type: 'text' },
+              'ssl.certificate_authorities': { type: 'yaml' },
+              'ssl.certificate': { type: 'yaml' },
+              'ssl.key': { type: 'yaml' },
+              'ssl.key_passphrase': { type: 'text' },
+              'ssl.verification_mode': { type: 'text' },
+              'ssl.supported_protocols': { type: 'yaml' },
+              location_name: { value: 'Fleet managed', type: 'text' },
+              id: { type: 'text' },
+              origin: { type: 'text' },
+              ipv4: { type: 'bool', value: true },
+              ipv6: { type: 'bool', value: true },
+              mode: { type: 'text' },
+            },
+            id: 'synthetics/tcp-tcp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+          },
+        ],
+      },
+      {
+        type: 'synthetics/icmp',
+        policy_template: 'synthetics',
+        enabled: false,
+        streams: [
+          {
+            enabled: false,
+            data_stream: {
+              type: 'synthetics',
+              dataset: 'icmp',
+            },
+            vars: {
+              __ui: { type: 'yaml' },
+              enabled: { value: true, type: 'bool' },
+              type: { value: 'icmp', type: 'text' },
+              name: { type: 'text' },
+              schedule: { value: '"@every 3m"', type: 'text' },
+              wait: { value: '1s', type: 'text' },
+              hosts: { type: 'text' },
+              'service.name': { type: 'text' },
+              timeout: { type: 'text' },
+              tags: { type: 'yaml' },
+              location_name: { value: 'Fleet managed', type: 'text' },
+              id: { type: 'text' },
+              origin: { type: 'text' },
+              ipv4: { type: 'bool', value: true },
+              ipv6: { type: 'bool', value: true },
+              mode: { type: 'text' },
+            },
+            id: 'synthetics/icmp-icmp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+          },
+        ],
+      },
+      getBrowserInput(props),
+    ],
+    is_managed: true,
+    revision: 1,
+    created_at: '2022-08-23T14:09:17.176Z',
+    created_by: 'system',
+    updated_at: '2022-08-23T14:09:17.176Z',
+    updated_by: 'system',
+  };
+};
+
+export const getHttpInput = ({
+  projectId,
+  id,
+  location,
+  proxyUrl,
+  isTLSEnabled,
+  isBrowser,
+  spaceId,
+  namespace,
+  name = 'check if title is present-Test private location 0',
+}: PolicyProps) => {
+  const enabled = !isBrowser;
+  const baseVars: PackagePolicyConfigRecord = {
+    __ui: { type: 'yaml' },
+    enabled: { value: true, type: 'bool' },
+    type: { value: 'http', type: 'text' },
+    name: { type: 'text' },
+    schedule: { value: '"@every 3m"', type: 'text' },
+    urls: { type: 'text' },
+    'service.name': { type: 'text' },
+    timeout: { type: 'text' },
+    max_redirects: { type: 'integer' },
+    proxy_url: { type: 'text' },
+    processors: { type: 'yaml' },
+    proxy_headers: { type: 'yaml' },
+    tags: { type: 'yaml' },
+    username: { type: 'text' },
+    password: { type: 'password' },
+    'response.include_headers': { type: 'bool' },
+    'response.include_body': { type: 'text' },
+    'response.include_body_max_bytes': { type: 'text' },
+    'check.request.method': { type: 'text' },
+    'check.request.headers': { type: 'yaml' },
+    'check.request.body': { type: 'yaml' },
+    'check.response.status': { type: 'yaml' },
+    'check.response.headers': { type: 'yaml' },
+    'check.response.body.positive': { type: 'yaml' },
+    'check.response.body.negative': { type: 'yaml' },
+    'check.response.json': { type: 'yaml' },
+    'ssl.certificate_authorities': { type: 'yaml' },
+    'ssl.certificate': { type: 'yaml' },
+    'ssl.key': { type: 'yaml' },
+    'ssl.key_passphrase': { type: 'text' },
+    'ssl.verification_mode': { type: 'text' },
+    'ssl.supported_protocols': { type: 'yaml' },
+    location_id: { value: 'fleet_managed', type: 'text' },
+    location_name: { value: 'Fleet managed', type: 'text' },
+    ...commonVars,
+    id: { type: 'text' },
+    origin: { type: 'text' },
+    ipv4: { type: 'bool', value: true },
+    ipv6: { type: 'bool', value: true },
+    mode: { type: 'text' },
+  };
+
+  const enabledVars = {
+    __ui: {
+      value: `{"is_tls_enabled":${isTLSEnabled || false}}`,
+      type: 'yaml',
+    },
+    enabled: { value: true, type: 'bool' },
+    type: { value: 'http', type: 'text' },
+    name: { value: JSON.stringify(name), type: 'text' },
+    schedule: { value: '"@every 5m"', type: 'text' },
+    urls: { value: '"https://nextjs-test-synthetics.vercel.app/api/users"', type: 'text' },
+    'service.name': { value: null, type: 'text' },
+    timeout: { value: '180s', type: 'text' },
+    max_redirects: { value: '3', type: 'integer' },
+    processors: {
+      type: 'yaml',
+      value: JSON.stringify([
+        {
+          add_fields: {
+            fields: {
+              'monitor.fleet_managed': true,
+              config_id: id,
+              meta: { space_id: spaceId ?? 'default' },
+              'monitor.project.name': projectId,
+              'monitor.project.id': projectId,
+            },
+            target: '',
+          },
+        },
+      ]),
+    },
+    proxy_url: { value: proxyUrl ?? '"http://proxy.com"', type: 'text' },
+    proxy_headers: { value: null, type: 'yaml' },
+    tags: { value: '["tag1","tag2"]', type: 'yaml' },
+    username: { value: '"test-username"', type: 'text' },
+    password: { value: '"test"', type: 'password' },
+    'response.include_headers': { value: true, type: 'bool' },
+    'response.include_body': { value: 'never', type: 'text' },
+    'response.include_body_max_bytes': { value: '1024', type: 'text' },
+    'check.request.method': { value: '', type: 'text' },
+    'check.request.headers': {
+      value: '{"sampleHeader":"sampleHeaderValue"}',
+      type: 'yaml',
+    },
+    'check.request.body': { value: '"testValue"', type: 'yaml' },
+    'check.response.status': { value: '["200","201"]', type: 'yaml' },
+    'check.response.headers': { value: null, type: 'yaml' },
+    'check.response.body.positive': { value: null, type: 'yaml' },
+    'check.response.body.negative': { value: null, type: 'yaml' },
+    'check.response.json': { value: null, type: 'yaml' },
+    'ssl.certificate_authorities': {
+      value: isTLSEnabled ? '"t.string"' : null,
+      type: 'yaml',
+    },
+    'ssl.certificate': { value: isTLSEnabled ? '"t.string"' : null, type: 'yaml' },
+    'ssl.key': { value: isTLSEnabled ? '"t.string"' : null, type: 'yaml' },
+    'ssl.key_passphrase': { value: isTLSEnabled ? 't.string' : null, type: 'text' },
+    'ssl.verification_mode': { value: isTLSEnabled ? 'certificate' : null, type: 'text' },
+    'ssl.supported_protocols': {
+      value: isTLSEnabled ? '["TLSv1.1","TLSv1.2"]' : null,
+      type: 'yaml',
+    },
+    location_id: {
+      type: 'text',
+      value: location.id ?? 'aaa3c150-f94d-11ed-9895-d36d5472fafd',
+    },
+    location_name: {
+      value: JSON.stringify(location.name) ?? '"Test private location 0"',
+      type: 'text',
+    },
+    ...commonVars,
+    id: { value: JSON.stringify(id), type: 'text' },
+    origin: { value: projectId ? 'project' : 'ui', type: 'text' },
+    ipv4: { type: 'bool', value: true },
+    ipv6: { type: 'bool', value: true },
+    mode: { type: 'text', value: 'any' },
+  };
+
+  const compiledHttpStream = {
+    __ui: {
+      is_tls_enabled: isTLSEnabled || false,
+    },
+    type: 'http',
+    name,
+    id,
+    origin: projectId ? 'project' : 'ui',
+    enabled: true,
+    urls: 'https://nextjs-test-synthetics.vercel.app/api/users',
+    schedule: '@every 5m',
+    timeout: '180s',
+    max_redirects: 3,
+    max_attempts: 2,
+    proxy_url: proxyUrl ?? 'http://proxy.com',
+    tags: ['tag1', 'tag2'],
+    username: 'test-username',
+    password: 'test',
+    'run_from.geo.name': location?.name ?? 'Test private location 0',
+    'run_from.id': location?.id ?? 'Test private location 0',
+    'response.include_headers': true,
+    'response.include_body': 'never',
+    'response.include_body_max_bytes': 1024,
+    'check.request.method': null,
+    'check.request.headers': { sampleHeader: 'sampleHeaderValue' },
+    'check.request.body': 'testValue',
+    'check.response.status': ['200', '201'],
+    ipv4: true,
+    ipv6: true,
+    mode: 'any',
+    ...(isTLSEnabled
+      ? {
+          'ssl.certificate': 't.string',
+          'ssl.certificate_authorities': 't.string',
+          'ssl.key': 't.string',
+          'ssl.key_passphrase': 't.string',
+          'ssl.verification_mode': 'certificate',
+          'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2'],
+        }
+      : {}),
+    processors: [
+      {
+        add_fields: {
+          fields: {
+            config_id: id,
+            meta: {
+              space_id: spaceId ?? 'default',
+            },
+            'monitor.fleet_managed': true,
+            ...(projectId
+              ? { 'monitor.project.id': projectId, 'monitor.project.name': projectId }
+              : {}),
+          },
+          target: '',
+        },
+      },
+    ],
+  };
+
+  return {
+    type: 'synthetics/http',
+    policy_template: 'synthetics',
+    enabled,
+    streams: [
+      {
+        enabled,
+        data_stream: {
+          type: 'synthetics',
+          dataset: 'http',
+          ...(enabled
+            ? {
+                elasticsearch: {
+                  privileges: {
+                    indices: ['auto_configure', 'create_doc', 'read'],
+                  },
+                },
+              }
+            : {}),
+        },
+        vars: enabled ? enabledVars : baseVars,
+        id: 'synthetics/http-http-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+        ...(enabled ? { compiled_stream: compiledHttpStream } : {}),
+      },
+    ],
+  };
+};
+
+export const getBrowserInput = ({ id, params, isBrowser, projectId }: PolicyProps) => {
+  const compiledBrowser = isBrowser
+    ? {
+        __ui: {
+          script_source: { is_generated_script: false, file_name: '' },
+          is_tls_enabled: false,
+        },
+        type: 'browser',
+        name: 'Test HTTP Monitor 03',
+        id,
+        origin: 'ui',
+        'run_from.id': 'Test private location 0',
+        'run_from.geo.name': 'Test private location 0',
+        enabled: true,
+        schedule: '@every 3m',
+        timeout: '16s',
+        throttling: { download: 5, upload: 3, latency: 20 },
+        tags: ['cookie-test', 'browser'],
+        'source.inline.script':
+          'step("Visit /users api route", async () => {\\n  const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\n  expect(response.status()).toEqual(200);\\n});',
+        ...(params ? { params } : {}),
+        screenshots: 'on',
+        processors: [
+          {
+            add_fields: {
+              target: '',
+              fields: {
+                'monitor.fleet_managed': true,
+                config_id: id,
+              },
+            },
+          },
+        ],
+      }
+    : {
+        __ui: null,
+        type: 'browser',
+        name: null,
+        enabled: true,
+        schedule: '@every 3m',
+        'run_from.id': 'Fleet managed',
+        'run_from.geo.name': 'Fleet managed',
+        timeout: null,
+        throttling: null,
+        processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+      };
+
+  const browserVars = isBrowser
+    ? {
+        __ui: {
+          value:
+            '{"script_source":{"is_generated_script":false,"file_name":""},"is_tls_enabled":false}',
+          type: 'yaml',
+        },
+        enabled: { value: true, type: 'bool' },
+        type: { value: 'browser', type: 'text' },
+        name: { value: 'Test HTTP Monitor 03', type: 'text' },
+        schedule: { value: '"@every 3m"', type: 'text' },
+        'service.name': { value: '', type: 'text' },
+        timeout: { value: '16s', type: 'text' },
+        tags: { value: '["cookie-test","browser"]', type: 'yaml' },
+        'source.zip_url.url': { type: 'text' },
+        'source.zip_url.username': { type: 'text' },
+        'source.zip_url.folder': { type: 'text' },
+        'source.zip_url.password': { type: 'password' },
+        'source.inline.script': {
+          value:
+            '"step(\\"Visit /users api route\\", async () => {\\\\n  const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\\\n  expect(response.status()).toEqual(200);\\\\n});"',
+          type: 'yaml',
+        },
+        'source.project.content': { value: '', type: 'text' },
+        params: { value: params ? JSON.stringify(params) : '', type: 'yaml' },
+        playwright_options: { value: '', type: 'yaml' },
+        screenshots: { value: 'on', type: 'text' },
+        synthetics_args: { value: null, type: 'text' },
+        ignore_https_errors: { value: false, type: 'bool' },
+        'throttling.config': {
+          value: JSON.stringify({ download: 5, upload: 3, latency: 20 }),
+          type: 'text',
+        },
+        'filter_journeys.tags': { value: null, type: 'yaml' },
+        'filter_journeys.match': { value: null, type: 'text' },
+        'source.zip_url.ssl.certificate_authorities': { type: 'yaml' },
+        'source.zip_url.ssl.certificate': { type: 'yaml' },
+        'source.zip_url.ssl.key': { type: 'yaml' },
+        'source.zip_url.ssl.key_passphrase': { type: 'text' },
+        'source.zip_url.ssl.verification_mode': { type: 'text' },
+        'source.zip_url.ssl.supported_protocols': { type: 'yaml' },
+        'source.zip_url.proxy_url': { type: 'text' },
+        location_id: {
+          type: 'text',
+          value: 'fleet_managed',
+        },
+        location_name: { value: 'Test private location 0', type: 'text' },
+        id: { value: id, type: 'text' },
+        origin: { value: 'ui', type: 'text' },
+      }
+    : {
+        __ui: { type: 'yaml' },
+        enabled: { value: true, type: 'bool' },
+        type: { value: 'browser', type: 'text' },
+        name: { type: 'text' },
+        schedule: { value: '"@every 3m"', type: 'text' },
+        'service.name': { type: 'text' },
+        timeout: { type: 'text' },
+        tags: { type: 'yaml' },
+        'source.zip_url.url': { type: 'text' },
+        'source.zip_url.username': { type: 'text' },
+        'source.zip_url.folder': { type: 'text' },
+        'source.zip_url.password': { type: 'password' },
+        'source.inline.script': { type: 'yaml' },
+        'source.project.content': { type: 'text' },
+        params: { type: 'yaml' },
+        playwright_options: { type: 'yaml' },
+        screenshots: { type: 'text' },
+        synthetics_args: { type: 'text' },
+        ignore_https_errors: { type: 'bool' },
+        'throttling.config': { type: 'text' },
+        'filter_journeys.tags': { type: 'yaml' },
+        'filter_journeys.match': { type: 'text' },
+        'source.zip_url.ssl.certificate_authorities': { type: 'yaml' },
+        'source.zip_url.ssl.certificate': { type: 'yaml' },
+        'source.zip_url.ssl.key': { type: 'yaml' },
+        'source.zip_url.ssl.key_passphrase': { type: 'text' },
+        'source.zip_url.ssl.verification_mode': { type: 'text' },
+        'source.zip_url.ssl.supported_protocols': { type: 'yaml' },
+        'source.zip_url.proxy_url': { type: 'text' },
+        location_name: { value: 'Fleet managed', type: 'text' },
+        location_id: { value: 'Fleet managed', type: 'text' },
+        id: { type: 'text' },
+        origin: { type: 'text' },
+      };
+
+  return {
+    type: 'synthetics/browser',
+    policy_template: 'synthetics',
+    enabled: false,
+    streams: [
+      {
+        enabled: true,
+        data_stream: getDataStream('browser'),
+        vars: browserVars,
+        id: 'synthetics/browser-browser-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+        compiled_stream: compiledBrowser,
+      },
+      {
+        enabled: true,
+        data_stream: getDataStream('browser.network'),
+        id: 'synthetics/browser-browser.network-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+        compiled_stream: {
+          processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+        },
+      },
+      {
+        enabled: true,
+        data_stream: getDataStream('browser.screenshot'),
+        id: 'synthetics/browser-browser.screenshot-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default',
+        compiled_stream: {
+          processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+        },
+      },
+    ],
+  };
+};
+
+export const getDataStream = (dataset: string) => ({
+  dataset,
+  type: 'synthetics',
+  elasticsearch: {
+    privileges: {
+      indices: ['auto_configure', 'create_doc', 'read'],
+    },
+  },
+});
+
+export const omitIds = (policy: PackagePolicy) => {
+  policy.inputs = sortBy(policy.inputs, 'type');
+
+  policy.inputs.forEach((input) => {
+    input.streams = sortBy(input.streams, 'data_stream.dataset');
+    input.streams.forEach((stream) => {
+      stream.id = '';
+    });
+  });
+
+  return omit(policy, ignoreTestFields);
+};
+
+export const comparePolicies = (aPolicy: PackagePolicy, bPolicy: PackagePolicy) => {
+  const a = omitIds(aPolicy);
+  const b = omitIds(bPolicy);
+
+  const aHttpInput = a.inputs?.find((input) => input.type === 'synthetics/http');
+  const aTcpInput = b.inputs?.find((input) => input.type === 'synthetics/tcp');
+  const aIcmpInput = b.inputs?.find((input) => input.type === 'synthetics/icmp');
+  const aBrowserInput = b.inputs?.find((input) => input.type === 'synthetics/browser');
+
+  const bHttpInput = b.inputs?.find((input) => input.type === 'synthetics/http');
+  const bTcpInput = b.inputs?.find((input) => input.type === 'synthetics/tcp');
+  const bIcmpInput = b.inputs?.find((input) => input.type === 'synthetics/icmp');
+  const bBrowserInput = b.inputs?.find((input) => input.type === 'synthetics/browser');
+
+  expect(aHttpInput).toEqual(bHttpInput);
+  expect(aTcpInput).toEqual(bTcpInput);
+  expect(aIcmpInput).toEqual(bIcmpInput);
+  expect(aBrowserInput).toEqual(bBrowserInput);
+
+  // delete inputs to compare rest of policy
+  delete a.inputs;
+  delete b.inputs;
+
+  // delete package  to compare rest of policy
+  delete a.package;
+  delete b.package;
+
+  expect(a).toEqual(b);
+};
+
+export const ignoreTestFields = [
+  'id',
+  'name',
+  'created_at',
+  'created_by',
+  'updated_at',
+  'updated_by',
+  'policy_id',
+  'policy_ids',
+  'version',
+  'revision',
+];
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_project_monitor_policy.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_project_monitor_policy.ts
new file mode 100644
index 0000000000000..cf9025fb8ce7f
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sample_data/test_project_monitor_policy.ts
@@ -0,0 +1,803 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { PackagePolicy } from '@kbn/fleet-plugin/common';
+import { INSTALLED_VERSION } from '../../../../services/synthetics_private_location';
+import { getDataStream } from './test_policy';
+
+export const commonVars = {
+  max_attempts: {
+    type: 'integer',
+    value: 2,
+  },
+};
+
+export const getTestProjectSyntheticsPolicyLightweight = (
+  {
+    name,
+    inputs = {},
+    configId,
+    id,
+    locationId,
+    projectId = 'test-suite',
+    locationName = 'Fleet Managed',
+    namespace,
+  }: {
+    name?: string;
+    inputs: Record<string, { value: string | boolean; type: string }>;
+    configId: string;
+    id: string;
+    projectId?: string;
+    locationId: string;
+    locationName?: string;
+    namespace?: string;
+  } = {
+    name: 'My Monitor 3',
+    inputs: {},
+    configId: '',
+    id: '',
+    locationId: 'fleet_managed',
+    locationName: 'Fleet Managed',
+  }
+): PackagePolicy => ({
+  id: `4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+  version: 'WzEzMDksMV0=',
+  name: `4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-${locationName}`,
+  namespace: namespace || undefined,
+  package: { name: 'synthetics', title: 'Elastic Synthetics', version: INSTALLED_VERSION },
+  enabled: true,
+  policy_id: '46034710-0ba6-11ed-ba04-5f123b9faa8b',
+  policy_ids: ['46034710-0ba6-11ed-ba04-5f123b9faa8b'],
+  inputs: [
+    {
+      type: 'synthetics/http',
+      policy_template: 'synthetics',
+      enabled: true,
+      streams: [
+        {
+          enabled: true,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'http',
+            elasticsearch: {
+              privileges: {
+                indices: ['auto_configure', 'create_doc', 'read'],
+              },
+            },
+          },
+          vars: {
+            __ui: {
+              type: 'yaml',
+              value: '{"is_tls_enabled":true}',
+            },
+            'check.request.body': {
+              type: 'yaml',
+              value: '"testGlobalParamValue"',
+            },
+            'check.request.headers': {
+              type: 'yaml',
+              value: '{"Content-Type":"application/x-www-form-urlencoded"}',
+            },
+            'check.request.method': {
+              type: 'text',
+              value: 'POST',
+            },
+            'check.response.body.negative': {
+              type: 'yaml',
+              value: null,
+            },
+            'check.response.body.positive': {
+              type: 'yaml',
+              value: '["testLocalParamsValue","saved"]',
+            },
+            'check.response.headers': {
+              type: 'yaml',
+              value: null,
+            },
+            'check.response.json': {
+              type: 'yaml',
+              value: '[{"description":"check status","expression":"foo.bar == \\"myValue\\""}]',
+            },
+            'check.response.status': {
+              type: 'yaml',
+              value: '["200"]',
+            },
+            enabled: {
+              type: 'bool',
+              value: false,
+            },
+            id: {
+              type: 'text',
+              value: JSON.stringify(id),
+            },
+            ipv4: {
+              type: 'bool',
+              value: true,
+            },
+            ipv6: {
+              type: 'bool',
+              value: true,
+            },
+            location_id: {
+              type: 'text',
+              value: locationId ?? 'fleet_managed',
+            },
+            location_name: {
+              type: 'text',
+              value: `"${locationName}"`,
+            },
+            max_redirects: {
+              type: 'integer',
+              value: '0',
+            },
+            ...commonVars,
+            mode: {
+              type: 'text',
+              value: 'any',
+            },
+            name: {
+              type: 'text',
+              value: JSON.stringify(name),
+            },
+            origin: {
+              type: 'text',
+              value: 'project',
+            },
+            password: {
+              type: 'password',
+              value: null,
+            },
+            processors: {
+              type: 'yaml',
+              value: JSON.stringify([
+                {
+                  add_fields: {
+                    fields: {
+                      'monitor.fleet_managed': true,
+                      config_id: configId,
+                      'monitor.project.name': projectId,
+                      'monitor.project.id': projectId,
+                      meta: { space_id: 'default' },
+                    },
+                    target: '',
+                  },
+                },
+              ]),
+            },
+            proxy_headers: {
+              type: 'yaml',
+              value: null,
+            },
+            proxy_url: {
+              type: 'text',
+              value: JSON.stringify('testGlobalParamOverwrite'),
+            },
+            'response.include_body': {
+              type: 'text',
+              value: 'always',
+            },
+            'response.include_body_max_bytes': {
+              type: 'text',
+              value: '900',
+            },
+            'response.include_headers': {
+              type: 'bool',
+              value: false,
+            },
+            schedule: {
+              type: 'text',
+              value: '"@every 60m"',
+            },
+            'service.name': {
+              type: 'text',
+              value: null,
+            },
+            'ssl.certificate': {
+              type: 'yaml',
+              value: null,
+            },
+            'ssl.certificate_authorities': {
+              type: 'yaml',
+              value: null,
+            },
+            'ssl.key': {
+              type: 'yaml',
+              value: null,
+            },
+            'ssl.key_passphrase': {
+              type: 'text',
+              value: null,
+            },
+            'ssl.supported_protocols': {
+              type: 'yaml',
+              value: '["TLSv1.1","TLSv1.2","TLSv1.3"]',
+            },
+            'ssl.verification_mode': {
+              type: 'text',
+              value: 'strict',
+            },
+            tags: {
+              type: 'yaml',
+              value: '["tag2","tag2"]',
+            },
+            timeout: {
+              type: 'text',
+              value: '80s',
+            },
+            type: {
+              type: 'text',
+              value: 'http',
+            },
+            urls: {
+              type: 'text',
+              value: '"http://localhost:9200"',
+            },
+            username: {
+              type: 'text',
+              value: null,
+            },
+          },
+          compiled_stream: {
+            __ui: {
+              is_tls_enabled: true,
+            },
+            type: 'http',
+            name,
+            id,
+            origin: 'project',
+            enabled: false,
+            urls: 'http://localhost:9200',
+            schedule: '@every 60m',
+            timeout: '80s',
+            max_redirects: 0,
+            max_attempts: 2,
+            tags: ['tag2', 'tag2'],
+            proxy_url: 'testGlobalParamOverwrite',
+            'run_from.geo.name': locationName ?? 'Test private location 0',
+            'run_from.id': locationId ?? 'Test private location 0',
+            'response.include_headers': false,
+            'response.include_body': 'always',
+            'response.include_body_max_bytes': 900,
+            'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'],
+            'ssl.verification_mode': 'strict',
+            'check.request.method': 'POST',
+            'check.request.headers': { 'Content-Type': 'application/x-www-form-urlencoded' },
+            'check.response.body.positive': ['testLocalParamsValue', 'saved'],
+            'check.response.json': [
+              {
+                description: 'check status',
+                expression: 'foo.bar == "myValue"',
+              },
+            ],
+            'check.response.status': ['200'],
+            'check.request.body': 'testGlobalParamValue',
+            ipv4: true,
+            ipv6: true,
+            mode: 'any',
+            processors: [
+              {
+                add_fields: {
+                  fields: {
+                    config_id: configId,
+                    'monitor.fleet_managed': true,
+                    'monitor.project.id': projectId,
+                    'monitor.project.name': projectId,
+                    meta: { space_id: 'default' },
+                  },
+                  target: '',
+                },
+              },
+            ],
+          },
+          id: `synthetics/http-http-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/tcp',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: false,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'tcp',
+          },
+          vars: {
+            __ui: { type: 'yaml' },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'tcp', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: '"@every 3m"', type: 'text' },
+            hosts: { type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            proxy_url: { type: 'text' },
+            proxy_use_local_resolver: { value: false, type: 'bool' },
+            tags: { type: 'yaml' },
+            'check.send': { type: 'text' },
+            'check.receive': { type: 'text' },
+            'ssl.certificate_authorities': { type: 'yaml' },
+            'ssl.certificate': { type: 'yaml' },
+            'ssl.key': { type: 'yaml' },
+            'ssl.key_passphrase': { type: 'text' },
+            'ssl.verification_mode': { type: 'text' },
+            'ssl.supported_protocols': { type: 'yaml' },
+            location_id: { value: 'fleet_managed', type: 'text' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            ...commonVars,
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ipv4: { type: 'bool', value: true },
+            ipv6: { type: 'bool', value: true },
+            mode: { type: 'text' },
+          },
+          id: `synthetics/tcp-tcp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/icmp',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: false,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'icmp',
+          },
+          vars: {
+            __ui: { type: 'yaml' },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'icmp', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: '"@every 3m"', type: 'text' },
+            wait: { value: '1s', type: 'text' },
+            hosts: { type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            tags: { type: 'yaml' },
+            location_id: { value: 'fleet_managed', type: 'text' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            ...commonVars,
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ipv4: { type: 'bool', value: true },
+            ipv6: { type: 'bool', value: true },
+            mode: { type: 'text' },
+          },
+          id: `synthetics/icmp-icmp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/browser',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: true,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'browser',
+            elasticsearch: {
+              privileges: {
+                indices: ['auto_configure', 'create_doc', 'read'],
+              },
+            },
+          },
+          vars: {
+            __ui: {
+              type: 'yaml',
+            },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'browser', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: JSON.stringify('@every 3m'), type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            tags: { type: 'yaml' },
+            'source.zip_url.url': { type: 'text' },
+            'source.zip_url.username': { type: 'text' },
+            'source.zip_url.folder': { type: 'text' },
+            'source.zip_url.password': { type: 'password' },
+            'source.inline.script': { type: 'yaml' },
+            'source.project.content': {
+              type: 'text',
+            },
+            params: {
+              type: 'yaml',
+            },
+            playwright_options: {
+              type: 'yaml',
+            },
+            screenshots: { type: 'text' },
+            synthetics_args: { type: 'text' },
+            ignore_https_errors: { type: 'bool' },
+            'throttling.config': {
+              type: 'text',
+            },
+            'filter_journeys.tags': { type: 'yaml' },
+            'filter_journeys.match': { type: 'text' },
+            'source.zip_url.ssl.certificate_authorities': { type: 'yaml' },
+            'source.zip_url.ssl.certificate': { type: 'yaml' },
+            'source.zip_url.ssl.key': { type: 'yaml' },
+            'source.zip_url.ssl.key_passphrase': { type: 'text' },
+            'source.zip_url.ssl.verification_mode': { type: 'text' },
+            'source.zip_url.ssl.supported_protocols': { type: 'yaml' },
+            'source.zip_url.proxy_url': { type: 'text' },
+            location_id: { value: 'fleet_managed', type: 'text' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            ...commonVars,
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ...inputs,
+          },
+          id: `synthetics/browser-browser-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            __ui: null,
+            type: 'browser',
+            name: null,
+            enabled: true,
+            schedule: '@every 3m',
+            timeout: null,
+            throttling: null,
+            processors: [
+              {
+                add_fields: {
+                  target: '',
+                  fields: {
+                    'monitor.fleet_managed': true,
+                  },
+                },
+              },
+            ],
+            'run_from.geo.name': 'Fleet managed',
+            'run_from.id': 'Fleet managed',
+            ...Object.keys(inputs).reduce((acc: Record<string, unknown>, key) => {
+              acc[key] = inputs[key].value;
+              return acc;
+            }, {}),
+          },
+        },
+        {
+          enabled: true,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'browser.network',
+            elasticsearch: {
+              privileges: {
+                indices: ['auto_configure', 'create_doc', 'read'],
+              },
+            },
+          },
+          id: `synthetics/browser-browser.network-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+          },
+        },
+        {
+          enabled: true,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'browser.screenshot',
+            elasticsearch: {
+              privileges: {
+                indices: ['auto_configure', 'create_doc', 'read'],
+              },
+            },
+          },
+          id: `synthetics/browser-browser.screenshot-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+          },
+        },
+      ],
+    },
+  ],
+  is_managed: true,
+  revision: 1,
+  created_at: '2022-08-23T13:52:42.531Z',
+  created_by: 'system',
+  updated_at: '2022-08-23T13:52:42.531Z',
+  updated_by: 'system',
+});
+
+export const getTestProjectSyntheticsPolicy = (
+  {
+    name,
+    inputs = {},
+    configId,
+    id,
+    projectId = 'test-suite',
+    locationId,
+    locationName = 'Fleet Managed',
+    namespace,
+  }: {
+    name?: string;
+    inputs: Record<string, { value: string | boolean; type: string }>;
+    configId: string;
+    id: string;
+    projectId?: string;
+    locationName?: string;
+    locationId: string;
+    namespace?: string;
+  } = {
+    name: 'check if title is present-Test private location 0',
+    inputs: {},
+    configId: '',
+    id: '',
+    locationId: 'fleet_managed',
+    locationName: 'Fleet Managed',
+  }
+): PackagePolicy => ({
+  id: `4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+  version: 'WzEzMDksMV0=',
+  name: `4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-Test private location 0`,
+  namespace: namespace || undefined,
+  package: { name: 'synthetics', title: 'Elastic Synthetics', version: INSTALLED_VERSION },
+  enabled: true,
+  policy_id: '46034710-0ba6-11ed-ba04-5f123b9faa8b',
+  policy_ids: ['46034710-0ba6-11ed-ba04-5f123b9faa8b'],
+  inputs: [
+    {
+      type: 'synthetics/http',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: false,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'http',
+          },
+          vars: {
+            __ui: { type: 'yaml' },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'http', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: '"@every 3m"', type: 'text' },
+            urls: { type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            max_redirects: { type: 'integer' },
+            proxy_url: { type: 'text' },
+            processors: { type: 'yaml' },
+            proxy_headers: { type: 'yaml' },
+            tags: { type: 'yaml' },
+            username: { type: 'text' },
+            password: { type: 'password' },
+            'response.include_headers': { type: 'bool' },
+            'response.include_body': { type: 'text' },
+            'response.include_body_max_bytes': { type: 'text' },
+            'check.request.method': { type: 'text' },
+            'check.request.headers': { type: 'yaml' },
+            'check.request.body': { type: 'yaml' },
+            'check.response.status': { type: 'yaml' },
+            'check.response.headers': { type: 'yaml' },
+            'check.response.body.positive': { type: 'yaml' },
+            'check.response.body.negative': { type: 'yaml' },
+            'check.response.json': { type: 'yaml' },
+            'ssl.certificate_authorities': { type: 'yaml' },
+            'ssl.certificate': { type: 'yaml' },
+            'ssl.key': { type: 'yaml' },
+            'ssl.key_passphrase': { type: 'text' },
+            'ssl.verification_mode': { type: 'text' },
+            'ssl.supported_protocols': { type: 'yaml' },
+            location_id: { value: 'fleet_managed', type: 'text' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            ...commonVars,
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ipv4: { type: 'bool', value: true },
+            ipv6: { type: 'bool', value: true },
+            mode: { type: 'text' },
+          },
+          id: `synthetics/http-http-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/tcp',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: false,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'tcp',
+          },
+          vars: {
+            __ui: { type: 'yaml' },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'tcp', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: '"@every 3m"', type: 'text' },
+            hosts: { type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            proxy_url: { type: 'text' },
+            proxy_use_local_resolver: { value: false, type: 'bool' },
+            tags: { type: 'yaml' },
+            'check.send': { type: 'text' },
+            'check.receive': { type: 'text' },
+            'ssl.certificate_authorities': { type: 'yaml' },
+            'ssl.certificate': { type: 'yaml' },
+            'ssl.key': { type: 'yaml' },
+            'ssl.key_passphrase': { type: 'text' },
+            'ssl.verification_mode': { type: 'text' },
+            'ssl.supported_protocols': { type: 'yaml' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            ...commonVars,
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ipv4: { type: 'bool', value: true },
+            ipv6: { type: 'bool', value: true },
+            mode: { type: 'text' },
+          },
+          id: `synthetics/tcp-tcp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/icmp',
+      policy_template: 'synthetics',
+      enabled: false,
+      streams: [
+        {
+          enabled: false,
+          data_stream: {
+            type: 'synthetics',
+            dataset: 'icmp',
+          },
+          vars: {
+            __ui: { type: 'yaml' },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'icmp', type: 'text' },
+            name: { type: 'text' },
+            schedule: { value: '"@every 3m"', type: 'text' },
+            wait: { value: '1s', type: 'text' },
+            hosts: { type: 'text' },
+            'service.name': { type: 'text' },
+            timeout: { type: 'text' },
+            tags: { type: 'yaml' },
+            location_name: { value: 'Fleet managed', type: 'text' },
+            max_attempts: {
+              type: 'integer',
+              value: 2,
+            },
+            id: { type: 'text' },
+            origin: { type: 'text' },
+            ipv4: { type: 'bool', value: true },
+            ipv6: { type: 'bool', value: true },
+            mode: { type: 'text' },
+          },
+          id: `synthetics/icmp-icmp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+        },
+      ],
+    },
+    {
+      type: 'synthetics/browser',
+      policy_template: 'synthetics',
+      enabled: true,
+      streams: [
+        {
+          enabled: true,
+          data_stream: getDataStream('browser'),
+          vars: {
+            __ui: {
+              value: '{"script_source":{"is_generated_script":false,"file_name":""}}',
+              type: 'yaml',
+            },
+            enabled: { value: true, type: 'bool' },
+            type: { value: 'browser', type: 'text' },
+            name: { value: '"check if title is present"', type: 'text' },
+            schedule: { value: '"@every 10m"', type: 'text' },
+            'service.name': { value: null, type: 'text' },
+            timeout: { value: null, type: 'text' },
+            tags: { value: null, type: 'yaml' },
+            'source.zip_url.url': { type: 'text' },
+            'source.zip_url.username': { type: 'text' },
+            'source.zip_url.folder': { type: 'text' },
+            'source.zip_url.password': { type: 'password' },
+            'source.inline.script': { value: null, type: 'yaml' },
+            'source.project.content': {
+              value:
+                'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
+              type: 'text',
+            },
+            params: {
+              value:
+                '{"testGlobalParam2":"testGlobalParamValue2","testGlobalParam":"testGlobalParamValue"}',
+              type: 'yaml',
+            },
+            playwright_options: {
+              value: '{"headless":true,"chromiumSandbox":false}',
+              type: 'yaml',
+            },
+            screenshots: { value: 'on', type: 'text' },
+            synthetics_args: { value: null, type: 'text' },
+            ignore_https_errors: { value: false, type: 'bool' },
+            'throttling.config': {
+              value: JSON.stringify({ download: 5, upload: 3, latency: 20 }),
+              type: 'text',
+            },
+            'filter_journeys.tags': { value: null, type: 'yaml' },
+            'filter_journeys.match': { value: '"check if title is present"', type: 'text' },
+            'source.zip_url.ssl.certificate_authorities': { type: 'yaml' },
+            'source.zip_url.ssl.certificate': { type: 'yaml' },
+            'source.zip_url.ssl.key': { type: 'yaml' },
+            'source.zip_url.ssl.key_passphrase': { type: 'text' },
+            'source.zip_url.ssl.verification_mode': { type: 'text' },
+            'source.zip_url.ssl.supported_protocols': { type: 'yaml' },
+            'source.zip_url.proxy_url': { type: 'text' },
+            location_name: { value: 'Test private location 0', type: 'text' },
+            ...commonVars,
+            location_id: { value: 'fleet_managed', type: 'text' },
+            id: { value: id, type: 'text' },
+            origin: { value: 'project', type: 'text' },
+            ...inputs,
+          },
+          id: `synthetics/browser-browser-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            __ui: {
+              script_source: { is_generated_script: false, file_name: '' },
+            },
+            type: 'browser',
+            name: 'check if title is present',
+            id,
+            origin: 'project',
+            enabled: true,
+            schedule: '@every 10m',
+            'run_from.geo.name': locationName,
+            'run_from.id': locationId,
+            timeout: null,
+            throttling: { download: 5, upload: 3, latency: 20 },
+            'source.project.content':
+              'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA',
+            playwright_options: { headless: true, chromiumSandbox: false },
+            screenshots: 'on',
+            'filter_journeys.match': 'check if title is present',
+            params: {
+              testGlobalParam: 'testGlobalParamValue',
+              testGlobalParam2: 'testGlobalParamValue2',
+            },
+            ...Object.keys(inputs).reduce((acc: Record<string, unknown>, key) => {
+              acc[key] = inputs[key].value;
+              return acc;
+            }, {}),
+          },
+        },
+        {
+          enabled: true,
+          data_stream: getDataStream('browser.network'),
+          id: `synthetics/browser-browser.network-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+          },
+        },
+        {
+          enabled: true,
+          data_stream: getDataStream('browser.screenshot'),
+          id: `synthetics/browser-browser.screenshot-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`,
+          compiled_stream: {
+            processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }],
+          },
+        },
+      ],
+    },
+  ],
+  is_managed: true,
+  revision: 1,
+  created_at: '2022-08-23T13:52:42.531Z',
+  created_by: 'system',
+  updated_at: '2022-08-23T13:52:42.531Z',
+  updated_by: 'system',
+});
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/suggestions.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/suggestions.ts
new file mode 100644
index 0000000000000..d6a42b6cc8972
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/suggestions.ts
@@ -0,0 +1,276 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import expect from 'expect';
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  MonitorFields,
+  EncryptedSyntheticsSavedMonitor,
+  ProjectMonitorsRequest,
+  PrivateLocation,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('SyntheticsSuggestions', function () {
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+    const privateLocationsTestService = new PrivateLocationTestService(getService);
+
+    const SPACE_ID = `test-space-${uuidv4()}`;
+    const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+    let projectMonitors: ProjectMonitorsRequest;
+    let _monitors: MonitorFields[];
+    let monitors: MonitorFields[];
+    let editorUser: RoleCredentials;
+    let privateLocation: PrivateLocation;
+
+    const setUniqueIds = (request: ProjectMonitorsRequest) => {
+      return {
+        ...request,
+        monitors: request.monitors.map((monitor) => ({
+          ...monitor,
+          id: uuidv4(),
+          locations: [],
+          privateLocations: [privateLocation.label],
+        })),
+      };
+    };
+
+    const saveMonitor = async (monitor: MonitorFields) => {
+      const res = await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(monitor);
+
+      return res.body as EncryptedSyntheticsSavedMonitor;
+    };
+
+    before(async () => {
+      await kibanaServer.savedObjects.clean({
+        types: [
+          syntheticsMonitorType,
+          'ingest-agent-policies',
+          'ingest-package-policies',
+          'synthetics-private-location',
+        ],
+      });
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+      privateLocation = await privateLocationsTestService.addTestPrivateLocation(SPACE_ID);
+      _monitors = [getFixtureJson('http_monitor')].map((monitor) => ({
+        ...monitor,
+        locations: [privateLocation],
+      }));
+      projectMonitors = setUniqueIds({
+        monitors: getFixtureJson('project_icmp_monitor')
+          .monitors.slice(0, 2)
+          .map((monitor: any) => ({
+            ...monitor,
+            privateLocations: [privateLocation.label],
+            locations: [],
+          })),
+      });
+    });
+
+    beforeEach(async () => {
+      await kibanaServer.savedObjects.clean({
+        types: [syntheticsMonitorType, 'ingest-package-policies'],
+      });
+
+      monitors = [];
+      for (let i = 0; i < 20; i++) {
+        monitors.push({
+          ..._monitors[0],
+          locations: [privateLocation],
+          name: `${_monitors[0].name} ${i}`,
+        });
+      }
+    });
+
+    after(async () => {
+      await kibanaServer.spaces.delete(SPACE_ID);
+    });
+
+    it('returns the suggestions', async () => {
+      const project = `test-project-${uuidv4()}`;
+      await supertest
+        .put(
+          `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+            '{projectName}',
+            project
+          )}`
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(projectMonitors)
+        .expect(200);
+      for (let i = 0; i < monitors.length; i++) {
+        await saveMonitor(monitors[i]);
+      }
+      const apiResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SUGGESTIONS}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      expect(apiResponse.body).toEqual({
+        locations: [
+          {
+            count: 22,
+            label: privateLocation.label,
+            value: privateLocation.id,
+          },
+        ],
+        monitorIds: expect.arrayContaining([
+          ...monitors.map((monitor) => ({
+            count: 1,
+            label: monitor.name,
+            value: expect.any(String),
+          })),
+          ...projectMonitors.monitors.slice(0, 2).map((monitor) => ({
+            count: 1,
+            label: monitor.name,
+            value: expect.any(String),
+          })),
+        ]),
+        monitorTypes: [
+          {
+            count: 20,
+            label: 'http',
+            value: 'http',
+          },
+          {
+            count: 2,
+            label: 'icmp',
+            value: 'icmp',
+          },
+        ],
+        projects: [
+          {
+            count: 2,
+            label: project,
+            value: project,
+          },
+        ],
+        tags: expect.arrayContaining([
+          {
+            count: 21,
+            label: 'tag1',
+            value: 'tag1',
+          },
+          {
+            count: 21,
+            label: 'tag2',
+            value: 'tag2',
+          },
+          {
+            count: 1,
+            label: 'org:google',
+            value: 'org:google',
+          },
+          {
+            count: 1,
+            label: 'service:smtp',
+            value: 'service:smtp',
+          },
+        ]),
+      });
+    });
+
+    it('handles query params for projects', async () => {
+      for (let i = 0; i < monitors.length; i++) {
+        await saveMonitor(monitors[i]);
+      }
+      const project = `test-project-${uuidv4()}`;
+      await supertest
+        .put(
+          `/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace(
+            '{projectName}',
+            project
+          )}`
+        )
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(projectMonitors)
+        .expect(200);
+
+      const apiResponse = await supertest
+        .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SUGGESTIONS}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .query({
+          projects: [project],
+        })
+        .expect(200);
+
+      expect(apiResponse.body).toEqual({
+        locations: [
+          {
+            count: 2,
+            label: privateLocation.label,
+            value: privateLocation.id,
+          },
+        ],
+        monitorIds: expect.arrayContaining(
+          projectMonitors.monitors.map((monitor) => ({
+            count: 1,
+            label: monitor.name,
+            value: expect.any(String),
+          }))
+        ),
+        monitorTypes: [
+          {
+            count: 2,
+            label: 'icmp',
+            value: 'icmp',
+          },
+        ],
+        projects: [
+          {
+            count: 2,
+            label: project,
+            value: project,
+          },
+        ],
+        tags: expect.arrayContaining([
+          {
+            count: 1,
+            label: 'tag1',
+            value: 'tag1',
+          },
+          {
+            count: 1,
+            label: 'tag2',
+            value: 'tag2',
+          },
+          {
+            count: 1,
+            label: 'org:google',
+            value: 'org:google',
+          },
+          {
+            count: 1,
+            label: 'service:smtp',
+            value: 'service:smtp',
+          },
+        ]),
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sync_global_params.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sync_global_params.ts
new file mode 100644
index 0000000000000..425eb0c704b50
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/sync_global_params.ts
@@ -0,0 +1,354 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import {
+  ConfigKey,
+  HTTPFields,
+  LocationStatus,
+  PrivateLocation,
+  ServiceLocation,
+  SyntheticsParams,
+} from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { PackagePolicy } from '@kbn/fleet-plugin/common';
+import expect from '@kbn/expect';
+import { syntheticsParamType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { PrivateLocationTestService } from '../../../services/synthetics_private_location';
+import { comparePolicies, getTestSyntheticsPolicy } from './sample_data/test_policy';
+import { addMonitorAPIHelper, omitMonitorKeys } from './create_monitor';
+
+export const LOCAL_LOCATION = {
+  id: 'dev',
+  label: 'Dev Service',
+  geo: {
+    lat: 0,
+    lon: 0,
+  },
+  isServiceManaged: true,
+};
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe.skip('SyncGlobalParams', function () {
+    this.tags('skipCloud');
+    const supertestAPI = getService('supertestWithoutAuth');
+    const supertestWithAuth = getService('supertest');
+    const kServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    let testFleetPolicyID: string;
+    let _browserMonitorJson: HTTPFields;
+    let browserMonitorJson: HTTPFields;
+
+    let _httpMonitorJson: HTTPFields;
+    let httpMonitorJson: HTTPFields;
+
+    let newMonitorId: string;
+    let newHttpMonitorId: string;
+    let privateLocations: PrivateLocation[] = [];
+
+    let editorUser: RoleCredentials;
+
+    const testPrivateLocations = new PrivateLocationTestService(getService);
+    const params: Record<string, string> = {};
+
+    const addMonitorAPI = async (monitor: any, statusCode = 200) => {
+      return addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorUser, samlAuth);
+    };
+
+    before(async () => {
+      await kServer.savedObjects.cleanStandardList();
+      await testPrivateLocations.installSyntheticsPackage();
+
+      _browserMonitorJson = getFixtureJson('browser_monitor');
+      _httpMonitorJson = getFixtureJson('http_monitor');
+      await kServer.savedObjects.clean({ types: [syntheticsParamType] });
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+    });
+
+    beforeEach(() => {
+      browserMonitorJson = _browserMonitorJson;
+      httpMonitorJson = _httpMonitorJson;
+    });
+
+    const testPolicyName = 'Fleet test server policy' + Date.now();
+
+    it('adds a test fleet policy', async () => {
+      const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
+      testFleetPolicyID = apiResponse.body.item.id;
+    });
+
+    it('add a test private location', async () => {
+      privateLocations = await testPrivateLocations.setTestLocations([testFleetPolicyID]);
+
+      const apiResponse = await supertestAPI.get(SYNTHETICS_API_URLS.SERVICE_LOCATIONS);
+
+      const testLocations: Array<PrivateLocation | ServiceLocation> = [
+        {
+          id: 'dev',
+          label: 'Dev Service',
+          geo: { lat: 0, lon: 0 },
+          url: 'mockDevUrl',
+          isServiceManaged: true,
+          status: LocationStatus.EXPERIMENTAL,
+          isInvalid: false,
+        },
+        {
+          id: 'dev2',
+          label: 'Dev Service 2',
+          geo: { lat: 0, lon: 0 },
+          url: 'mockDevUrl',
+          isServiceManaged: true,
+          status: LocationStatus.EXPERIMENTAL,
+          isInvalid: false,
+        },
+        {
+          id: testFleetPolicyID,
+          isInvalid: false,
+          isServiceManaged: false,
+          label: privateLocations[0].label,
+          geo: {
+            lat: 0,
+            lon: 0,
+          },
+          agentPolicyId: testFleetPolicyID,
+        },
+      ];
+
+      expect(apiResponse.body.locations).eql(testLocations);
+    });
+
+    it('adds a monitor in private location', async () => {
+      const newMonitor = browserMonitorJson;
+
+      const pvtLoc = {
+        id: testFleetPolicyID,
+        agentPolicyId: testFleetPolicyID,
+        label: privateLocations[0].label,
+        isServiceManaged: false,
+        geo: {
+          lat: 0,
+          lon: 0,
+        },
+      };
+
+      newMonitor.locations.push(pvtLoc);
+
+      const apiResponse = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse.body).eql(
+        omitMonitorKeys({
+          ...newMonitor,
+          [ConfigKey.MONITOR_QUERY_ID]: apiResponse.body.id,
+          [ConfigKey.CONFIG_ID]: apiResponse.body.id,
+          locations: [LOCAL_LOCATION, pvtLoc],
+        })
+      );
+      newMonitorId = apiResponse.rawBody.id;
+    });
+
+    it('added an integration for previously added monitor', async () => {
+      const apiResponse = await supertestAPI.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponse.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy?.policy_id).eql(
+        testFleetPolicyID,
+        JSON.stringify({ testFleetPolicyID, newMonitorId })
+      );
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: browserMonitorJson.name,
+          id: newMonitorId,
+          isBrowser: true,
+          location: { id: testFleetPolicyID },
+        })
+      );
+    });
+
+    it('adds a test param', async () => {
+      const apiResponse = await supertestAPI
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'test', value: 'http://proxy.com' });
+
+      expect(apiResponse.status).eql(200);
+    });
+
+    it('get list of params', async () => {
+      const apiResponse = await supertestAPI
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'test', value: 'http://proxy.com' });
+
+      expect(apiResponse.status).eql(200);
+
+      apiResponse.body.forEach(({ key, value }: SyntheticsParams) => {
+        params[key] = value;
+      });
+    });
+
+    it('sync global params', async () => {
+      const apiResponse = await supertestAPI
+        .get(SYNTHETICS_API_URLS.SYNC_GLOBAL_PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'test', value: 'test' });
+
+      expect(apiResponse.status).eql(200);
+    });
+
+    it('added params to for previously added integration', async () => {
+      const apiResponse = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponse.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: browserMonitorJson.name,
+          id: newMonitorId,
+          params,
+          isBrowser: true,
+          location: { id: testFleetPolicyID },
+        })
+      );
+    });
+
+    it('add a http monitor using param', async () => {
+      const newMonitor = httpMonitorJson;
+      const pvtLoc = {
+        id: testFleetPolicyID,
+        agentPolicyId: testFleetPolicyID,
+        label: privateLocations[0].label,
+        isServiceManaged: false,
+        geo: {
+          lat: 0,
+          lon: 0,
+        },
+      };
+      newMonitor.locations.push(pvtLoc);
+
+      newMonitor.proxy_url = '${test}';
+
+      const apiResponse = await addMonitorAPI(newMonitor);
+
+      expect(apiResponse.body).eql(
+        omitMonitorKeys({
+          ...newMonitor,
+          [ConfigKey.MONITOR_QUERY_ID]: apiResponse.body.id,
+          [ConfigKey.CONFIG_ID]: apiResponse.body.id,
+          locations: [LOCAL_LOCATION, pvtLoc],
+        })
+      );
+      newHttpMonitorId = apiResponse.rawBody.id;
+    });
+
+    it('parsed params for previously added http monitors', async () => {
+      const apiResponse = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponse.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newHttpMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+
+      const pPolicy = getTestSyntheticsPolicy({
+        name: httpMonitorJson.name,
+        id: newHttpMonitorId,
+        isTLSEnabled: false,
+        namespace: 'testnamespace',
+        location: { id: testFleetPolicyID },
+      });
+
+      comparePolicies(packagePolicy, pPolicy);
+    });
+
+    it('delete all params and sync again', async () => {
+      await supertestAPI
+        .post(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ key: 'get', value: 'test' });
+      const getResponse = await supertestAPI
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(getResponse.body.length).eql(2);
+
+      const paramsResponse = getResponse.body || [];
+      const ids = paramsResponse.map((param: any) => param.id);
+
+      const deleteResponse = await supertestAPI
+        .delete(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send({ ids })
+        .expect(200);
+
+      expect(deleteResponse.body).to.have.length(2);
+
+      const getResponseAfterDelete = await supertestAPI
+        .get(SYNTHETICS_API_URLS.PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      expect(getResponseAfterDelete.body.length).eql(0);
+
+      await supertestAPI
+        .get(SYNTHETICS_API_URLS.SYNC_GLOBAL_PARAMS)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const apiResponse = await supertestWithAuth.get(
+        '/api/fleet/package_policies?page=1&perPage=2000&kuery=ingest-package-policies.package.name%3A%20synthetics'
+      );
+
+      const packagePolicy = apiResponse.body.items.find(
+        (pkgPolicy: PackagePolicy) =>
+          pkgPolicy.id === newMonitorId + '-' + testFleetPolicyID + '-default'
+      );
+
+      expect(packagePolicy.policy_id).eql(testFleetPolicyID);
+
+      comparePolicies(
+        packagePolicy,
+        getTestSyntheticsPolicy({
+          name: browserMonitorJson.name,
+          id: newMonitorId,
+          isBrowser: true,
+          location: { id: testFleetPolicyID },
+        })
+      );
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/synthetics_enablement.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/synthetics_enablement.ts
new file mode 100644
index 0000000000000..cb2197bf54169
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/synthetics_enablement.ts
@@ -0,0 +1,352 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { SupertestWithRoleScopeType } from '../../../services';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  const config = getService('config');
+  const isServerless = config.get('serverless');
+  const correctPrivileges = {
+    applications: [],
+    cluster: ['monitor', 'read_pipeline', ...(!isServerless ? ['read_ilm'] : [])],
+    indices: [
+      {
+        allow_restricted_indices: false,
+        names: ['synthetics-*'],
+        privileges: ['view_index_metadata', 'create_doc', 'auto_configure', 'read'],
+      },
+    ],
+    metadata: {},
+    run_as: [],
+    transient_metadata: {
+      enabled: true,
+    },
+  };
+
+  describe('SyntheticsEnablement', function () {
+    /* temporarily skip MKI. Public locations appear to be disabled in QA causing failures
+     * in isServiceAllowed check */
+    this.tags(['skipMKI']);
+
+    const roleScopedSupertest = getService('roleScopedSupertest');
+    const kibanaServer = getService('kibanaServer');
+
+    let supertestWithEditorScope: SupertestWithRoleScopeType;
+    let supertestWithAdminScope: SupertestWithRoleScopeType;
+
+    const getApiKeys = async () => {
+      const { body } = await supertestWithAdminScope
+        .post('/internal/security/api_key/_query')
+        .send({
+          query: {
+            bool: {
+              filter: [
+                {
+                  term: {
+                    name: 'synthetics-api-key (required for Synthetics App)',
+                  },
+                },
+              ],
+            },
+          },
+          sort: { field: 'creation', direction: 'desc' },
+          from: 0,
+          size: 25,
+          filters: {},
+        })
+        .expect(200);
+
+      const apiKeys = body.apiKeys || [];
+      return apiKeys.filter(
+        (apiKey: any) => apiKey.name.includes('synthetics-api-key') && apiKey.invalidated === false
+      );
+    };
+
+    before(async () => {
+      supertestWithEditorScope = await roleScopedSupertest.getSupertestWithRoleScope('editor', {
+        withInternalHeaders: true,
+        useCookieHeader: true,
+      });
+      supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
+        withInternalHeaders: true,
+        useCookieHeader: true,
+      });
+    });
+
+    describe('[PUT] /internal/uptime/service/enablement', () => {
+      before(async () => {
+        supertestWithEditorScope = await roleScopedSupertest.getSupertestWithRoleScope('editor', {
+          withInternalHeaders: true,
+          useCookieHeader: true,
+        });
+        supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
+          withInternalHeaders: true,
+          useCookieHeader: true,
+        });
+      });
+
+      beforeEach(async () => {
+        const apiKeys = await getApiKeys();
+        if (apiKeys.length) {
+          await supertestWithAdminScope
+            .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+            .expect(200);
+        }
+      });
+
+      after(async () => {
+        // always invalidate API key for the scoped role in the end
+        await supertestWithAdminScope.destroy();
+        await supertestWithEditorScope.destroy();
+      });
+
+      it(`returns response when user cannot manage api keys`, async () => {
+        const apiResponse = await supertestWithEditorScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: false,
+          canEnable: false,
+          isEnabled: false,
+          isValidApiKey: false,
+          isServiceAllowed: true,
+        });
+      });
+
+      it(`returns response for an admin with privilege`, async () => {
+        const apiResponse = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+        const validApiKeys = await getApiKeys();
+        expect(validApiKeys.length).eql(1);
+        expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges);
+      });
+
+      it(`does not create excess api keys`, async () => {
+        const apiResponse = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        const validApiKeys = await getApiKeys();
+        expect(validApiKeys.length).eql(1);
+        expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges);
+
+        // call api a second time
+        const apiResponse2 = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse2.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        const validApiKeys2 = await getApiKeys();
+        expect(validApiKeys2.length).eql(1);
+        expect(validApiKeys2[0].role_descriptors.synthetics_writer).eql(correctPrivileges);
+      });
+
+      it(`auto re-enables api key when invalidated`, async () => {
+        const apiResponse = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        const validApiKeys = await getApiKeys();
+        expect(validApiKeys.length).eql(1);
+        expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges);
+
+        // delete api key
+        await supertestWithAdminScope
+          .post('/internal/security/api_key/invalidate')
+          .send({
+            apiKeys: validApiKeys.map((apiKey: { id: string; name: string }) => ({
+              id: apiKey.id,
+              name: apiKey.name,
+            })),
+            isAdmin: true,
+          })
+          .expect(200);
+
+        const validApiKeysAferDeletion = await getApiKeys();
+        expect(validApiKeysAferDeletion.length).eql(0);
+
+        // call api a second time
+        const apiResponse2 = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse2.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        const validApiKeys2 = await getApiKeys();
+        expect(validApiKeys2.length).eql(1);
+        expect(validApiKeys2[0].role_descriptors.synthetics_writer).eql(correctPrivileges);
+      });
+
+      it('returns response for an uptime all user without admin privileges', async () => {
+        const apiResponse = await supertestWithEditorScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: false,
+          canEnable: false,
+          isEnabled: false,
+          isValidApiKey: false,
+          isServiceAllowed: true,
+        });
+      });
+    });
+
+    describe('[DELETE] /internal/uptime/service/enablement', () => {
+      beforeEach(async () => {
+        const apiKeys = await getApiKeys();
+        if (apiKeys.length) {
+          await supertestWithAdminScope
+            .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+            .expect(200);
+        }
+      });
+
+      it('with an admin', async () => {
+        await supertestWithAdminScope.put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT).expect(200);
+        const delResponse = await supertestWithAdminScope
+          .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+        expect(delResponse.body).eql({});
+        const apiResponse = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+      });
+
+      it('with an uptime user', async () => {
+        await supertestWithAdminScope.put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT).expect(200);
+        await supertestWithEditorScope
+          .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(403);
+        const apiResponse = await supertestWithEditorScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: false,
+          canEnable: false,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+      });
+
+      it('is space agnostic', async () => {
+        const SPACE_ID = `test-space-${uuidv4()}`;
+        const SPACE_NAME = `test-space-name-${uuidv4()}`;
+        await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+        // can enable synthetics in default space when enabled in a non default space
+        const apiResponseGet = await supertestWithAdminScope
+          .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT}`)
+          .expect(200);
+
+        expect(apiResponseGet.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        await supertestWithAdminScope
+          .put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT}`)
+          .expect(200);
+        await supertestWithAdminScope.delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT).expect(200);
+        const apiResponse = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+
+        // can disable synthetics in non default space when enabled in default space
+        await supertestWithAdminScope.put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT).expect(200);
+        await supertestWithAdminScope
+          .delete(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT}`)
+          .expect(200);
+        const apiResponse2 = await supertestWithAdminScope
+          .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+          .expect(200);
+
+        expect(apiResponse2.body).eql({
+          areApiKeysEnabled: true,
+          canManageApiKeys: true,
+          canEnable: true,
+          isEnabled: true,
+          isValidApiKey: true,
+          isServiceAllowed: true,
+        });
+      });
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/test_now_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/test_now_monitor.ts
new file mode 100644
index 0000000000000..1efe174a2c666
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics/test_now_monitor.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { RoleCredentials } from '@kbn/ftr-common-functional-services';
+import { MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import expect from '@kbn/expect';
+import { omit } from 'lodash';
+import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
+import { getFixtureJson } from './helpers/get_fixture_json';
+import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';
+
+export const LOCAL_LOCATION = {
+  id: 'dev',
+  label: 'Dev Service',
+  geo: {
+    lat: 0,
+    lon: 0,
+  },
+  isServiceManaged: true,
+};
+
+export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
+  describe('RunTestManually', function () {
+    this.tags(['skipMKI', 'skipCloud']);
+
+    const supertest = getService('supertestWithoutAuth');
+    const kibanaServer = getService('kibanaServer');
+    const samlAuth = getService('samlAuth');
+
+    const monitorTestService = new SyntheticsMonitorTestService(getService);
+
+    let newMonitor: MonitorFields;
+    let editorUser: RoleCredentials;
+
+    before(async () => {
+      await kibanaServer.savedObjects.cleanStandardList();
+      editorUser = await samlAuth.createM2mApiKeyWithRoleScope('editor');
+      await supertest
+        .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+      newMonitor = getFixtureJson('http_monitor');
+    });
+
+    it('runs test manually', async () => {
+      const resp = await monitorTestService.addMonitor(newMonitor, editorUser);
+
+      const res = await supertest
+        .post(SYNTHETICS_API_URLS.TRIGGER_MONITOR + `/${resp.id}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const result = res.body;
+      expect(typeof result.testRunId).to.eql('string');
+      expect(typeof result.configId).to.eql('string');
+      expect(result.schedule).to.eql({ number: '5', unit: 'm' });
+      expect(result.locations).to.eql([LOCAL_LOCATION]);
+
+      expect(omit(result.monitor, ['id', 'config_id'])).to.eql(
+        omit(newMonitor, ['id', 'config_id'])
+      );
+    });
+
+    it('works in non default space', async () => {
+      const { SPACE_ID } = await monitorTestService.addNewSpace();
+
+      const resp = await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .send(newMonitor)
+        .expect(200);
+
+      const res = await supertest
+        .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.TRIGGER_MONITOR}/${resp.body.id}`)
+        .set(editorUser.apiKeyHeader)
+        .set(samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const result = res.body;
+      expect(typeof result.testRunId).to.eql('string');
+      expect(typeof result.configId).to.eql('string');
+      expect(result.schedule).to.eql({ number: '5', unit: 'm' });
+      expect(result.locations).to.eql([LOCAL_LOCATION]);
+
+      expect(omit(result.monitor, ['id', 'config_id'])).to.eql(
+        omit(newMonitor, ['id', 'config_id'])
+      );
+    });
+  });
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.index.ts b/x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.index.ts
index abd875f8c17cf..7544d7d90f1d5 100644
--- a/x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.index.ts
+++ b/x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.index.ts
@@ -20,5 +20,6 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext)
     loadTestFile(require.resolve('../../apis/painless_lab'));
     loadTestFile(require.resolve('../../apis/saved_objects_management'));
     loadTestFile(require.resolve('../../apis/observability/slo'));
+    loadTestFile(require.resolve('../../apis/observability/synthetics'));
   });
 }
diff --git a/x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.index.ts b/x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.index.ts
index 4f21d708d4186..4f666fc5b3ebe 100644
--- a/x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.index.ts
+++ b/x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.index.ts
@@ -13,6 +13,7 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext)
     loadTestFile(require.resolve('../../apis/observability/alerting'));
     loadTestFile(require.resolve('../../apis/observability/dataset_quality'));
     loadTestFile(require.resolve('../../apis/observability/slo'));
+    loadTestFile(require.resolve('../../apis/observability/synthetics'));
     loadTestFile(require.resolve('../../apis/observability/infra'));
   });
 }
diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts
index e7df37f5aa312..8d3432f2e504e 100644
--- a/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts
+++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts
@@ -113,6 +113,11 @@ export function createServerlessTestConfig<T extends DeploymentAgnosticCommonSer
           ...svlSharedConfig.get('kbnTestServer.serverArgs'),
           ...kbnServerArgsFromController[options.serverlessProject],
           `--serverless=${options.serverlessProject}`,
+          // defined in MKI control plane. Necessary for Synthetics app testing
+          '--xpack.uptime.service.password=test',
+          '--xpack.uptime.service.username=localKibanaIntegrationTestsUser',
+          '--xpack.uptime.service.devUrl=mockDevUrl',
+          '--xpack.uptime.service.manifestUrl=mockDevUrl',
         ],
       },
       testFiles: options.testFiles,
diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts
index 1cf8493347a6a..c4b8c725df2d6 100644
--- a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts
+++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts
@@ -145,6 +145,10 @@ export function createStatefulTestConfig<T extends DeploymentAgnosticCommonServi
             basic: { 'cloud-basic': { order: 1 } },
           })}`,
           `--server.publicBaseUrl=${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`,
+          '--xpack.uptime.service.password=test',
+          '--xpack.uptime.service.username=localKibanaIntegrationTestsUser',
+          '--xpack.uptime.service.devUrl=mockDevUrl',
+          '--xpack.uptime.service.manifestUrl=mockDevUrl',
         ],
       },
     };
diff --git a/x-pack/test/api_integration/deployment_agnostic/services/synthetics_monitor.ts b/x-pack/test/api_integration/deployment_agnostic/services/synthetics_monitor.ts
new file mode 100644
index 0000000000000..e2bd2881db956
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/services/synthetics_monitor.ts
@@ -0,0 +1,240 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { RoleCredentials, SamlAuthProviderType } from '@kbn/ftr-common-functional-services';
+import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
+import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved_objects';
+import { EncryptedSyntheticsSavedMonitor } from '@kbn/synthetics-plugin/common/runtime_types';
+import { MonitorInspectResponse } from '@kbn/synthetics-plugin/public/apps/synthetics/state/monitor_management/api';
+import { v4 as uuidv4 } from 'uuid';
+import expect from '@kbn/expect';
+import { ProjectAPIKeyResponse } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/get_api_key';
+import moment from 'moment/moment';
+import { omit } from 'lodash';
+import { KibanaSupertestProvider } from '@kbn/ftr-common-functional-services';
+import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context';
+
+export class SyntheticsMonitorTestService {
+  private supertest: ReturnType<typeof KibanaSupertestProvider>;
+  private getService: DeploymentAgnosticFtrProviderContext['getService'];
+  public apiKey: string | undefined = '';
+  public samlAuth: SamlAuthProviderType;
+
+  constructor(getService: DeploymentAgnosticFtrProviderContext['getService']) {
+    this.supertest = getService('supertestWithoutAuth');
+    this.samlAuth = getService('samlAuth');
+    this.getService = getService;
+  }
+
+  generateProjectAPIKey = async (accessToPublicLocations = true, user: RoleCredentials) => {
+    const res = await this.supertest
+      .get(
+        SYNTHETICS_API_URLS.SYNTHETICS_PROJECT_APIKEY +
+          '?accessToElasticManagedLocations=' +
+          accessToPublicLocations
+      )
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .expect(200);
+    const result = res.body as ProjectAPIKeyResponse;
+    expect(result).to.have.property('apiKey');
+    const apiKey = result.apiKey?.encoded;
+    expect(apiKey).to.not.be.empty();
+    this.apiKey = apiKey;
+    return apiKey;
+  };
+
+  async getMonitor(
+    monitorId: string,
+    {
+      statusCode = 200,
+      space,
+      internal,
+      user,
+    }: {
+      statusCode?: number;
+      space?: string;
+      internal?: boolean;
+      user: RoleCredentials;
+    }
+  ) {
+    let url = SYNTHETICS_API_URLS.GET_SYNTHETICS_MONITOR.replace('{monitorId}', monitorId);
+    if (space) {
+      url = '/s/' + space + url;
+    }
+    if (internal) {
+      url += `?internal=${internal}`;
+    }
+    const apiResponse = await this.supertest
+      .get(url)
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .expect(200);
+
+    expect(apiResponse.status).eql(statusCode, JSON.stringify(apiResponse.body));
+
+    if (statusCode === 200) {
+      const {
+        created_at: createdAt,
+        updated_at: updatedAt,
+        id,
+        config_id: configId,
+        spaceId,
+      } = apiResponse.body;
+      expect(id).not.empty();
+      expect(configId).not.empty();
+      expect(spaceId).not.empty();
+      expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
+      return {
+        rawBody: omit(apiResponse.body, ['spaceId']),
+        body: {
+          ...omit(apiResponse.body, [
+            'created_at',
+            'updated_at',
+            'id',
+            'config_id',
+            'form_monitor_type',
+            'spaceId',
+          ]),
+        },
+      };
+    }
+    return apiResponse.body;
+  }
+
+  async addMonitor(monitor: any, user: RoleCredentials) {
+    const res = await this.supertest
+      .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .send(monitor)
+      .expect(200);
+
+    return res.body as EncryptedSyntheticsSavedMonitor;
+  }
+
+  async inspectMonitor(user: RoleCredentials, monitor: any, hideParams: boolean = true) {
+    const res = await this.supertest
+      .post(SYNTHETICS_API_URLS.SYNTHETICS_MONITOR_INSPECT)
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .send(monitor)
+      .expect(200);
+
+    // remove the id and config_id from the response
+    delete res.body.result?.publicConfigs?.[0].monitors[0].id;
+    delete res.body.result?.publicConfigs?.[0].monitors[0].streams[0].id;
+    delete res.body.result?.publicConfigs?.[0].monitors[0].streams[0].config_id;
+    delete res.body.result?.publicConfigs?.[0].monitors[0].streams[0].fields.config_id;
+    delete res.body.result?.publicConfigs?.[0].output.api_key;
+    delete res.body.result?.publicConfigs?.[0].license_issued_to;
+    delete res.body.result?.publicConfigs?.[0].stack_version;
+
+    return res.body as { result: MonitorInspectResponse; decodedCode: string };
+  }
+
+  async addProjectMonitors(project: string, monitors: any, user: RoleCredentials) {
+    if (this.apiKey) {
+      return this.supertest
+        .put(
+          SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+        )
+        .set(this.samlAuth.getInternalRequestHeader())
+        .set('authorization', `ApiKey ${this.apiKey}`)
+        .send({ monitors });
+    } else {
+      return this.supertest
+        .put(
+          SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT_UPDATE.replace('{projectName}', project)
+        )
+        .set(user.apiKeyHeader)
+        .set(this.samlAuth.getInternalRequestHeader())
+        .send({ monitors });
+    }
+  }
+
+  async deleteMonitorByJourney(
+    journeyId: string,
+    projectId: string,
+    space: string = 'default',
+    user: RoleCredentials
+  ) {
+    try {
+      const response = await this.supertest
+        .get(`/s/${space}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+        .query({
+          filter: `${syntheticsMonitorType}.attributes.journey_id: "${journeyId}" AND ${syntheticsMonitorType}.attributes.project_id: "${projectId}"`,
+        })
+        .set(user.apiKeyHeader)
+        .set(this.samlAuth.getInternalRequestHeader())
+        .expect(200);
+
+      const { monitors } = response.body;
+      if (monitors[0]?.id) {
+        await this.supertest
+          .delete(`/s/${space}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
+          .set(user.apiKeyHeader)
+          .set(this.samlAuth.getInternalRequestHeader())
+          .send({ ids: monitors.map((monitor: { id: string }) => monitor.id) })
+          .expect(200);
+      }
+    } catch (e) {
+      // eslint-disable-next-line no-console
+      console.error(e);
+    }
+  }
+
+  async addNewSpace() {
+    const SPACE_ID = `test-space-${uuidv4()}`;
+    const SPACE_NAME = `test-space-name ${uuidv4()}`;
+
+    const kibanaServer = this.getService('kibanaServer');
+
+    await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
+
+    return { SPACE_ID };
+  }
+
+  async deleteMonitor(
+    user: RoleCredentials,
+    monitorId?: string | string[],
+    statusCode = 200,
+    spaceId?: string
+  ) {
+    const deleteResponse = await this.supertest
+      .delete(
+        spaceId
+          ? `/s/${spaceId}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`
+          : SYNTHETICS_API_URLS.SYNTHETICS_MONITORS
+      )
+      .send({ ids: Array.isArray(monitorId) ? monitorId : [monitorId] })
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .expect(statusCode);
+
+    return deleteResponse;
+  }
+
+  async deleteMonitorByIdParam(
+    user: RoleCredentials,
+    monitorId?: string,
+    statusCode = 200,
+    spaceId?: string
+  ) {
+    const deleteResponse = await this.supertest
+      .delete(
+        spaceId
+          ? `/s/${spaceId}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}/${monitorId}`
+          : SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId
+      )
+      .send()
+      .set(user.apiKeyHeader)
+      .set(this.samlAuth.getInternalRequestHeader())
+      .expect(statusCode);
+
+    return deleteResponse;
+  }
+}
diff --git a/x-pack/test/api_integration/deployment_agnostic/services/synthetics_private_location.ts b/x-pack/test/api_integration/deployment_agnostic/services/synthetics_private_location.ts
new file mode 100644
index 0000000000000..8b3c95e0b9489
--- /dev/null
+++ b/x-pack/test/api_integration/deployment_agnostic/services/synthetics_private_location.ts
@@ -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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { v4 as uuidv4 } from 'uuid';
+import { RetryService } from '@kbn/ftr-common-functional-services';
+import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common';
+import { privateLocationSavedObjectName } from '@kbn/synthetics-plugin/common/saved_objects/private_locations';
+import { SyntheticsPrivateLocations } from '@kbn/synthetics-plugin/common/runtime_types';
+import { KibanaSupertestProvider } from '@kbn/ftr-common-functional-services';
+import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context';
+
+export const INSTALLED_VERSION = '1.1.1';
+
+export class PrivateLocationTestService {
+  private supertestWithAuth: ReturnType<typeof KibanaSupertestProvider>;
+  private readonly retry: RetryService;
+
+  constructor(getService: DeploymentAgnosticFtrProviderContext['getService']) {
+    this.supertestWithAuth = getService('supertest');
+    this.retry = getService('retry');
+  }
+
+  async installSyntheticsPackage() {
+    await this.supertestWithAuth
+      .post('/api/fleet/setup')
+      .set('kbn-xsrf', 'true')
+      .send()
+      .expect(200);
+    const response = await this.supertestWithAuth
+      .get(`/api/fleet/epm/packages/synthetics/${INSTALLED_VERSION}`)
+      .set('kbn-xsrf', 'true')
+      .expect(200);
+    if (response.body.item.status !== 'installed') {
+      await this.supertestWithAuth
+        .post(`/api/fleet/epm/packages/synthetics/${INSTALLED_VERSION}`)
+        .set('kbn-xsrf', 'true')
+        .send({ force: true })
+        .expect(200);
+    }
+  }
+
+  async addTestPrivateLocation(spaceId?: string) {
+    const apiResponse = await this.addFleetPolicy(uuidv4());
+    const testPolicyId = apiResponse.body.item.id;
+    return (await this.setTestLocations([testPolicyId], spaceId))[0];
+  }
+
+  async addFleetPolicy(name: string) {
+    return await this.retry.try(async () => {
+      const response = await this.supertestWithAuth
+        .post('/api/fleet/agent_policies?sys_monitoring=true')
+        .set('kbn-xsrf', 'true')
+        .send({
+          name,
+          description: '',
+          namespace: 'default',
+          monitoring_enabled: [],
+        });
+      return response;
+    });
+  }
+
+  async setTestLocations(testFleetPolicyIds: string[], spaceId?: string) {
+    const locations: SyntheticsPrivateLocations = testFleetPolicyIds.map((id, index) => ({
+      label: `Test private location ${id}`,
+      agentPolicyId: id,
+      id,
+      geo: {
+        lat: 0,
+        lon: 0,
+      },
+      isServiceManaged: false,
+    }));
+
+    await this.supertestWithAuth
+      .post(`/s/${spaceId || 'default'}/api/saved_objects/_bulk_create`)
+      .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
+      .set('kbn-xsrf', 'true')
+      .send(
+        locations.map((location) => ({
+          type: privateLocationSavedObjectName,
+          id: location.id,
+          attributes: location,
+          initialNamespaces: [spaceId ? spaceId : 'default'],
+        }))
+      )
+      .expect(200);
+
+    return locations;
+  }
+}