diff --git a/superset-embedded-sdk/src/index.ts b/superset-embedded-sdk/src/index.ts
index 3944c8cc4f26599228f00c8fb391f16f03d55317..f0ae1cab42e9851620316bbe0ead55bc58da5a89 100644
--- a/superset-embedded-sdk/src/index.ts
+++ b/superset-embedded-sdk/src/index.ts
@@ -19,7 +19,7 @@
 
 import {
   DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY,
-  IFRAME_COMMS_MESSAGE_TYPE
+  IFRAME_COMMS_MESSAGE_TYPE,
 } from './const';
 
 // We can swap this out for the actual switchboard package once it gets published
@@ -34,51 +34,61 @@ import { getGuestTokenRefreshTiming } from './guestTokenRefresh';
 export type GuestTokenFetchFn = () => Promise<string>;
 
 export type UiConfigType = {
-  hideTitle?: boolean
-  hideTab?: boolean
-  hideChartControls?: boolean
-  emitDataMasks?: boolean
+  hideTitle?: boolean;
+  hideTab?: boolean;
+  hideChartControls?: boolean;
+  emitDataMasks?: boolean;
   filters?: {
-    [key: string]: boolean | undefined
-    visible?: boolean
-    expanded?: boolean
-  }
+    [key: string]: boolean | undefined;
+    visible?: boolean;
+    expanded?: boolean;
+  };
   urlParams?: {
-    [key: string]: any
-  }
-}
+    [key: string]: any;
+  };
+};
 
 export type EmbedDashboardParams = {
   /** The id provided by the embed configuration UI in Superset */
-  id: string
+  id: string;
   /** The domain where Superset can be located, with protocol, such as: https://superset.example.com */
-  supersetDomain: string
+  supersetDomain: string;
   /** The html element within which to mount the iframe */
-  mountPoint: HTMLElement
+  mountPoint: HTMLElement;
   /** A function to fetch a guest token from the Host App's backend server */
-  fetchGuestToken: GuestTokenFetchFn
+  fetchGuestToken: GuestTokenFetchFn;
   /** The dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded **/
-  dashboardUiConfig?: UiConfigType
+  dashboardUiConfig?: UiConfigType;
   /** Are we in debug mode? */
-  debug?: boolean
+  debug?: boolean;
   /** The iframe title attribute */
-  iframeTitle?: string
+  iframeTitle?: string;
   /** additional iframe sandbox attributes ex (allow-top-navigation, allow-popups-to-escape-sandbox) **/
-  iframeSandboxExtras?: string[]
+  iframeSandboxExtras?: string[];
   /** force a specific refererPolicy to be used in the iframe request **/
-  referrerPolicy?: ReferrerPolicy
-}
+  referrerPolicy?: ReferrerPolicy;
+};
 
 export type Size = {
-  width: number, height: number
-}
+  width: number;
+  height: number;
+};
 
+export type ObserveDataMaskCallbackFn = (
+  dataMask: Record<string, any> & {
+    crossFiltersChanged: boolean;
+    nativeFiltersChanged: boolean;
+  },
+) => void;
 export type EmbeddedDashboard = {
   getScrollSize: () => Promise<Size>;
   unmount: () => void;
   getDashboardPermalink: (anchor: string) => Promise<string>;
   getActiveTabs: () => Promise<string[]>;
-  getDataMasks: (callbackFn: (dataMasks: any[]) => void) => void;
+  observeDataMask: (
+    callbackFn: ObserveDataMaskCallbackFn,
+  ) => void;
+  getDataMask: () => Record<string, any>;
 };
 
 /**
@@ -91,7 +101,7 @@ export async function embedDashboard({
   fetchGuestToken,
   dashboardUiConfig,
   debug = false,
-  iframeTitle = "Embedded Dashboard",
+  iframeTitle = 'Embedded Dashboard',
   iframeSandboxExtras = [],
   referrerPolicy,
 }: EmbedDashboardParams): Promise<EmbeddedDashboard> {
@@ -103,55 +113,67 @@ export async function embedDashboard({
 
   log('embedding');
 
-  if (supersetDomain.endsWith("/")) {
+  if (supersetDomain.endsWith('/')) {
     supersetDomain = supersetDomain.slice(0, -1);
   }
 
   function calculateConfig() {
-    let configNumber = 0
-    if(dashboardUiConfig) {
-      if(dashboardUiConfig.hideTitle) {
-        configNumber += 1
+    let configNumber = 0;
+    if (dashboardUiConfig) {
+      if (dashboardUiConfig.hideTitle) {
+        configNumber += 1;
       }
-      if(dashboardUiConfig.hideTab) {
-        configNumber += 2
+      if (dashboardUiConfig.hideTab) {
+        configNumber += 2;
       }
-      if(dashboardUiConfig.hideChartControls) {
-        configNumber += 8
+      if (dashboardUiConfig.hideChartControls) {
+        configNumber += 8;
       }
       if (dashboardUiConfig.emitDataMasks) {
-        configNumber += 16
+        configNumber += 16;
       }
     }
-    return configNumber
+    return configNumber;
   }
 
   async function mountIframe(): Promise<Switchboard> {
     return new Promise(resolve => {
       const iframe = document.createElement('iframe');
-      const dashboardConfigUrlParams = dashboardUiConfig ? {uiConfig: `${calculateConfig()}`} : undefined;
-      const filterConfig = dashboardUiConfig?.filters || {}
-      const filterConfigKeys = Object.keys(filterConfig)
-      const filterConfigUrlParams = Object.fromEntries(filterConfigKeys.map(
-        key => [DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[key], filterConfig[key]]))
+      const dashboardConfigUrlParams = dashboardUiConfig
+        ? { uiConfig: `${calculateConfig()}` }
+        : undefined;
+      const filterConfig = dashboardUiConfig?.filters || {};
+      const filterConfigKeys = Object.keys(filterConfig);
+      const filterConfigUrlParams = Object.fromEntries(
+        filterConfigKeys.map(key => [
+          DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[key],
+          filterConfig[key],
+        ]),
+      );
 
       // Allow url query parameters from dashboardUiConfig.urlParams to override the ones from filterConfig
-      const urlParams = {...dashboardConfigUrlParams, ...filterConfigUrlParams, ...dashboardUiConfig?.urlParams}
-      const urlParamsString = Object.keys(urlParams).length ? '?' + new URLSearchParams(urlParams).toString() : ''
+      const urlParams = {
+        ...dashboardConfigUrlParams,
+        ...filterConfigUrlParams,
+        ...dashboardUiConfig?.urlParams,
+      };
+      const urlParamsString = Object.keys(urlParams).length
+        ? '?' + new URLSearchParams(urlParams).toString()
+        : '';
 
       // set up the iframe's sandbox configuration
-      iframe.sandbox.add("allow-same-origin"); // needed for postMessage to work
-      iframe.sandbox.add("allow-scripts"); // obviously the iframe needs scripts
-      iframe.sandbox.add("allow-presentation"); // for fullscreen charts
-      iframe.sandbox.add("allow-downloads"); // for downloading charts as image
-      iframe.sandbox.add("allow-forms"); // for forms to submit
-      iframe.sandbox.add("allow-popups"); // for exporting charts as csv
+      iframe.sandbox.add('allow-same-origin'); // needed for postMessage to work
+      iframe.sandbox.add('allow-scripts'); // obviously the iframe needs scripts
+      iframe.sandbox.add('allow-presentation'); // for fullscreen charts
+      iframe.sandbox.add('allow-downloads'); // for downloading charts as image
+      iframe.sandbox.add('allow-forms'); // for forms to submit
+      iframe.sandbox.add('allow-popups'); // for exporting charts as csv
       // additional sandbox props
       iframeSandboxExtras.forEach((key: string) => {
         iframe.sandbox.add(key);
       });
       // force a specific refererPolicy to be used in the iframe request
-      if(referrerPolicy) {
+      if (referrerPolicy) {
         iframe.referrerPolicy = referrerPolicy;
       }
 
@@ -167,20 +189,26 @@ export async function embedDashboard({
         // See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
         // we know the content window isn't null because we are in the load event handler.
         iframe.contentWindow!.postMessage(
-          { type: IFRAME_COMMS_MESSAGE_TYPE, handshake: "port transfer" },
+          { type: IFRAME_COMMS_MESSAGE_TYPE, handshake: 'port transfer' },
           supersetDomain,
           [theirPort],
-        )
+        );
         log('sent message channel to the iframe');
 
         // return our port from the promise
-        resolve(new Switchboard({ port: ourPort, name: 'superset-embedded-sdk', debug }));
+        resolve(
+          new Switchboard({
+            port: ourPort,
+            name: 'superset-embedded-sdk',
+            debug,
+          }),
+        );
       });
       iframe.src = `${supersetDomain}/embedded/${id}${urlParamsString}`;
       iframe.title = iframeTitle;
       //@ts-ignore
       mountPoint.replaceChildren(iframe);
-      log('placed the iframe')
+      log('placed the iframe');
     });
   }
 
@@ -210,17 +238,20 @@ export async function embedDashboard({
   const getDashboardPermalink = (anchor: string) =>
     ourPort.get<string>('getDashboardPermalink', { anchor });
   const getActiveTabs = () => ourPort.get<string[]>('getActiveTabs');
-  const getDataMasks = (callbackFn: (dataMasks: any[]) => void) => {
+  const getDataMask = () => ourPort.get<Record<string, any>>('getDataMask');
+  const observeDataMask = (
+    callbackFn: ObserveDataMaskCallbackFn,
+  ) => {
     ourPort.start();
-    ourPort.defineMethod("getDataMasks", callbackFn);
+    ourPort.defineMethod('observeDataMask', callbackFn);
   };
 
-
   return {
     getScrollSize,
     unmount,
     getDashboardPermalink,
     getActiveTabs,
-    getDataMasks,
+    observeDataMask,
+    getDataMask,
   };
 }
diff --git a/superset-frontend/src/embedded/api.tsx b/superset-frontend/src/embedded/api.tsx
index 9d37daf2e01bc92d6f43b3e5bfdf1aeefe88201b..2665916758b0c2a1cff0ee1dddcd927442a245a7 100644
--- a/superset-frontend/src/embedded/api.tsx
+++ b/superset-frontend/src/embedded/api.tsx
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { DataMaskStateWithId } from '@superset-ui/core';
 import getBootstrapData from 'src/utils/getBootstrapData';
 import { store } from '../views/store';
 import { getDashboardPermalink as getDashboardPermalinkUtil } from '../utils/urlUtils';
@@ -31,6 +32,7 @@ type EmbeddedSupersetApi = {
   getScrollSize: () => Size;
   getDashboardPermalink: ({ anchor }: { anchor: string }) => Promise<string>;
   getActiveTabs: () => string[];
+  getDataMask: () => DataMaskStateWithId;
 };
 
 const getScrollSize = (): Size => ({
@@ -61,8 +63,11 @@ const getDashboardPermalink = async ({
 
 const getActiveTabs = () => store?.getState()?.dashboardState?.activeTabs || [];
 
+const getDataMask = () => store?.getState()?.dataMask || {};
+
 export const embeddedApi: EmbeddedSupersetApi = {
   getScrollSize,
   getDashboardPermalink,
   getActiveTabs,
+  getDataMask,
 };
diff --git a/superset-frontend/src/embedded/index.tsx b/superset-frontend/src/embedded/index.tsx
index 5331c20421c902e8aa9017abc74ad2b7e28d5149..4263de96092d906daa829cbae9f8f16b67df504f 100644
--- a/superset-frontend/src/embedded/index.tsx
+++ b/superset-frontend/src/embedded/index.tsx
@@ -33,6 +33,7 @@ import { addDangerToast } from 'src/components/MessageToasts/actions';
 import ToastContainer from 'src/components/MessageToasts/ToastContainer';
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import { embeddedApi } from './api';
+import { getDataMaskChangeTrigger } from './utils';
 
 setupPlugins();
 
@@ -59,9 +60,20 @@ const EmbededLazyDashboardPage = () => {
   if (uiConfig?.emitDataMasks) {
     log('setting up Switchboard event emitter');
 
+    let previousDataMask = store.getState().dataMask;
+
     store.subscribe(() => {
-      const state = store.getState();
-      Switchboard.emit('getDataMasks', state.dataMask);
+      const currentState = store.getState();
+      const currentDataMask = currentState.dataMask;
+
+      // Only emit if the dataMask has changed
+      if (previousDataMask !== currentDataMask) {
+        Switchboard.emit('observeDataMask', {
+          ...currentDataMask,
+          ...getDataMaskChangeTrigger(currentDataMask, previousDataMask),
+        });
+        previousDataMask = currentDataMask;
+      }
     });
   }
 
@@ -226,6 +238,7 @@ window.addEventListener('message', function embeddedPageInitializer(event) {
       embeddedApi.getDashboardPermalink,
     );
     Switchboard.defineMethod('getActiveTabs', embeddedApi.getActiveTabs);
+    Switchboard.defineMethod('getDataMask', embeddedApi.getDataMask);
     Switchboard.start();
   }
 });
diff --git a/superset-frontend/src/embedded/utils.test.ts b/superset-frontend/src/embedded/utils.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06ce75afa0f1f4d3970c57db0914162729bdac51
--- /dev/null
+++ b/superset-frontend/src/embedded/utils.test.ts
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { DataMaskStateWithId } from '@superset-ui/core';
+import { cloneDeep } from 'lodash';
+import { getDataMaskChangeTrigger } from './utils';
+
+const dataMask: DataMaskStateWithId = {
+  '1': {
+    id: '1',
+    extraFormData: {},
+    filterState: {},
+    ownState: {},
+  },
+  '2': {
+    id: '2',
+    extraFormData: {},
+    filterState: {},
+    ownState: {},
+  },
+  'NATIVE_FILTER-1': {
+    id: 'NATIVE_FILTER-1',
+    extraFormData: {},
+    filterState: {
+      value: null,
+    },
+    ownState: {},
+  },
+  'NATIVE_FILTER-2': {
+    id: 'NATIVE_FILTER-2',
+    extraFormData: {},
+    filterState: {},
+    ownState: {},
+  },
+};
+
+it('datamask didnt change - both triggers set to false', () => {
+  const previousDataMask = cloneDeep(dataMask);
+  expect(getDataMaskChangeTrigger(dataMask, previousDataMask)).toEqual({
+    crossFiltersChanged: false,
+    nativeFiltersChanged: false,
+  });
+});
+
+it('a native filter changed - nativeFiltersChanged set to true', () => {
+  const previousDataMask = cloneDeep(dataMask);
+  previousDataMask['NATIVE_FILTER-1'].filterState!.value = 'test';
+  expect(getDataMaskChangeTrigger(dataMask, previousDataMask)).toEqual({
+    crossFiltersChanged: false,
+    nativeFiltersChanged: true,
+  });
+});
+
+it('a cross filter changed - crossFiltersChanged set to true', () => {
+  const previousDataMask = cloneDeep(dataMask);
+  previousDataMask['1'].filterState!.value = 'test';
+  expect(getDataMaskChangeTrigger(dataMask, previousDataMask)).toEqual({
+    crossFiltersChanged: true,
+    nativeFiltersChanged: false,
+  });
+});
diff --git a/superset-frontend/src/embedded/utils.ts b/superset-frontend/src/embedded/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8645dd7f08a3650741684a1e05d4ad4a51dbdbe2
--- /dev/null
+++ b/superset-frontend/src/embedded/utils.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { DataMaskStateWithId } from '@superset-ui/core';
+import { isEmpty, isEqual } from 'lodash';
+import { NATIVE_FILTER_PREFIX } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/utils';
+
+export const getDataMaskChangeTrigger = (
+  dataMask: DataMaskStateWithId,
+  previousDataMask: DataMaskStateWithId,
+) => {
+  let crossFiltersChanged = false;
+  let nativeFiltersChanged = false;
+
+  if (!isEmpty(dataMask) && !isEmpty(previousDataMask)) {
+    for (const key in dataMask) {
+      if (
+        key.startsWith(NATIVE_FILTER_PREFIX) &&
+        !isEqual(dataMask[key], previousDataMask[key])
+      ) {
+        nativeFiltersChanged = true;
+        break;
+      } else if (!isEqual(dataMask[key], previousDataMask[key])) {
+        crossFiltersChanged = true;
+        break;
+      }
+    }
+  }
+  return { crossFiltersChanged, nativeFiltersChanged };
+};