diff --git a/enterprise/frontend/src/embedding-sdk/README.md b/enterprise/frontend/src/embedding-sdk/README.md
index 59a4db6318a455ec0ad4846ec16817b0d7cfe76d..80bbbe2bea794fcffe4dce22a3603e47cef5f76c 100644
--- a/enterprise/frontend/src/embedding-sdk/README.md
+++ b/enterprise/frontend/src/embedding-sdk/README.md
@@ -178,7 +178,7 @@ yarn add @metabase/embedding-sdk-react
 
 Once installed, you need to import `MetabaseProvider` and provide it with a `config` object.
 
-```jsx
+```typescript jsx
 import React from "react";
 import { MetabaseProvider } from "@metabase/embedding-sdk-react";
 
@@ -218,7 +218,7 @@ After the SDK is configured, you can use embed your question using the `StaticQu
 The component has a default height, which can be customized by using the `height` prop.
 To inherit the height from the parent container, you can pass `100%` to the height prop.
 
-```jsx
+```typescript jsx
 import React from "react";
 import { MetabaseProvider, StaticQuestion } from "@metabase/embedding-sdk-react";
 
@@ -237,7 +237,7 @@ export default function App() {
 
 ### Embedding an interactive question (with drill-down)
 
-```jsx
+```typescript jsx
 import React from "react";
 import { MetabaseProvider, InteractiveQuestion } from "@metabase/embedding-sdk-react";
 
@@ -265,13 +265,13 @@ make your application unique. Therefore, we've added the ability to customize th
 
 Using the `InteractiveQuestion` with its default layout looks like this:
 
-```jsx
+```typescript jsx
 <InteractiveQuestion questionId={95} />
 ```
 
 To customize the layout, use namespaced components within the `InteractiveQuestion`. For example:
 
-```jsx
+```typescript jsx
 <InteractiveQuestion questionId={95}>
   <div
     style={{
@@ -340,7 +340,7 @@ After the SDK is configured, you can embed your dashboard using the `StaticDashb
 - **onLoad**: `(dashboard: Dashboard | null) => void;` - event handler that triggers after dashboard loads with all visible cards and their content.
 - **onLoadWithoutCards**: `(dashboard: Dashboard | null) => void;` - event handler that triggers after dashboard loads, but without its cards - at this stage dashboard title, tabs and cards grid is rendered, but cards content is not yet loaded.
 
-```jsx
+```typescript jsx
 import React from "react";
 import { MetabaseProvider, StaticDashboard } from "@metabase/embedding-sdk-react";
 
@@ -384,7 +384,7 @@ After the SDK is configured, you can embed your dashboard using the `Interactive
 - **onLoad**: `(dashboard: Dashboard | null) => void;` - event handler that triggers after dashboard loads with all visible cards and their content.
 - **onLoadWithoutCards**: `(dashboard: Dashboard | null) => void;` - event handler that triggers after dashboard loads, but without its cards - at this stage dashboard title, tabs and cards grid is rendered, but cards content is not yet loaded.
 
-```jsx
+```typescript jsx
 import React from "react";
 import { MetabaseProvider, InteractiveDashboard } from "@metabase/embedding-sdk-react";
 
@@ -605,13 +605,46 @@ const theme = {
 };
 ```
 
-### Implementing custom actions
+### Plugins
+
+The Metabase Embedding SDK supports plugins to customize the behavior of components. These plugins can be used in a
+global context or on a per-component basis. This list of plugins will continue to grow as we add more options to each
+component.
+
+To use a plugin globally, add the plugin to the `MetabaseProvider`'s `pluginsConfig` prop:
+
+```typescript jsx
+<MetabaseProvider
+  config={config}
+  theme={theme}
+  pluginsConfig={{
+    mapQuestionClickActions: [...] // Add your custom actions here
+  }}
+>
+  {children}
+</MetabaseProvider>
+```
+
+To use a plugin on a per-component basis, pass the plugin as a prop to the component:
+
+```typescript jsx
+<InteractiveQuestion
+  questionId={1}
+  plugins={{
+    mapQuestionClickActions: [...],
+  }}
+/>
+```
+
+#### _Interactive Question_
 
-`MetabaseProvider` also supports `pluginsConfig`. You can use `pluginsConfig` to customize the behavior of components. Currently we only allow configuring `mapQuestionClickActions` which lets you add custom actions or remove Metabase default actions in `InteractiveQuestion` component.
+###### `mapQuestionClickActions`
 
-We'll support more plugins in next releases. Please share your uses cases for us!
+This plugin allows you to add custom actions to
+the click-through menu of an interactive question. You can add and
+customize the appearance and behavior of the custom actions.
 
-```jsx
+```typescript jsx
 // You can provide a custom action with your own `onClick` logic.
 const createCustomAction = clicked => ({
   buttonType: "horizontal",
@@ -667,6 +700,115 @@ return (
 );
 ```
 
+#### _Interactive Dashboard_
+
+###### `dashcardMenu`
+
+This plugin allows you to add, remove, and modify the custom actions on the overflow menu of dashboard cards. The plugin
+appears as a dropdown menu on the top right corner of the card.
+
+The plugin's default configuration looks like this:
+
+```typescript jsx
+const plugins = {
+  dashboard: {
+    dashcardMenu: {
+      withDownloads: true,
+      withEditLink: true,
+      customItems: [],
+    },
+  },
+}
+```
+
+and can be used in the InteractiveDashboard like this:
+
+```typescript jsx
+<InteractiveDashboard
+  questionId={1}
+  plugins={{
+    dashboard: {
+      dashcardMenu: null,
+    },
+  }}
+/>
+```
+
+Take a look below to see how you can customize the plugin:
+
+###### Enabling/disabling default actions
+
+To remove the download button from the dashcard menu, set `withDownloads` to `false`. To remove the edit link from the
+dashcard menu, set `withEditLink` to `false`.
+
+```typescript jsx
+const plugins = {
+  dashboard: {
+    dashcardMenu: {
+       withDownloads: false,
+       withEditLink: false,
+       customItems: [],
+    }
+  }
+};
+```
+
+###### Adding custom actions to the existing menu:
+
+You can add custom actions to the dashcard menu by adding an object to the `customItems` array. Each element can either
+be an object or a function that takes in the dashcard's question, and outputs a list of custom items in the form of:
+
+```typescript jsx
+{
+  iconName: string;
+  label: string;
+  onClick: () => void;
+  disabled?: boolean;
+}
+```
+
+```typescript jsx
+const plugins: SdkPluginsConfig = {
+  dashboard: {
+    dashcardMenu: {
+      customItems: [
+        {
+          iconName: "chevronright",
+          label: "Custom action",
+          onClick: () => {
+            alert(`Custom action clicked`);
+          },
+        },
+        ({ question }) => {
+          return {
+            iconName: "chevronright",
+            label: "Custom action",
+            onClick: () => {
+              alert(`Custom action clicked ${question.name}`);
+            },
+          };
+        },
+      ],
+    },
+  },
+};
+```
+
+###### Replacing the existing menu with your own component
+
+If you want to replace the existing menu with your own component, you can do so by providing a function that returns a
+React component. This function also can receive the question as an argument.
+
+```typescript jsx
+const plugins: SdkPluginsConfig = {
+  dashboard: {
+    dashcardMenu: ({ question }) => (
+      <button onClick={() => console.log(question.name)}>Click me</button>
+    ),
+  },
+};
+```
+
 ### Adding global event handlers
 
 `MetabaseProvider` also supports `eventHandlers` configuration. This way you can add global handlers to react on events that happen in the SDK context.
@@ -697,7 +839,7 @@ return (
 
 In case you need to reload a Metabase component, for example, your users modify your application data and that data is used to render a question in Metabase. If you embed this question and want to force Metabase to reload the question to show the latest data, you can do so by using the `key` prop to force a component to reload.
 
-```jsx
+```typescript jsx
 // Inside your application component
 const [data, setData] = useState({});
 // This is used to force reloading Metabase components
@@ -731,7 +873,7 @@ return <InteractiveQuestion key={counter} questionId={yourQuestionId} />;
 
 You can customize how the SDK fetches the refresh token by specifying the `fetchRefreshToken` function in the `config` prop:
 
-```jsx
+```typescript jsx
 /**
   * This is the default implementation used in the SDK.
   * You can customize this function to fit your needs, such as adding headers or excluding cookies.
diff --git a/enterprise/frontend/src/embedding-sdk/lib/plugins.ts b/enterprise/frontend/src/embedding-sdk/lib/plugins.ts
index f0fce6836699209d19f027948123f3d78a63aee7..e6f8048c7e25b5f46d0b0b236269a79537a6c763 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/plugins.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/plugins.ts
@@ -2,7 +2,7 @@ import type { ReactNode } from "react";
 
 import type { DashCardMenuItem } from "metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenu";
 import type { ClickAction, ClickObject } from "metabase/visualizations/types";
-import type Question from "metabase-lib/v1/Question";
+import type { Card as QuestionType } from "metabase-types/api";
 
 export type SdkDataPointObject = Pick<
   ClickObject,
@@ -14,16 +14,16 @@ export type SdkClickActionPluginsConfig = (
   clickedDataPoint: SdkDataPointObject,
 ) => ClickAction[];
 
-type DashCardMenuCustomElement = ({
+export type DashCardMenuCustomElement = ({
   question,
 }: {
-  question: Question;
+  question: QuestionType;
 }) => ReactNode;
 
-type CustomDashCardMenuItem = ({
+export type CustomDashCardMenuItem = ({
   question,
 }: {
-  question?: Question;
+  question?: QuestionType;
 }) => DashCardMenuItem;
 
 export type DashCardCustomMenuItem = {
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenu.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenu.tsx
index dfc2cb90663e7c95332c987c95e5efe41fdd850f..a2568d7db01705a86388727144a84ebdda8b0210 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenu.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenu.tsx
@@ -2,6 +2,7 @@ import { useDisclosure } from "@mantine/hooks";
 import cx from "classnames";
 import { isValidElement, useState } from "react";
 
+import type { SdkPluginsConfig } from "embedding-sdk";
 import { useInteractiveDashboardContext } from "embedding-sdk/components/public/InteractiveDashboard/context";
 import CS from "metabase/css/core/index.css";
 import {
@@ -48,6 +49,20 @@ export type DashCardMenuItem = {
   disabled?: boolean;
 } & MenuItemProps;
 
+function isDashCardMenuEmpty(plugins?: SdkPluginsConfig) {
+  const dashcardMenu = plugins?.dashboard?.dashcardMenu;
+
+  if (!plugins || !dashcardMenu || typeof dashcardMenu !== "object") {
+    return false;
+  }
+
+  return (
+    dashcardMenu?.withDownloads === false &&
+    dashcardMenu?.withEditLink === false &&
+    !dashcardMenu?.customItems?.length
+  );
+}
+
 export const DashCardMenu = ({
   question,
   result,
@@ -76,7 +91,15 @@ export const DashCardMenu = ({
     },
   });
 
+  if (isDashCardMenuEmpty(plugins)) {
+    return null;
+  }
+
   const getMenuContent = () => {
+    if (typeof plugins?.dashboard?.dashcardMenu === "function") {
+      return plugins.dashboard.dashcardMenu({ question: question.card() });
+    }
+
     if (isValidElement(plugins?.dashboard?.dashcardMenu)) {
       return plugins.dashboard.dashcardMenu;
     }
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenuItems.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenuItems.tsx
index f0fc066bd9ddae24c226781328d908fdd99a3142..16159f88ffdbe3dc9677b5ddc153dc89a1af6c95 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenuItems.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardMenu/DashCardMenuItems.tsx
@@ -30,6 +30,7 @@ export const DashCardMenuItems = ({
     plugins,
     onEditQuestion = question => dispatch(editQuestion(question)),
   } = useInteractiveDashboardContext();
+
   const dashcardMenuItems = plugins?.dashboard?.dashcardMenu as
     | DashCardCustomMenuItem
     | undefined;
@@ -69,7 +70,9 @@ export const DashCardMenuItems = ({
       items.push(
         ...customItems.map(item => {
           const customItem =
-            typeof item === "function" ? item({ question }) : item;
+            typeof item === "function"
+              ? item({ question: question.card() })
+              : item;
 
           return {
             ...customItem,