diff --git a/_docs/latest/ai/overview.md b/_docs/latest/ai/overview.md
index 9c7a975d54..44bebda771 100644
--- a/_docs/latest/ai/overview.md
+++ b/_docs/latest/ai/overview.md
@@ -27,7 +27,7 @@ Here are the different ways to use AI with Metabase:
**Best for: daily tasks in Metabase; granular control over people's AI usage.**
-Metabot is Metabase's built-in AI agent. Metabot can help you with most daily tasks around Metabase, like answering questions about your data, creating queries, generating SQL code, explaining charts, or creating Documents. If you're embedding Metabase into your product, you can get the Metabot agent through the [AI chat component](../embedding/components#ai-chat). See [non-exhaustive list of things Metabot can do](metabot#what-metabot-can-do), as well as its [limitations](metabot#current-limitations).
+Metabot is Metabase's built-in AI agent. Metabot can help you with most daily tasks around Metabase, like answering questions about your data, creating queries, generating SQL code, explaining charts, or creating Documents. If you're embedding Metabase into your product, you can get the Metabot agent through the [AI chat component](../embedding/ai-chat). See a [non-exhaustive list of things Metabot can do](metabot#what-metabot-can-do), as well as its [limitations](metabot#current-limitations).
**Controls:** Metabot will only see what the person using it can see. Metabot also comes with additional permission controls and usage limits so that you control who can use which Metabot tools (e.g. chat vs SQL generation) and how many tokens they can spend.
@@ -38,7 +38,7 @@ Metabot is Metabase's built-in AI agent. Metabot can help you with most daily ta
**Plans**: available on all plans. You can only use Metabase's AI Service on Metabase Cloud.
-See [full docs for Metabot](metabot) and [embedded AI chat](../embedding/components#ai-chat).
+See [full docs for Metabot](metabot) and [embedded AI chat](../embedding/ai-chat).
## MCP server
diff --git a/_docs/latest/ai/settings.md b/_docs/latest/ai/settings.md
index 1a957ac922..5253b157bf 100644
--- a/_docs/latest/ai/settings.md
+++ b/_docs/latest/ai/settings.md
@@ -110,10 +110,10 @@ Toggling off Metabot only turns off in-app Metabot features. People can still us
_Embedded tab._
-The **Embedded Metabot is enabled** toggle turns embedded Metabot on or off. The toggle affects both full-app embeds and modular embeds.
+The **Enable Embedded Metabot** toggle turns embedded Metabot on or off. The toggle affects both full-app embeds and modular embeds.
- [Full-app embedding](../embedding/full-app-embedding): The Metabot icon and keyboard shortcuts are only available when Metabot is enabled. Turning off Embedded Metabot will hide these icons and disable the keyboard shortcuts.
-- [Modular embedding](../embedding/modular-embedding): The toggle doesn't add Metabot anywhere; you have to explicitly include a chat component (like the SDK's [`MetabotQuestion`](../embedding/sdk/ai-chat)) in your application. If, however, you've added a component, and you turn off the Embedded Metabot toggle, your chat component will stop working, so you should also remove or hide the component in your application.
+- [Modular embedding](../embedding/modular-embedding): The toggle doesn't add Metabot anywhere; you have to explicitly include a chat component (like the SDK's [`MetabotQuestion`](../embedding/ai-chat)) in your application. If, however, you've added a component, and you turn off the Embedded Metabot toggle, your chat component will stop working, so you should also remove or hide the component in your application.
### Verified content
@@ -137,7 +137,11 @@ People can still @-mention items outside of this collection when prompting in AI
_Embedded tab._
-If you're embedding the Metabot component in an app, you can specify a different collection that embedded Metabot is allowed to use for creating queries. Click **Pick a different collection** to choose the collection (and its subcollections) that embedded Metabot can query.
+If you're embedding the Metabot component in an app, you can point embedded Metabot at a different collection to use for creating queries. Click **Pick a different collection** to choose the collection (and its subcollections) that embedded Metabot searches for metrics, models, and saved questions.
+
+Picking **Our analytics** is the same as picking no collection at all, so pick something narrower if you want the scoping to do anything. And once you set a collection, tables drop out of embedded Metabot's search results, so pick a collection with the metrics and models you want people building on.
+
+This setting narrows where embedded Metabot searches; it's _not_ a substitute for setting permissions. Embedded Metabot can still read and query anything the person using it has permissions for. Embedded Metabot can also see the items that person viewed recently, whichever collection those live in. Restricting Metabot to [verified content](#verified-content) narrows those recent items to verified, official, and [Library](../data-studio/library) content, but it doesn't confine them to the collection you picked. To control what data people can get to in an embed, set [data permissions](../permissions/embedding). See also [Set up AI chat in Metabase](../embedding/ai-chat#set-up-ai-chat-in-metabase).
### Prompt suggestions
diff --git a/_docs/latest/api.json b/_docs/latest/api.json
index ecf263d8ef..e66d4f738a 100644
--- a/_docs/latest/api.json
+++ b/_docs/latest/api.json
@@ -12175,6 +12175,215 @@
"required" : [ "payload_type" ]
} ]
},
+ "metabase.notification.api.notification.NotificationApiUpdateInput" : {
+ "description" : "::NotificationApiInput restricted to what `notification-update-spec` writes. On PUT the URL,\n not the body, identifies the target (RFC 9110 §9.3.4), so a client-sent id is stripped.",
+ "oneOf" : [ {
+ "type" : "object",
+ "properties" : {
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "creator_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "handlers" : {
+ "type" : "array",
+ "items" : {
+ "type" : "object",
+ "properties" : {
+ "template_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "recipients" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationRecipient"
+ }
+ },
+ "template" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.ChannelTemplateUserProvided"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_type" : { },
+ "channel" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.Channel"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "notification_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "required" : [ "channel_type" ]
+ }
+ },
+ "payload" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationCardUpdate"
+ },
+ "payload_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "payload_type" : {
+ "type" : "string",
+ "enum" : [ "notification/dashboard", "notification/system-event", "notification/testing", "notification/card" ]
+ },
+ "subscriptions" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationSubscription"
+ }
+ }
+ },
+ "required" : [ "payload_type", "payload" ]
+ }, {
+ "type" : "object",
+ "properties" : {
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "creator_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "handlers" : {
+ "type" : "array",
+ "items" : {
+ "type" : "object",
+ "properties" : {
+ "template_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "recipients" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationRecipient"
+ }
+ },
+ "template" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.ChannelTemplateUserProvided"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_type" : { },
+ "channel" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.Channel"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "notification_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "required" : [ "channel_type" ]
+ }
+ },
+ "payload_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "payload_type" : {
+ "type" : "string",
+ "enum" : [ "notification/dashboard", "notification/system-event", "notification/testing", "notification/card" ]
+ },
+ "subscriptions" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationSubscription"
+ }
+ }
+ },
+ "required" : [ "payload_type" ]
+ } ]
+ },
"metabase.notification.models.CreateNotificationCardParams" : {
"description" : "[[::NotificationCard]] on a create request, where the caller does not get to pick the id.",
"type" : "object",
@@ -12695,6 +12904,25 @@
},
"required" : [ "card_id" ]
},
+ "metabase.notification.models.NotificationCardUpdate" : {
+ "description" : "::NotificationCard restricted to what the update spec writes - `:id` comes from the URL's notification.",
+ "type" : "object",
+ "properties" : {
+ "card_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "send_condition" : {
+ "type" : "string",
+ "enum" : [ "goal_below", "has_result", "goal_above" ]
+ },
+ "send_once" : {
+ "type" : "boolean"
+ }
+ },
+ "required" : [ "card_id" ]
+ },
"metabase.notification.models.NotificationHandler" : {
"type" : "object",
"properties" : {
@@ -15142,6 +15370,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/agent" ]
}
},
@@ -19944,6 +20182,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/comment" ]
},
"post" : {
@@ -20022,6 +20270,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/comment" ]
}
},
@@ -21407,6 +21665,18 @@
"pattern" : "^[A-Za-z0-9_\\-]{21}$"
} ]
}
+ }, {
+ "in" : "query",
+ "name" : "dashboard_load_id",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string",
+ "minLength" : 1
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -22156,6 +22426,18 @@
"pattern" : "^[A-Za-z0-9_\\-]{21}$"
} ]
}
+ }, {
+ "in" : "query",
+ "name" : "dashboard_load_id",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string",
+ "minLength" : 1
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -23237,6 +23519,18 @@
"minimum" : 1
},
"description" : "value must be an integer greater than zero."
+ }, {
+ "in" : "query",
+ "name" : "include_editable_data_model",
+ "required" : false,
+ "schema" : {
+ "default" : false,
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -23788,7 +24082,14 @@
"operationId" : "get-api-database-virtual-db-datasets-schema",
"summary" : "GET /api/database/{virtual-db}/datasets/{schema}",
"description" : "Returns a list of Tables for the datasets virtual database.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "schema",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -23828,7 +24129,14 @@
"operationId" : "get-api-database-virtual-db-schema-schema",
"summary" : "GET /api/database/{virtual-db}/schema/{schema}",
"description" : "Returns a list of Tables for the saved questions virtual database.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "schema",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -26206,6 +26514,17 @@
"type" : "string",
"minLength" : 1
}
+ }, {
+ "in" : "query",
+ "name" : "locale",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -26899,6 +27218,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/custom-viz-plugin" ]
}
},
@@ -27062,6 +27391,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/custom-viz-plugin" ]
}
},
@@ -27346,6 +27685,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/data-complexity-score" ]
}
},
@@ -28526,6 +28875,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/library" ]
},
"get" : {
@@ -28561,6 +28920,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/library" ]
}
},
@@ -29617,6 +29986,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/permission_debug" ]
}
},
@@ -30252,6 +30631,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/remote-sync" ]
}
},
@@ -31085,7 +31474,15 @@
"operationId" : "put-api-ee-scim-v2-Groups-id",
"summary" : "PUT /api/ee/scim/v2/Groups/{id}",
"description" : "Update a group.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "id",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -31368,7 +31765,15 @@
"operationId" : "put-api-ee-scim-v2-Users-id",
"summary" : "PUT /api/ee/scim/v2/Users/{id}",
"description" : "Update a user.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "id",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -32234,6 +32639,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/serialization" ]
}
},
@@ -32684,6 +33099,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/tenant" ]
}
},
@@ -35228,6 +35653,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/geojson" ]
}
},
@@ -35256,6 +35691,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/geojson" ]
}
},
@@ -35450,7 +35895,16 @@
"operationId" : "get-api-health-inspector",
"summary" : "GET /api/health-inspector",
"description" : "Get a list of recent health check runs.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "limit",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "description" : "value must be an integer greater than zero."
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response",
@@ -39384,7 +39838,7 @@
"content" : {
"application/json" : {
"schema" : {
- "$ref" : "#/components/schemas/metabase.notification.api.notification.NotificationApiInput"
+ "$ref" : "#/components/schemas/metabase.notification.api.notification.NotificationApiUpdateInput"
}
}
}
@@ -41155,6 +41609,13 @@
"type" : "string",
"minLength" : 1
}
+ }, {
+ "in" : "query",
+ "name" : "value",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
} ],
"responses" : {
"2XX" : {
@@ -44547,6 +45008,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/session" ]
}
},
@@ -45124,7 +45595,7 @@
"post" : {
"operationId" : "post-api-slack-bug-report",
"summary" : "POST /api/slack/bug-report",
- "description" : "Send diagnostic information to the configured Slack channels.",
+ "description" : "Send diagnostic information to the configured Slack channels. Requires bug reporting to be enabled. The report is\n attributed to the current user when `diagnosticInfo.reporter` is true, and anonymous otherwise. The `{name, email}`\n form of `reporter` that clients before 0.64 send is treated as true; the identity in it is ignored.",
"parameters" : [ ],
"responses" : {
"2XX" : {
@@ -45144,10 +45615,145 @@
"type" : "object",
"properties" : {
"diagnosticInfo" : {
- "type" : "object"
+ "type" : "object",
+ "properties" : {
+ "entityName" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "frontendErrors" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "type" : "string"
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "entityInfo" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "reporter" : {
+ "oneOf" : [ {
+ "anyOf" : [ {
+ "type" : "object",
+ "properties" : {
+ "email" : {
+ "type" : "string"
+ },
+ "name" : {
+ "type" : "string"
+ }
+ },
+ "required" : [ "name", "email" ],
+ "additionalProperties" : false
+ }, {
+ "type" : "boolean"
+ } ]
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "url" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "backendErrors" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "bugReportDetails" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "userLogs" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "localizedEntityName" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "logs" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "queryResults" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "browserInfo" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "description" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "additionalProperties" : false
}
},
- "required" : [ "diagnosticInfo" ]
+ "required" : [ "diagnosticInfo" ],
+ "additionalProperties" : false
}
}
}
diff --git a/_docs/latest/dashboards/subscriptions.md b/_docs/latest/dashboards/subscriptions.md
index 19fe479e62..f4fa55ec7e 100644
--- a/_docs/latest/dashboards/subscriptions.md
+++ b/_docs/latest/dashboards/subscriptions.md
@@ -165,7 +165,7 @@ See [Remove Metabase branding from exports](../questions/exporting-results#remov
## Subscriptions in embedded dashboards exclude links
-Since people viewing embedded dashboards likely don't have direct access to your Metabase, Metabase will omit the links to Metabase items in subscriptions sent from embedded dashboards. That way people don't get broken links in their email. See [embedded dashboards](../embedding/components).
+Since people viewing embedded dashboards likely don't have direct access to your Metabase, Metabase will omit the links to Metabase items in subscriptions sent from embedded dashboards. That way people don't get broken links in their email. See [Embed a dashboard](../embedding/dashboard).
## Further reading
diff --git a/_docs/latest/embedding/ai-chat.md b/_docs/latest/embedding/ai-chat.md
new file mode 100644
index 0000000000..6a5a47cc86
--- /dev/null
+++ b/_docs/latest/embedding/ai-chat.md
@@ -0,0 +1,214 @@
+---
+version: v0.63
+has_magic_breadcrumbs: true
+show_category_breadcrumb: true
+show_title_breadcrumb: true
+category: Embedding
+title: Embed an AI chat
+source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/ai-chat.md'
+layout: new-docs
+summary: >-
+ Embed an AI chat in your app with a web component or the React SDK, so people
+ can ask questions of their data in natural language.
+redirect_from:
+ - /docs/latest/embedding/sdk/ai-chat
+latest: true
+---
+
+# Embed an AI chat
+
+
+
+{% include plans-blockquote.html feature="AI chat component" convert_pro_link_to_embedding=true%}
+
+You can embed an AI chat in your app, so people can ask questions of their data in natural language. Embedded chat is a focused version of [Metabot](../ai/metabot): it builds a question in the query builder and charts the answer.
+
+To build that question, embedded chat first searches your Metabase for the best thing to build on: a [metric](../data-modeling/metrics), a [model](../data-modeling/models), a saved question, or a table (tables drop out once you [scope the chat to a collection](#set-up-ai-chat-in-metabase)). Then it writes a query against whatever it picked. So embedded chat does look through your saved content, but as raw material for a new question, not as results to hand back. What people get back is always a new question they can drill into, and save if you [turn saving on](#let-people-save-questions-metabot-creates).
+
+Embedded chat won't write SQL, build or edit dashboards, or create metrics and models — it builds on the ones you already have. It also won't work as a search box for finding existing content. For those, chat will suggest doing the work in Metabase itself.
+
+AI chat requires the embed to use SSO authentication that signs people into your Metabase.
+
+## Try the AI chat demo
+
+For what AI chat looks like in action, check out the [AI chat component](https://embedded-analytics-sdk-demo.metabase.com/admin/analytics/new/ask-metabot) running on Shoppy, our modular embedding demo app. The demo's chat uses the [dedicated chart component](#ai-chat-with-dedicated-chart-panel).
+
+## Set up AI chat in Metabase
+
+An admin sets up embedded Metabot in your Metabase:
+
+1. Click the **grid** icon in the upper right.
+2. Select **Admin**.
+3. Click the **AI** tab.
+4. In the left sidebar, click **AI Settings**.
+5. The first card on the page is your AI provider connection. If the card says **Connect to an AI provider**, [set one up](../ai/settings#enable-ai-features). If you're self-hosting, that means [bringing your own API key](../ai/settings#bring-your-own-api-key). If the card says **AI providers**, you're already connected.
+6. In the **Metabot settings** card, click the **Embedded** tab.
+7. Turn on **Enable Embedded Metabot**.
+8. Under **Collection Embedded Metabot can use**, click **Pick a different collection** and choose the collection that holds the metrics, models, and saved questions embedded Metabot should build on.
+
+The collection you pick narrows what embedded chat finds when it searches for something to build on: that collection and everything under it. It doesn't limit what embedded chat can query, since people can still get to any data they have [permissions](../permissions/embedding) for. And once you set a collection, tables drop out of the chat's search results, so pick a collection with the metrics and models you want people building on.
+
+The **Embedded** tab configures Metabot in an embedded context, which is separate from the [Metabot](../ai/settings) in your own Metabase (which lives on the **Internal** tab). Both tabs control what each Metabot can see, not what it runs on: the AI provider, API key, and model are set once for the whole instance, above the **Metabot settings** card, and both Metabots use them.
+
+With embedded Metabot set up, there are two ways to add the chat to your app:
+
+- **[Web component](#web-component-ai-chat)**: the whole chat interface, chart and all, from a single tag.
+- **[React SDK](#react-sdk-ai-chat)**: the same interface from the `MetabotQuestion` component, or the [`useMetabot`](#build-a-custom-ai-chat-ui-with-usemetabot-react-sdk-only) hook if you'd rather build the interface yourself.
+
+Both the web component and `MetabotQuestion` let you [set where the chart appears](#set-where-the-chart-appears) and [whether people can save questions](#let-people-save-questions-metabot-creates).
+
+## Web component AI chat
+
+You can use the in-app wizard to generate the code:
+
+1. Open the command palette with Ctrl/Cmd+K and type **New embed**.
+2. For the experience, select **Metabot**.
+3. Pick a [layout](#set-where-the-chart-appears) and decide whether people can [save questions](#let-people-save-questions-metabot-creates).
+4. Click **Get code** and paste the snippet into your app.
+
+The **Metabot** option only shows up once an admin has turned on embedded Metabot, and only for SSO authentication. For what the rest of the generated snippet does, see [modular embedding](./modular-embedding).
+
+To render the AI chat interface:
+
+```html
+
+```
+
+### Web component `metabase-metabot` attributes
+
+{% include_file "{{ dirname }}/eajs/snippets/MetabaseMetabotAttributes.md" snippet="properties" %}
+
+Depending on the framework you're using, you may need to stringify attributes before passing them to the component. And if you surround an attribute's value with double quotes, use single quotes inside it.
+
+For all modular embeds, you can also set a `locale` in your page-level configuration to [translate embedded content](./translations). But [Metabot's own text isn't translated](./translations#the-ai-chat-component-isnt-translated).
+
+## React SDK AI chat
+
+{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
+
+To embed an AI chat with the [SDK](./sdk/introduction), use the `MetabotQuestion` component. Wrap the component in the `MetabaseProvider` component with your auth config.
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/ai-question.tsx" %}
+```
+
+### React SDK `MetabotQuestion` props
+
+- [Component](./sdk/api/MetabotQuestion)
+- [Props](./sdk/api/MetabotQuestionProps)
+
+{% include_file "{{ dirname }}/sdk/api/snippets/MetabotQuestionProps.md" snippet="properties" %}
+
+## Set where the chart appears
+
+- [Web component](#web-component-chart-layout)
+- [React SDK](#react-sdk-chart-layout)
+
+The `layout` setting positions the chart relative to the chat interface:
+
+- `auto` (default): Metabot uses the `stacked` layout on mobile screens, and a `sidebar` layout on larger screens.
+- `stacked`: the chart stacks on top of the chat interface.
+- `sidebar`: the chart appears to the left of the chat interface, which sits in a sidebar on the right.
+
+`layout` only applies to the built-in chat component. If you're building your own interface with [`useMetabot`](#build-a-custom-ai-chat-ui-with-usemetabot-react-sdk-only), you position the chart yourself.
+
+### Web component chart layout
+
+Set the `layout` attribute:
+
+```html
+
+```
+
+### React SDK chart layout
+
+Set the `layout` prop on `MetabotQuestion`:
+
+```tsx
+
+```
+
+## Let people save questions Metabot creates
+
+- [Web component](#web-component-question-saving)
+- [React SDK](#react-sdk-question-saving)
+
+Turning on the chat's save button lets people keep a question Metabot built. Saving is off by default.
+
+Setting a target collection is optional, but it's worth doing: it picks the collection that new questions land in, so people's work doesn't scatter across your Metabase. It also hides the collection picker in the save modal, so nobody has to decide where their question goes.
+
+### Web component question saving
+
+Turn saving on with `is-save-enabled="true"`, and set the collection with `target-collection`:
+
+```html
+
+```
+
+### React SDK question saving
+
+The equivalent props on `MetabotQuestion` are `isSaveEnabled` and `targetCollection`:
+
+```tsx
+
+```
+
+## Build a custom AI chat UI with `useMetabot` (React SDK only)
+
+If `MetabotQuestion`'s built-in layouts don't fit your app, use the `useMetabot` hook to read Metabot's conversation state directly and render your own UI. The hook gives you the messages, the chart the agent most recently produced, processing and error state, and actions to submit, cancel, retry, or reset the conversation.
+
+### AI chat with inline charts
+
+
+
+When an agent responds, the message can contain a `Chart` component. You can walk the agent's messages and render charts inline alongside the chat transcript:
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/use-metabot-inline-charts.tsx" %}
+```
+
+### AI chat with dedicated chart panel
+
+
+
+The `CurrentChart` component is bound to the latest chart the agent produced. Render `CurrentChart` once, and it will swap in new charts as the agent creates them. You'll want to filter chart messages out of the transcript so they don't render twice:
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/use-metabot-dedicated-chart.tsx" %}
+```
+
+### React SDK `useMetabot` return values
+
+- [Hook](./sdk/api/useMetabot)
+- [Return values](./sdk/api/UseMetabotResult)
+
+{% include_file "{{ dirname }}/sdk/api/snippets/UseMetabotResult.md" snippet="properties" %}
+
+### Guard against null while the SDK bundle loads
+
+`useMetabot` returns `null` until the SDK bundle has loaded and `` has mounted, so always guard before you use it. The SDK ships its Metabot internals in a code-split chunk that isn't available synchronously, which means an unguarded first render throws `Cannot read properties of null` as soon as you reach for `metabot.messages`, `metabot.submitMessage`, or anything else on the hook.
+
+### Bring your own markdown renderer
+
+`MetabotQuestion` renders agent text messages for you, markdown formatting and all, along with transcript scrolling and input styling. The `useMetabot` hook hands you the raw conversation state instead, so you can handle the markdown rendering.
+
+Agent text messages (the ones where `message.type === 'text'`) contain markdown (like links, bold, lists, inline code). The snippets above render `message.message` as plain text to keep them short, but in production you'll want to pass that text through a markdown renderer, like `react-markdown` or `markdown-to-jsx`, so links and formatting come out right.
+
+### Strip links back to Metabase
+
+Agent text can include links pointing back to the Metabase it's running against, like a link to a chart the agent just created. Opening one requires an authenticated Metabase session, so people viewing your app will hit a login screen. Strip those links out when you render the message, or swap them for a route in your own app.
+
+## Further reading
+
+- [Modular embedding components](./components)
+- [Metabot](../ai/metabot)
+- [Metabot settings](../ai/settings)
+- [Embed a chart](./chart)
+- [Embed a dashboard](./dashboard)
+- [Appearance](./appearance)
+- [Authentication](./authentication)
+- [Modular embedding](./modular-embedding)
+- [Modular embedding SDK](./sdk/introduction)
diff --git a/_docs/latest/embedding/browser.md b/_docs/latest/embedding/browser.md
index 3975a86a85..db2b49fda7 100644
--- a/_docs/latest/embedding/browser.md
+++ b/_docs/latest/embedding/browser.md
@@ -152,3 +152,4 @@ One quirk: when someone clicks on a collection, `CollectionBrowser` navigates in
- [Collection permissions](../permissions/collections)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
+- [Modular embedding components](./components)
diff --git a/_docs/latest/embedding/chart.md b/_docs/latest/embedding/chart.md
index 94fbfd78c3..c83d067a61 100644
--- a/_docs/latest/embedding/chart.md
+++ b/_docs/latest/embedding/chart.md
@@ -287,9 +287,9 @@ For both the SDK props (`initialSqlParameters`, `sqlParameters`, and `onSqlParam
To hide a parameter from the question's UI, use the [`hidden-parameters`](./question-reference#web-component-metabase-question-attributes) attribute (web component) or the `hiddenParameters` prop (SDK). Both work in any modular embed, on any plan.
-In practice you'll reach for them on [SSO embeds](./introduction#components-with-sso-authentication), where every parameter on the question shows up by default. On a [guest embed](./guest-embedding), a parameter that you haven't set to **Editable** or **Locked** is already hidden.
+In practice you'll reach for them on an [SSO embed](./introduction#components-with-sso-authentication), where every parameter on the question shows up by default. On a [guest embed](./guest-embedding#configuring-parameters), a parameter that you haven't set to **Editable** or **Locked** is already hidden, so the embed wizard won't generate `hidden-parameters` for you. You can still add the attribute by hand to hide a parameter you've made editable.
-Hiding a parameter declutters the UI; it doesn't restrict what people can query. Setting a value with `initial-sql-parameters` and then hiding the widget isn't a secure way to filter data, because your app sets that value in the browser. To restrict the data itself, see [Show people only their own data](#show-people-only-their-own-data).
+Hiding a parameter declutters the UI; it doesn't restrict what people can query. Setting a value with `initial-sql-parameters` and then hiding the widget isn't a secure way to filter data, because your app sets that value in the browser. Instead, see [Show people only their own data](#show-people-only-their-own-data), or set [data permissions](../permissions/embedding).
## Let people set up alerts on a question
@@ -356,4 +356,5 @@ On the OSS and Starter plans, Metabase adds a "Powered by Metabase" banner to gu
- [Guest embeds](./guest-embedding)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
-- [AI chat](./sdk/ai-chat)
+- [Modular embedding components](./components)
+- [Embed an AI chat](./ai-chat)
diff --git a/_docs/latest/embedding/components.md b/_docs/latest/embedding/components.md
index 4a35a155a3..5296b17207 100644
--- a/_docs/latest/embedding/components.md
+++ b/_docs/latest/embedding/components.md
@@ -7,35 +7,39 @@ category: Embedding
title: Modular embedding components
source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/components.md'
layout: new-docs
-summary: 'Embed dashboards, questions, query builder, AI chat, and a collection browser.'
+summary: >-
+ A map of the modular embedding docs: dashboards, charts, the query builder, a
+ collection browser, and AI chat.
latest: true
---
# Modular embedding components
-There are different components you can embed, each with various options.
+Modular embedding gives you a handful of components you can drop into your app, with either web components or the React SDK. This page maps out the docs for each one.
-> While you can use component parameters to show or hide parts of the embedded component, these parameters are _not_ a substitute for [permissions](../permissions/start). Even if you hide stuff, people could still grab their token from the frontend and use it to query the Metabase API.
-
-This page covers what you can embed. For theming your embeds, see [Appearance](./appearance).
-
-> Depending on the framework you're using, you may need to stringify attributes before passing them to the embedded components.
+> While you can use attributes and props to show or hide parts of an embedded component, they're _not_ a substitute for [permissions](../permissions/start). Even if you hide stuff, people could still grab their token from the frontend and use it to query the Metabase API.
## Dashboard
+Embed a dashboard view-only, interactive, or editable, and let people create dashboards from your app.
+
- [Embed a dashboard](./dashboard)
- [Dashboard component reference](./dashboard-reference), for `` attributes and SDK props
## Question
+Embed a single chart, or the query builder and SQL editor so people can build questions from scratch.
+
- [Embed a chart](./chart)
-- [Embed a query editor](./query-builder)
+- [Embed the query builder](./query-builder)
- [Question component reference](./question-reference), for `` attributes and SDK props
## Browser
{% include plans-blockquote.html feature="Browser component" convert_pro_link_to_embedding=true%}
+Embed a browsable collection, so people can find and open dashboards and questions themselves.
+
- [Embed a collection browser](./browser)
- [Browser component reference](./browser-reference), for `` attributes and SDK props
@@ -43,33 +47,24 @@ This page covers what you can embed. For theming your embeds, see [Appearance](.
{% include plans-blockquote.html feature="AI chat component" convert_pro_link_to_embedding=true%}
-AI chat component is only available for authenticated modular embeds. It's unavailable for [Guest embeds](./guest-embedding).
-
-To render the AI chat interface:
-
-```html
-
-```
-
-If you're using the SDK, you can use either the [`MetabotQuestion`](./sdk/ai-chat#example) component or the [`useMetabot`](./sdk/ai-chat#building-custom-ai-chat-uis-with-usemetabot) hook for a custom UI.
-
-### Attributes
-
-{% include_file "{{ dirname }}/eajs/snippets/MetabaseMetabotAttributes.md" snippet="properties" %}
-
-## Customizing loader and error components
+Embed an AI chat, so people can ask questions of their data in natural language.
-{% include plans-blockquote.html feature="Customizing loader and error components" convert_pro_link_to_embedding=true%}
+- [Embed an AI chat](./ai-chat), for the `` attributes, the `MetabotQuestion` props, and the `useMetabot` hook
-If you're using the [modular embedding SDK](./sdk/introduction), you can provide your own components for loading and error states by specifying `loaderComponent` and `errorComponent` as props to `MetabaseProvider`.
+## Things you can only do with the React SDK
-```tsx
-{% include_file "{{ dirname }}/sdk/snippets/appearance/customizing-loader-and-components.tsx" snippet="imports" %}
+Web components cover the components above. A few features are React-only, because they take React components or hooks that an HTML attribute can't carry. For those, use the [Modular embedding SDK](./sdk/introduction).
-{% include_file "{{ dirname }}/sdk/snippets/appearance/customizing-loader-and-components.tsx" snippet="example" %}
-```
+- [Plugins](./sdk/plugins), to customize component menus and click actions
+- [Actions](./sdk/actions), to run Metabase actions from your app with the `useAction` hook
+- [Custom question layouts](./question-reference#customize-the-layout-of-an-interactive-chart), to lay out a question yourself with namespaced `InteractiveQuestion` components
+- [Customize loading, error, and empty states](./sdk/loading-and-errors), to swap in your own loading and error components, and your own no-results image
## Further reading
- [Appearance](./appearance)
-- [Modular embedding SDK](./sdk/introduction).
+- [Modular embedding SDK config](./sdk/config), for the `MetabaseProvider` props
+- [Modular embedding parameters](./parameters)
+- [Translating embeds](./translations)
+- [Authentication](./authentication)
+- [Modular embedding SDK](./sdk/introduction)
diff --git a/_docs/latest/embedding/dashboard.md b/_docs/latest/embedding/dashboard.md
index e18e567d61..c43627360c 100644
--- a/_docs/latest/embedding/dashboard.md
+++ b/_docs/latest/embedding/dashboard.md
@@ -563,4 +563,5 @@ On the OSS and Starter plans, Metabase adds a "Powered by Metabase" banner to gu
- [Guest embeds](./guest-embedding)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
-- [AI chat](./sdk/ai-chat)
+- [Modular embedding components](./components)
+- [Embed an AI chat](./ai-chat)
diff --git a/_docs/latest/embedding/guest-embedding.md b/_docs/latest/embedding/guest-embedding.md
index 0908a33be5..6260e41d2d 100644
--- a/_docs/latest/embedding/guest-embedding.md
+++ b/_docs/latest/embedding/guest-embedding.md
@@ -42,7 +42,7 @@ To create a guest embed:
3. Select **Embed**.
4. Under **Authentication**, select **Guest**.
5. Optional: [customize the appearance of the embed](./appearance)
-6. Optional: [Add parameters to the embed](./components).
+6. Optional: [set the visibility of each parameter](#configuring-parameters).
7. Click **Publish**.
8. Get the code snippet that the wizard generates and add it to your app.
@@ -50,7 +50,7 @@ To create a guest embed:
## Notes on the code the wizard generates
-You can edit the code (see [components](./components) and [appearance](./appearance)). But here's an overview of the code the wizard generates, and where to put it.
+You can edit the code (see the [question component reference](./question-reference), the [dashboard component reference](./dashboard-reference), and [appearance](./appearance)). But here's an overview of the code the wizard generates, and where to put it.
### Client-side code
@@ -122,7 +122,7 @@ You can set different attributes to enable/disable UI. Here are some example att
\* Disabling downloads is only available on [Pro](/product/pro) and [Enterprise](/product/enterprise) plans.
-Attributes will differ based on the type of thing you're embedding. Guest embeds have fewer options than embeds that use SSO. See more on [components and their attributes](./components).
+Attributes will differ based on the type of thing you're embedding. Guest embeds have fewer options than embeds that use SSO. For every attribute, see the [dashboard component reference](./dashboard-reference) and the [question component reference](./question-reference).
### Customizing appearance of guest embeds
@@ -549,7 +549,8 @@ Guest embeds can't take advantage of:
- [Drill-through](../questions/visualizations/drill-through)
- [Usage analytics](../usage-and-performance-tools/usage-analytics)
- [Query builder](../questions/query-builder/editor)
-- [AI chat](./sdk/ai-chat)
+- [AI chat](./ai-chat)
+- [Custom visualizations](./sdk/config#custom-visualizations)
For those features, check out [Modular embedding with SSO](./modular-embedding).
diff --git a/_docs/latest/embedding/images/custom-error.png b/_docs/latest/embedding/images/custom-error.png
new file mode 100644
index 0000000000..4a916b6f0c
Binary files /dev/null and b/_docs/latest/embedding/images/custom-error.png differ
diff --git a/_docs/latest/embedding/introduction.md b/_docs/latest/embedding/introduction.md
index e60be6b994..27f0afde2e 100644
--- a/_docs/latest/embedding/introduction.md
+++ b/_docs/latest/embedding/introduction.md
@@ -59,7 +59,7 @@ All SSO options require a Pro or Enterprise plan.
| [Drill-through menus](../questions/visualizations/drill-through) | ✅ | ❌ |
| [Query builder](../questions/query-builder/editor) | ✅ | ❌ |
| [SQL editor](../questions/native-editor/writing-sql) | ✅ | ❌ |
-| [AI chat](./sdk/ai-chat) | ✅ | ❌ |
+| [AI chat](./ai-chat) | ✅ | ❌ |
| [Collection browser](./browser) | ✅ | ❌ |
| Advanced [Tenant](./tenants) and [permissions](../permissions/embedding) management | ✅ | ❌ |
| [Advanced theming](./appearance) | ✅ | ❌ |
diff --git a/_docs/latest/embedding/modular-embedding.md b/_docs/latest/embedding/modular-embedding.md
index 47ae6b1e50..8d69adca37 100644
--- a/_docs/latest/embedding/modular-embedding.md
+++ b/_docs/latest/embedding/modular-embedding.md
@@ -117,7 +117,7 @@ The code snippet has three parts:
- Loading the modular embedding library from your Metabase instance.
- Setting global configuration settings, like the URL of your Metabase and the `theme`. See [Page-level config](#page-level-config).
-- The component(s) to embed, with their parameters. See [Components](./components).
+- The component(s) to embed, with their attributes. To pick a component, see [Modular embedding components](./components). For the attributes each one takes, see the [dashboard](./dashboard-reference), [question](./question-reference), and [browser](./browser-reference) component references.
Here's an example snippet:
@@ -173,7 +173,7 @@ In addition to this, we consider shared accounts to be unfair usage. Fair usage
The exact customization options you see will depend on which type of entity you're embedding. These are the settings for authenticated embeds available on [Pro](/product/pro) and [Enterprise](/product/enterprise) plans. For guest embeds (OSS and paid), see [guest embedding options](./guest-embedding#component-attributes).
-When you're creating a new embed using **Admin > Embedding > Setup guide > Embed in your code**, you'll see some or all of the following customization options in the interactive creation flow. These options correspond to parameters in [components](./components).
+When you're creating a new embed using **Admin > Embedding > Setup guide > Embed in your code**, you'll see some or all of the following customization options in the interactive creation flow. These options correspond to attributes on the component you're embedding. For each attribute, see the [dashboard](./dashboard-reference) and [question](./question-reference) component references.
- **Allow people to drill through on data points**: determines whether people can interact with the chart (or charts on a dashboard). Interactivity includes [drilling down](../questions/visualizations/drill-through) to individual records from aggregated questions, filtering on click, zooming in, etc. Disabling drill-through for an embedded _question_ also disables people's ability to add filters and summaries.
diff --git a/_docs/latest/embedding/sdk/ai-chat.md b/_docs/latest/embedding/sdk/ai-chat.md
deleted file mode 100644
index 77dc99afaf..0000000000
--- a/_docs/latest/embedding/sdk/ai-chat.md
+++ /dev/null
@@ -1,98 +0,0 @@
----
-version: v0.63
-has_magic_breadcrumbs: true
-show_category_breadcrumb: true
-show_title_breadcrumb: true
-category: Embedding
-title: Modular embedding SDK - AI chat
-source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/sdk/ai-chat.md'
-layout: new-docs
-summary: >-
- Embed an AI chat component in your app that can create queries from natural
- language questions.
-latest: true
----
-
-# Modular embedding SDK - AI chat
-
-
-
-{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
-
-You can embed an AI chat in your application similar to [Metabot](../modular-embedding) in Metabase.
-
-Embedded Metabot is a more focused version of [Metabot](../../ai/metabot) designed to work well in an embedded context. Embedded Metabot can only display ad-hoc questions and metrics; it doesn't know about dashboards.
-
-To help embedded Metabot more easily find and focus on the data you care about most, select the collection containing the models and metrics it should be able to use to create queries.
-
-If you're embedding the Metabot component in an app, you can specify a different collection that embedded Metabot is allowed to use for creating queries.
-
-## Chat preview
-
-You can check out a [demo of the AI chat component](https://embedded-analytics-sdk-demo.metabase.com/admin/analytics/new/ask-metabot) on our Shoppy demo site.
-
-## Example
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/ai-question.tsx" %}
-```
-
-## `MetabotQuestion` props
-
-{% include_file "{{ dirname }}/api/snippets/MetabotQuestionProps.md" snippet="properties" %}
-
-## API reference
-
-- [Component](./api/MetabotQuestion)
-- [Props](./api/MetabotQuestionProps)
-
-## Setting up AI chat
-
-To configure your embedded AI chat in your Metabase:
-
-1. Click the **grid** icon in the upper right.
-2. Select **Admin**.
-3. Click the **AI** tab.
-4. In the left sidebar, click **Embedded Metabot**.
-
-When embedding the Metabot component in your app, you should specify a collection that embedded Metabot is allowed to use for creating queries. Embedded Metabot will only have access to that collection.
-
-For tips and more, see [Metabot settings](../../ai/settings).
-
-## Layout
-
-Use the `layout` prop to specify which layout to use for the Metabot component:
-
-- `auto` (default): Metabot uses the `stacked` layout on mobile screens, and a `sidebar` layout on larger screens.
-- `stacked`: the question visualization stacks on top of the chat interface.
-- `sidebar`: the question visualization appears to the left of the chat interface, which is on a sidebar on the right.
-
-## Building custom AI chat UIs with `useMetabot`
-
-If `MetabotQuestion`'s built-in layouts don't fit your app, use the `useMetabot` hook to read Metabot's conversation state directly and render your own UI. The hook gives you the messages, the chart the agent most recently produced, processing and error state, and actions to submit, cancel, retry, or reset the conversation.
-
-### AI chat with inline charts
-
-
-
-When an agent responds, the message can contain a `Chart` component. You can walk the agent's messages and render charts inline alongside the chat transcript:
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/use-metabot-inline-charts.tsx" %}
-```
-
-### AI chat with dedicated chart panel
-
-
-
-The `CurrentChart` component is bound to the latest chart the agent produced. Render `CurrentChart` once, and it will swap in new charts as the agent creates them. You'll want to filter chart messages out of the transcript so they don't render twice:
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/use-metabot-dedicated-chart.tsx" %}
-```
-
-### Notes on `useMetabot`
-
-- **Guard against null while waiting for the SDK bundle**: `useMetabot` returns `null` until the SDK bundle has loaded and `` has mounted. Always guard before use. If you don't guard it, the first render will throw `Cannot read properties of null` when you reach for `metabot.messages`, `metabot.submitMessage`, etc., because the SDK ships its Metabot internals via a code-split chunk that isn't available synchronously.
-- **Bring your own Markdown renderer**: `MetabotQuestion` renders agent text messages internally, including markdown formatting, transcript scrolling, and input styling. The `useMetabot` hook hands you the raw conversation state, which means you own the rendering. In particular, agent text messages (`message.type === 'text'`) contain **markdown**: links, bold, lists, inline code. The snippets above render `message.message` as plain text for brevity, but production usage should pass the text through a markdown renderer (`react-markdown`, `markdown-to-jsx`, or your own) so links and formatting display correctly.
-- **Strip links returned by the agent**: the agent text may include links pointing back to the host Metabase (like a link to a chart it created). Those links require an authenticated Metabase session, so people won't be able to view the links.
diff --git a/_docs/latest/embedding/sdk/config.md b/_docs/latest/embedding/sdk/config.md
index 058dbb6fda..735aca8318 100644
--- a/_docs/latest/embedding/sdk/config.md
+++ b/_docs/latest/embedding/sdk/config.md
@@ -57,6 +57,10 @@ Omitting the prop, or passing an empty array, turns off custom visualizations. C
For security, the SDK runs each custom visualization's code in an isolated sandbox, so a visualization can't reach your app or make network requests. The sandbox doesn't block passive image loads, though. A visualization can still trigger outbound requests through `
` tags or CSS `url()`. To limit where custom visualizations can load images from, set a Content Security Policy with an `img-src` allowlist in your app (the core Metabase app does this with [Restrict image domains](../../configuring-metabase/settings#restrict-image-domains)). [Only add visualizations you trust](../../questions/visualizations/custom#only-add-visualizations-you-trust).
+## Loading and error components
+
+The `loaderComponent` and `errorComponent` props swap the SDK's default loading and error screens for your own React components. See [Customize loading, error, and empty states](./loading-and-errors).
+
## Global event handlers
You can listen for events by defining the `eventHandlers` prop for `MetabaseProvider`.
diff --git a/_docs/latest/embedding/sdk/introduction.md b/_docs/latest/embedding/sdk/introduction.md
index e689bc547d..6879dda82b 100644
--- a/_docs/latest/embedding/sdk/introduction.md
+++ b/_docs/latest/embedding/sdk/introduction.md
@@ -114,13 +114,14 @@ Start with one of the quickstarts, then see these pages for more info on compone
- [Authentication](../authentication)
- [Embed a chart](../chart)
-- [AI chat](./ai-chat)
+- [Embed an AI chat](../ai-chat)
- [Embed a dashboard](../dashboard)
- [Actions](./actions)
- [Appearance](../appearance)
- [Embed a collection browser](../browser)
- [Plugins](./plugins)
- [Config](./config)
+- [Customize loading, error, and empty states](./loading-and-errors)
- [Versioning](./version)
- [Notes on Next.js](./next-js)
diff --git a/_docs/latest/embedding/sdk/loading-and-errors.md b/_docs/latest/embedding/sdk/loading-and-errors.md
new file mode 100644
index 0000000000..56ba504949
--- /dev/null
+++ b/_docs/latest/embedding/sdk/loading-and-errors.md
@@ -0,0 +1,62 @@
+---
+version: v0.63
+has_magic_breadcrumbs: true
+show_category_breadcrumb: true
+show_title_breadcrumb: true
+category: Embedding
+title: 'Customize loading, error, and empty states'
+source_url: >-
+ https://github.com/metabase/metabase/blob/master/docs/embedding/sdk/loading-and-errors.md
+layout: new-docs
+summary: >-
+ Replace the modular embedding SDK's default loading screen, error screen, and
+ no-results image with your own.
+latest: true
+---
+
+# Customize loading, error, and empty states
+
+
+
+{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
+
+While an embedded component is loading, when it fails, or when a query comes back with no rows, the [modular embedding SDK](./introduction) renders a screen of its own. You can swap in your own React components and images instead, so all three states match the rest of your app.
+
+These customizations are SDK only. `loaderComponent` and `errorComponent` are props on `MetabaseProvider`, the no-results image comes from a [plugin](./plugins), and there's no web component equivalent for any of them.
+
+## Replace the loading and error components
+
+Pass `loaderComponent` and `errorComponent` to `MetabaseProvider`. Every embedded component inside that provider picks them up.
+
+```tsx
+{% include_file "{{ dirname }}/snippets/appearance/customizing-loader-and-components.tsx" snippet="imports" %}
+
+{% include_file "{{ dirname }}/snippets/appearance/customizing-loader-and-components.tsx" snippet="example" %}
+```
+
+`loaderComponent` receives an optional `label` prop with the loading message, which you can render or ignore. `errorComponent` receives the error details, so you can decide how much of the error to show, and where to put it.
+
+## Error component props
+
+These are the props Metabase passes to your `errorComponent`. The `type` prop tells you how Metabase intended to display the error: `relative` errors sit in the flow of the component, while `fixed` errors are meant to overlay the page, like a toast.
+
+{% include_file "{{ dirname }}/api/snippets/SdkErrorComponentProps.md" snippet="properties" %}
+
+## Replace the no-results illustration
+
+By default, Metabase displays a sailboat image when a query returns no results. To use a different image, set the `getNoDataIllustration` and `getNoObjectIllustration` plugins.
+
+Unlike `loaderComponent` and `errorComponent`, these are [plugins](./plugins), so they go in `pluginsConfig` rather than being props of their own. And instead of a React component, each one returns a base64-encoded image:
+
+```typescript
+{% include_file "{{ dirname }}/snippets/plugins/custom-images.tsx" snippet="example" %}
+```
+
+`getNoDataIllustration` covers a query that came back with no rows. `getNoObjectIllustration` covers a search that turned up nothing, like a search page or an entity picker with no matches. Both can only be set [globally](./plugins#plugin-scope), on the provider.
+
+## Further reading
+
+- [Modular embedding SDK plugins](./plugins)
+- [Appearance](../appearance)
+- [Modular embedding SDK config](./config)
+- [Modular embedding components](../components)
diff --git a/_docs/latest/embedding/sdk/plugins.md b/_docs/latest/embedding/sdk/plugins.md
index a1f052f2d5..bc52f85816 100644
--- a/_docs/latest/embedding/sdk/plugins.md
+++ b/_docs/latest/embedding/sdk/plugins.md
@@ -68,15 +68,12 @@ To create clickable links in your table columns, set the column's formatting to
### `getNoDataIllustration` and `getNoObjectIllustration`
-By default, Metabase displays a sailboat image when a query returns no results. To use a different image, you can use `getNoDataIllustration` and `getNoObjectIllustration` plugins which can accept a custom base64-encoded image:
+By default, Metabase displays a sailboat image when a query returns no results. The plugins `getNoDataIllustration` and `getNoObjectIllustration` swap in an image of your own. Both can only be used [globally](#plugin-scope) on provider level.
-```typescript
-{% include_file "{{ dirname }}/snippets/plugins/custom-images.tsx" snippet="example" %}
-```
-
-The plugins `getNoDataIllustration` and `getNoObjectIllustration` can only be used [globally](#plugin-scope) on provider level.
+See [Replace the no-results illustration](./loading-and-errors#replace-the-no-results-illustration) for more information and examples.
## Further reading
- [Question plugins](../chart#customize-what-happens-when-someone-clicks-on-a-chart)
- [Dashboard plugins](../dashboard#customize-the-menu-on-dashboard-cards-react-sdk-only)
+- [Customize loading, error, and empty states](./loading-and-errors)
diff --git a/_docs/latest/embedding/start.md b/_docs/latest/embedding/start.md
index 675e228cc8..f35d281c41 100644
--- a/_docs/latest/embedding/start.md
+++ b/_docs/latest/embedding/start.md
@@ -34,6 +34,10 @@ With the Modular embedding SDK, you can embed individual Metabase components wit
Jump to a SDK quickstart with a sample React application.
+### [Modular embedding components](./components)
+
+A map of the components you can embed, with links to each component's docs and its attribute and prop reference.
+
### [Embed a dashboard](./dashboard)
Embed a dashboard, view-only or interactive, with web components or the React SDK. Let people edit and create dashboards from your app.
@@ -62,10 +66,18 @@ Embed a browsable collection with web components or the React SDK, so people can
Every `` attribute and the `CollectionBrowser` props.
+### [Embed an AI chat](./ai-chat)
+
+Embed an AI chat with web components or the React SDK, so people can ask questions of their data in natural language.
+
### [Guest embedding](./guest-embedding)
Guest embedding is a secure way to embed charts and dashboards. Guest embeds are view-only; people won't be able to drill-through charts and tables.
+### [Customize loading, error, and empty states](./sdk/loading-and-errors)
+
+Replace the React SDK's default loading screen, error screen, and no-results image with your own.
+
### [Translating embeds](./translations)
Upload a translation dictionary to translate questions and dashboards in modular embeds.
diff --git a/_docs/latest/embedding/translations.md b/_docs/latest/embedding/translations.md
index b7acdb8a42..38eb173fcf 100644
--- a/_docs/latest/embedding/translations.md
+++ b/_docs/latest/embedding/translations.md
@@ -120,7 +120,7 @@ If the strings you want to translate include markdown formatting, you'll need to
## The AI chat component isn't translated
-One exception is that Metabase won't translate the text in the [AI chat component](./sdk/ai-chat). While Metabot can understand other languages, it works best in English.
+One exception is that Metabase won't translate the text in the [AI chat component](./ai-chat). While Metabot can understand other languages, it works best in English.
## Further reading
diff --git a/_docs/v0.63/ai/overview.md b/_docs/v0.63/ai/overview.md
index 191caea3f2..f5ba7249bf 100644
--- a/_docs/v0.63/ai/overview.md
+++ b/_docs/v0.63/ai/overview.md
@@ -26,7 +26,7 @@ Here are the different ways to use AI with Metabase:
**Best for: daily tasks in Metabase; granular control over people's AI usage.**
-Metabot is Metabase's built-in AI agent. Metabot can help you with most daily tasks around Metabase, like answering questions about your data, creating queries, generating SQL code, explaining charts, or creating Documents. If you're embedding Metabase into your product, you can get the Metabot agent through the [AI chat component](../embedding/components#ai-chat). See [non-exhaustive list of things Metabot can do](metabot#what-metabot-can-do), as well as its [limitations](metabot#current-limitations).
+Metabot is Metabase's built-in AI agent. Metabot can help you with most daily tasks around Metabase, like answering questions about your data, creating queries, generating SQL code, explaining charts, or creating Documents. If you're embedding Metabase into your product, you can get the Metabot agent through the [AI chat component](../embedding/ai-chat). See a [non-exhaustive list of things Metabot can do](metabot#what-metabot-can-do), as well as its [limitations](metabot#current-limitations).
**Controls:** Metabot will only see what the person using it can see. Metabot also comes with additional permission controls and usage limits so that you control who can use which Metabot tools (e.g. chat vs SQL generation) and how many tokens they can spend.
@@ -37,7 +37,7 @@ Metabot is Metabase's built-in AI agent. Metabot can help you with most daily ta
**Plans**: available on all plans. You can only use Metabase's AI Service on Metabase Cloud.
-See [full docs for Metabot](metabot) and [embedded AI chat](../embedding/components#ai-chat).
+See [full docs for Metabot](metabot) and [embedded AI chat](../embedding/ai-chat).
## MCP server
diff --git a/_docs/v0.63/ai/settings.md b/_docs/v0.63/ai/settings.md
index ca14bed35c..5f7e4fb475 100644
--- a/_docs/v0.63/ai/settings.md
+++ b/_docs/v0.63/ai/settings.md
@@ -107,10 +107,10 @@ Toggling off Metabot only turns off in-app Metabot features. People can still us
_Embedded tab._
-The **Embedded Metabot is enabled** toggle turns embedded Metabot on or off. The toggle affects both full-app embeds and modular embeds.
+The **Enable Embedded Metabot** toggle turns embedded Metabot on or off. The toggle affects both full-app embeds and modular embeds.
- [Full-app embedding](../embedding/full-app-embedding): The Metabot icon and keyboard shortcuts are only available when Metabot is enabled. Turning off Embedded Metabot will hide these icons and disable the keyboard shortcuts.
-- [Modular embedding](../embedding/modular-embedding): The toggle doesn't add Metabot anywhere; you have to explicitly include a chat component (like the SDK's [`MetabotQuestion`](../embedding/sdk/ai-chat)) in your application. If, however, you've added a component, and you turn off the Embedded Metabot toggle, your chat component will stop working, so you should also remove or hide the component in your application.
+- [Modular embedding](../embedding/modular-embedding): The toggle doesn't add Metabot anywhere; you have to explicitly include a chat component (like the SDK's [`MetabotQuestion`](../embedding/ai-chat)) in your application. If, however, you've added a component, and you turn off the Embedded Metabot toggle, your chat component will stop working, so you should also remove or hide the component in your application.
### Verified content
@@ -134,7 +134,11 @@ People can still @-mention items outside of this collection when prompting in AI
_Embedded tab._
-If you're embedding the Metabot component in an app, you can specify a different collection that embedded Metabot is allowed to use for creating queries. Click **Pick a different collection** to choose the collection (and its subcollections) that embedded Metabot can query.
+If you're embedding the Metabot component in an app, you can point embedded Metabot at a different collection to use for creating queries. Click **Pick a different collection** to choose the collection (and its subcollections) that embedded Metabot searches for metrics, models, and saved questions.
+
+Picking **Our analytics** is the same as picking no collection at all, so pick something narrower if you want the scoping to do anything. And once you set a collection, tables drop out of embedded Metabot's search results, so pick a collection with the metrics and models you want people building on.
+
+This setting narrows where embedded Metabot searches; it's _not_ a substitute for setting permissions. Embedded Metabot can still read and query anything the person using it has permissions for. Embedded Metabot can also see the items that person viewed recently, whichever collection those live in. Restricting Metabot to [verified content](#verified-content) narrows those recent items to verified, official, and [Library](../data-studio/library) content, but it doesn't confine them to the collection you picked. To control what data people can get to in an embed, set [data permissions](../permissions/embedding). See also [Set up AI chat in Metabase](../embedding/ai-chat#set-up-ai-chat-in-metabase).
### Prompt suggestions
diff --git a/_docs/v0.63/api.json b/_docs/v0.63/api.json
index ecf263d8ef..e66d4f738a 100644
--- a/_docs/v0.63/api.json
+++ b/_docs/v0.63/api.json
@@ -12175,6 +12175,215 @@
"required" : [ "payload_type" ]
} ]
},
+ "metabase.notification.api.notification.NotificationApiUpdateInput" : {
+ "description" : "::NotificationApiInput restricted to what `notification-update-spec` writes. On PUT the URL,\n not the body, identifies the target (RFC 9110 §9.3.4), so a client-sent id is stripped.",
+ "oneOf" : [ {
+ "type" : "object",
+ "properties" : {
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "creator_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "handlers" : {
+ "type" : "array",
+ "items" : {
+ "type" : "object",
+ "properties" : {
+ "template_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "recipients" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationRecipient"
+ }
+ },
+ "template" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.ChannelTemplateUserProvided"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_type" : { },
+ "channel" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.Channel"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "notification_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "required" : [ "channel_type" ]
+ }
+ },
+ "payload" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationCardUpdate"
+ },
+ "payload_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "payload_type" : {
+ "type" : "string",
+ "enum" : [ "notification/dashboard", "notification/system-event", "notification/testing", "notification/card" ]
+ },
+ "subscriptions" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationSubscription"
+ }
+ }
+ },
+ "required" : [ "payload_type", "payload" ]
+ }, {
+ "type" : "object",
+ "properties" : {
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "creator_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "handlers" : {
+ "type" : "array",
+ "items" : {
+ "type" : "object",
+ "properties" : {
+ "template_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_id" : {
+ "oneOf" : [ {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "recipients" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationRecipient"
+ }
+ },
+ "template" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.ChannelTemplateUserProvided"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "channel_type" : { },
+ "channel" : {
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/metabase.channel.models.channel.Channel"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "notification_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "active" : {
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "required" : [ "channel_type" ]
+ }
+ },
+ "payload_id" : {
+ "oneOf" : [ {
+ "type" : "integer"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "payload_type" : {
+ "type" : "string",
+ "enum" : [ "notification/dashboard", "notification/system-event", "notification/testing", "notification/card" ]
+ },
+ "subscriptions" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/metabase.notification.models.NotificationSubscription"
+ }
+ }
+ },
+ "required" : [ "payload_type" ]
+ } ]
+ },
"metabase.notification.models.CreateNotificationCardParams" : {
"description" : "[[::NotificationCard]] on a create request, where the caller does not get to pick the id.",
"type" : "object",
@@ -12695,6 +12904,25 @@
},
"required" : [ "card_id" ]
},
+ "metabase.notification.models.NotificationCardUpdate" : {
+ "description" : "::NotificationCard restricted to what the update spec writes - `:id` comes from the URL's notification.",
+ "type" : "object",
+ "properties" : {
+ "card_id" : {
+ "description" : "value must be an integer greater than zero.",
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "send_condition" : {
+ "type" : "string",
+ "enum" : [ "goal_below", "has_result", "goal_above" ]
+ },
+ "send_once" : {
+ "type" : "boolean"
+ }
+ },
+ "required" : [ "card_id" ]
+ },
"metabase.notification.models.NotificationHandler" : {
"type" : "object",
"properties" : {
@@ -15142,6 +15370,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/agent" ]
}
},
@@ -19944,6 +20182,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/comment" ]
},
"post" : {
@@ -20022,6 +20270,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/comment" ]
}
},
@@ -21407,6 +21665,18 @@
"pattern" : "^[A-Za-z0-9_\\-]{21}$"
} ]
}
+ }, {
+ "in" : "query",
+ "name" : "dashboard_load_id",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string",
+ "minLength" : 1
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -22156,6 +22426,18 @@
"pattern" : "^[A-Za-z0-9_\\-]{21}$"
} ]
}
+ }, {
+ "in" : "query",
+ "name" : "dashboard_load_id",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string",
+ "minLength" : 1
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -23237,6 +23519,18 @@
"minimum" : 1
},
"description" : "value must be an integer greater than zero."
+ }, {
+ "in" : "query",
+ "name" : "include_editable_data_model",
+ "required" : false,
+ "schema" : {
+ "default" : false,
+ "oneOf" : [ {
+ "type" : "boolean"
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -23788,7 +24082,14 @@
"operationId" : "get-api-database-virtual-db-datasets-schema",
"summary" : "GET /api/database/{virtual-db}/datasets/{schema}",
"description" : "Returns a list of Tables for the datasets virtual database.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "schema",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -23828,7 +24129,14 @@
"operationId" : "get-api-database-virtual-db-schema-schema",
"summary" : "GET /api/database/{virtual-db}/schema/{schema}",
"description" : "Returns a list of Tables for the saved questions virtual database.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "schema",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -26206,6 +26514,17 @@
"type" : "string",
"minLength" : 1
}
+ }, {
+ "in" : "query",
+ "name" : "locale",
+ "required" : false,
+ "schema" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ }
} ],
"responses" : {
"2XX" : {
@@ -26899,6 +27218,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/custom-viz-plugin" ]
}
},
@@ -27062,6 +27391,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/custom-viz-plugin" ]
}
},
@@ -27346,6 +27685,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/data-complexity-score" ]
}
},
@@ -28526,6 +28875,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/library" ]
},
"get" : {
@@ -28561,6 +28920,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/library" ]
}
},
@@ -29617,6 +29986,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/permission_debug" ]
}
},
@@ -30252,6 +30631,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/remote-sync" ]
}
},
@@ -31085,7 +31474,15 @@
"operationId" : "put-api-ee-scim-v2-Groups-id",
"summary" : "PUT /api/ee/scim/v2/Groups/{id}",
"description" : "Update a group.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "id",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -31368,7 +31765,15 @@
"operationId" : "put-api-ee-scim-v2-Users-id",
"summary" : "PUT /api/ee/scim/v2/Users/{id}",
"description" : "Update a user.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "path",
+ "name" : "id",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response"
@@ -32234,6 +32639,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/serialization" ]
}
},
@@ -32684,6 +33099,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/ee/tenant" ]
}
},
@@ -35228,6 +35653,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/geojson" ]
}
},
@@ -35256,6 +35691,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/geojson" ]
}
},
@@ -35450,7 +35895,16 @@
"operationId" : "get-api-health-inspector",
"summary" : "GET /api/health-inspector",
"description" : "Get a list of recent health check runs.",
- "parameters" : [ ],
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "limit",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "minimum" : 1
+ },
+ "description" : "value must be an integer greater than zero."
+ } ],
"responses" : {
"2XX" : {
"description" : "Successful response",
@@ -39384,7 +39838,7 @@
"content" : {
"application/json" : {
"schema" : {
- "$ref" : "#/components/schemas/metabase.notification.api.notification.NotificationApiInput"
+ "$ref" : "#/components/schemas/metabase.notification.api.notification.NotificationApiUpdateInput"
}
}
}
@@ -41155,6 +41609,13 @@
"type" : "string",
"minLength" : 1
}
+ }, {
+ "in" : "query",
+ "name" : "value",
+ "required" : true,
+ "schema" : {
+ "type" : "string"
+ }
} ],
"responses" : {
"2XX" : {
@@ -44547,6 +45008,16 @@
"description" : "Server error response"
}
},
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : { }
+ }
+ }
+ }
+ },
"tags" : [ "/api/session" ]
}
},
@@ -45124,7 +45595,7 @@
"post" : {
"operationId" : "post-api-slack-bug-report",
"summary" : "POST /api/slack/bug-report",
- "description" : "Send diagnostic information to the configured Slack channels.",
+ "description" : "Send diagnostic information to the configured Slack channels. Requires bug reporting to be enabled. The report is\n attributed to the current user when `diagnosticInfo.reporter` is true, and anonymous otherwise. The `{name, email}`\n form of `reporter` that clients before 0.64 send is treated as true; the identity in it is ignored.",
"parameters" : [ ],
"responses" : {
"2XX" : {
@@ -45144,10 +45615,145 @@
"type" : "object",
"properties" : {
"diagnosticInfo" : {
- "type" : "object"
+ "type" : "object",
+ "properties" : {
+ "entityName" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "frontendErrors" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "type" : "string"
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "entityInfo" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "reporter" : {
+ "oneOf" : [ {
+ "anyOf" : [ {
+ "type" : "object",
+ "properties" : {
+ "email" : {
+ "type" : "string"
+ },
+ "name" : {
+ "type" : "string"
+ }
+ },
+ "required" : [ "name", "email" ],
+ "additionalProperties" : false
+ }, {
+ "type" : "boolean"
+ } ]
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "url" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "backendErrors" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "bugReportDetails" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "userLogs" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "localizedEntityName" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "logs" : {
+ "oneOf" : [ {
+ "type" : "array",
+ "items" : {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "queryResults" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "browserInfo" : {
+ "oneOf" : [ {
+ "description" : "Value must be a map.",
+ "type" : "object",
+ "properties" : { }
+ }, {
+ "type" : "null"
+ } ]
+ },
+ "description" : {
+ "oneOf" : [ {
+ "type" : "string"
+ }, {
+ "type" : "null"
+ } ]
+ }
+ },
+ "additionalProperties" : false
}
},
- "required" : [ "diagnosticInfo" ]
+ "required" : [ "diagnosticInfo" ],
+ "additionalProperties" : false
}
}
}
diff --git a/_docs/v0.63/dashboards/subscriptions.md b/_docs/v0.63/dashboards/subscriptions.md
index a81ca19be7..c1ff399231 100644
--- a/_docs/v0.63/dashboards/subscriptions.md
+++ b/_docs/v0.63/dashboards/subscriptions.md
@@ -163,7 +163,7 @@ See [Remove Metabase branding from exports](../questions/exporting-results#remov
## Subscriptions in embedded dashboards exclude links
-Since people viewing embedded dashboards likely don't have direct access to your Metabase, Metabase will omit the links to Metabase items in subscriptions sent from embedded dashboards. That way people don't get broken links in their email. See [embedded dashboards](../embedding/components).
+Since people viewing embedded dashboards likely don't have direct access to your Metabase, Metabase will omit the links to Metabase items in subscriptions sent from embedded dashboards. That way people don't get broken links in their email. See [Embed a dashboard](../embedding/dashboard).
## Further reading
diff --git a/_docs/v0.63/embedding/ai-chat.md b/_docs/v0.63/embedding/ai-chat.md
new file mode 100644
index 0000000000..7aefbd6a91
--- /dev/null
+++ b/_docs/v0.63/embedding/ai-chat.md
@@ -0,0 +1,211 @@
+---
+version: v0.63
+has_magic_breadcrumbs: true
+show_category_breadcrumb: true
+show_title_breadcrumb: true
+category: Embedding
+title: 'Embed an AI chat'
+source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/ai-chat.md'
+layout: new-docs
+summary: 'Embed an AI chat in your app with a web component or the React SDK, so people can ask questions of their data in natural language.'
+redirect_from:
+ - /docs/v0.63/embedding/sdk/ai-chat
+---
+
+# Embed an AI chat
+
+
+
+{% include plans-blockquote.html feature="AI chat component" convert_pro_link_to_embedding=true%}
+
+You can embed an AI chat in your app, so people can ask questions of their data in natural language. Embedded chat is a focused version of [Metabot](../ai/metabot): it builds a question in the query builder and charts the answer.
+
+To build that question, embedded chat first searches your Metabase for the best thing to build on: a [metric](../data-modeling/metrics), a [model](../data-modeling/models), a saved question, or a table (tables drop out once you [scope the chat to a collection](#set-up-ai-chat-in-metabase)). Then it writes a query against whatever it picked. So embedded chat does look through your saved content, but as raw material for a new question, not as results to hand back. What people get back is always a new question they can drill into, and save if you [turn saving on](#let-people-save-questions-metabot-creates).
+
+Embedded chat won't write SQL, build or edit dashboards, or create metrics and models — it builds on the ones you already have. It also won't work as a search box for finding existing content. For those, chat will suggest doing the work in Metabase itself.
+
+AI chat requires the embed to use SSO authentication that signs people into your Metabase.
+
+## Try the AI chat demo
+
+For what AI chat looks like in action, check out the [AI chat component](https://embedded-analytics-sdk-demo.metabase.com/admin/analytics/new/ask-metabot) running on Shoppy, our modular embedding demo app. The demo's chat uses the [dedicated chart component](#ai-chat-with-dedicated-chart-panel).
+
+## Set up AI chat in Metabase
+
+An admin sets up embedded Metabot in your Metabase:
+
+1. Click the **grid** icon in the upper right.
+2. Select **Admin**.
+3. Click the **AI** tab.
+4. In the left sidebar, click **AI Settings**.
+5. The first card on the page is your AI provider connection. If the card says **Connect to an AI provider**, [set one up](../ai/settings#enable-ai-features). If you're self-hosting, that means [bringing your own API key](../ai/settings#bring-your-own-api-key). If the card says **AI providers**, you're already connected.
+6. In the **Metabot settings** card, click the **Embedded** tab.
+7. Turn on **Enable Embedded Metabot**.
+8. Under **Collection Embedded Metabot can use**, click **Pick a different collection** and choose the collection that holds the metrics, models, and saved questions embedded Metabot should build on.
+
+The collection you pick narrows what embedded chat finds when it searches for something to build on: that collection and everything under it. It doesn't limit what embedded chat can query, since people can still get to any data they have [permissions](../permissions/embedding) for. And once you set a collection, tables drop out of the chat's search results, so pick a collection with the metrics and models you want people building on.
+
+The **Embedded** tab configures Metabot in an embedded context, which is separate from the [Metabot](../ai/settings) in your own Metabase (which lives on the **Internal** tab). Both tabs control what each Metabot can see, not what it runs on: the AI provider, API key, and model are set once for the whole instance, above the **Metabot settings** card, and both Metabots use them.
+
+With embedded Metabot set up, there are two ways to add the chat to your app:
+
+- **[Web component](#web-component-ai-chat)**: the whole chat interface, chart and all, from a single tag.
+- **[React SDK](#react-sdk-ai-chat)**: the same interface from the `MetabotQuestion` component, or the [`useMetabot`](#build-a-custom-ai-chat-ui-with-usemetabot-react-sdk-only) hook if you'd rather build the interface yourself.
+
+Both the web component and `MetabotQuestion` let you [set where the chart appears](#set-where-the-chart-appears) and [whether people can save questions](#let-people-save-questions-metabot-creates).
+
+## Web component AI chat
+
+You can use the in-app wizard to generate the code:
+
+1. Open the command palette with Ctrl/Cmd+K and type **New embed**.
+2. For the experience, select **Metabot**.
+3. Pick a [layout](#set-where-the-chart-appears) and decide whether people can [save questions](#let-people-save-questions-metabot-creates).
+4. Click **Get code** and paste the snippet into your app.
+
+The **Metabot** option only shows up once an admin has turned on embedded Metabot, and only for SSO authentication. For what the rest of the generated snippet does, see [modular embedding](./modular-embedding).
+
+To render the AI chat interface:
+
+```html
+
+```
+
+### Web component `metabase-metabot` attributes
+
+{% include_file "{{ dirname }}/eajs/snippets/MetabaseMetabotAttributes.md" snippet="properties" %}
+
+Depending on the framework you're using, you may need to stringify attributes before passing them to the component. And if you surround an attribute's value with double quotes, use single quotes inside it.
+
+For all modular embeds, you can also set a `locale` in your page-level configuration to [translate embedded content](./translations). But [Metabot's own text isn't translated](./translations#the-ai-chat-component-isnt-translated).
+
+## React SDK AI chat
+
+{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
+
+To embed an AI chat with the [SDK](./sdk/introduction), use the `MetabotQuestion` component. Wrap the component in the `MetabaseProvider` component with your auth config.
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/ai-question.tsx" %}
+```
+
+### React SDK `MetabotQuestion` props
+
+- [Component](./sdk/api/MetabotQuestion)
+- [Props](./sdk/api/MetabotQuestionProps)
+
+{% include_file "{{ dirname }}/sdk/api/snippets/MetabotQuestionProps.md" snippet="properties" %}
+
+## Set where the chart appears
+
+- [Web component](#web-component-chart-layout)
+- [React SDK](#react-sdk-chart-layout)
+
+The `layout` setting positions the chart relative to the chat interface:
+
+- `auto` (default): Metabot uses the `stacked` layout on mobile screens, and a `sidebar` layout on larger screens.
+- `stacked`: the chart stacks on top of the chat interface.
+- `sidebar`: the chart appears to the left of the chat interface, which sits in a sidebar on the right.
+
+`layout` only applies to the built-in chat component. If you're building your own interface with [`useMetabot`](#build-a-custom-ai-chat-ui-with-usemetabot-react-sdk-only), you position the chart yourself.
+
+### Web component chart layout
+
+Set the `layout` attribute:
+
+```html
+
+```
+
+### React SDK chart layout
+
+Set the `layout` prop on `MetabotQuestion`:
+
+```tsx
+
+```
+
+## Let people save questions Metabot creates
+
+- [Web component](#web-component-question-saving)
+- [React SDK](#react-sdk-question-saving)
+
+Turning on the chat's save button lets people keep a question Metabot built. Saving is off by default.
+
+Setting a target collection is optional, but it's worth doing: it picks the collection that new questions land in, so people's work doesn't scatter across your Metabase. It also hides the collection picker in the save modal, so nobody has to decide where their question goes.
+
+### Web component question saving
+
+Turn saving on with `is-save-enabled="true"`, and set the collection with `target-collection`:
+
+```html
+
+```
+
+### React SDK question saving
+
+The equivalent props on `MetabotQuestion` are `isSaveEnabled` and `targetCollection`:
+
+```tsx
+
+```
+
+## Build a custom AI chat UI with `useMetabot` (React SDK only)
+
+If `MetabotQuestion`'s built-in layouts don't fit your app, use the `useMetabot` hook to read Metabot's conversation state directly and render your own UI. The hook gives you the messages, the chart the agent most recently produced, processing and error state, and actions to submit, cancel, retry, or reset the conversation.
+
+### AI chat with inline charts
+
+
+
+When an agent responds, the message can contain a `Chart` component. You can walk the agent's messages and render charts inline alongside the chat transcript:
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/use-metabot-inline-charts.tsx" %}
+```
+
+### AI chat with dedicated chart panel
+
+
+
+The `CurrentChart` component is bound to the latest chart the agent produced. Render `CurrentChart` once, and it will swap in new charts as the agent creates them. You'll want to filter chart messages out of the transcript so they don't render twice:
+
+```typescript
+{% include_file "{{ dirname }}/sdk/snippets/questions/use-metabot-dedicated-chart.tsx" %}
+```
+
+### React SDK `useMetabot` return values
+
+- [Hook](./sdk/api/useMetabot)
+- [Return values](./sdk/api/UseMetabotResult)
+
+{% include_file "{{ dirname }}/sdk/api/snippets/UseMetabotResult.md" snippet="properties" %}
+
+### Guard against null while the SDK bundle loads
+
+`useMetabot` returns `null` until the SDK bundle has loaded and `` has mounted, so always guard before you use it. The SDK ships its Metabot internals in a code-split chunk that isn't available synchronously, which means an unguarded first render throws `Cannot read properties of null` as soon as you reach for `metabot.messages`, `metabot.submitMessage`, or anything else on the hook.
+
+### Bring your own markdown renderer
+
+`MetabotQuestion` renders agent text messages for you, markdown formatting and all, along with transcript scrolling and input styling. The `useMetabot` hook hands you the raw conversation state instead, so you can handle the markdown rendering.
+
+Agent text messages (the ones where `message.type === 'text'`) contain markdown (like links, bold, lists, inline code). The snippets above render `message.message` as plain text to keep them short, but in production you'll want to pass that text through a markdown renderer, like `react-markdown` or `markdown-to-jsx`, so links and formatting come out right.
+
+### Strip links back to Metabase
+
+Agent text can include links pointing back to the Metabase it's running against, like a link to a chart the agent just created. Opening one requires an authenticated Metabase session, so people viewing your app will hit a login screen. Strip those links out when you render the message, or swap them for a route in your own app.
+
+## Further reading
+
+- [Modular embedding components](./components)
+- [Metabot](../ai/metabot)
+- [Metabot settings](../ai/settings)
+- [Embed a chart](./chart)
+- [Embed a dashboard](./dashboard)
+- [Appearance](./appearance)
+- [Authentication](./authentication)
+- [Modular embedding](./modular-embedding)
+- [Modular embedding SDK](./sdk/introduction)
diff --git a/_docs/v0.63/embedding/browser.md b/_docs/v0.63/embedding/browser.md
index 7c742cdd81..472115d40d 100644
--- a/_docs/v0.63/embedding/browser.md
+++ b/_docs/v0.63/embedding/browser.md
@@ -149,3 +149,4 @@ One quirk: when someone clicks on a collection, `CollectionBrowser` navigates in
- [Collection permissions](../permissions/collections)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
+- [Modular embedding components](./components)
diff --git a/_docs/v0.63/embedding/chart.md b/_docs/v0.63/embedding/chart.md
index 1f98c8cb6f..3fc7fda5b0 100644
--- a/_docs/v0.63/embedding/chart.md
+++ b/_docs/v0.63/embedding/chart.md
@@ -284,9 +284,9 @@ For both the SDK props (`initialSqlParameters`, `sqlParameters`, and `onSqlParam
To hide a parameter from the question's UI, use the [`hidden-parameters`](./question-reference#web-component-metabase-question-attributes) attribute (web component) or the `hiddenParameters` prop (SDK). Both work in any modular embed, on any plan.
-In practice you'll reach for them on [SSO embeds](./introduction#components-with-sso-authentication), where every parameter on the question shows up by default. On a [guest embed](./guest-embedding), a parameter that you haven't set to **Editable** or **Locked** is already hidden.
+In practice you'll reach for them on an [SSO embed](./introduction#components-with-sso-authentication), where every parameter on the question shows up by default. On a [guest embed](./guest-embedding#configuring-parameters), a parameter that you haven't set to **Editable** or **Locked** is already hidden, so the embed wizard won't generate `hidden-parameters` for you. You can still add the attribute by hand to hide a parameter you've made editable.
-Hiding a parameter declutters the UI; it doesn't restrict what people can query. Setting a value with `initial-sql-parameters` and then hiding the widget isn't a secure way to filter data, because your app sets that value in the browser. To restrict the data itself, see [Show people only their own data](#show-people-only-their-own-data).
+Hiding a parameter declutters the UI; it doesn't restrict what people can query. Setting a value with `initial-sql-parameters` and then hiding the widget isn't a secure way to filter data, because your app sets that value in the browser. Instead, see [Show people only their own data](#show-people-only-their-own-data), or set [data permissions](../permissions/embedding).
## Let people set up alerts on a question
@@ -353,4 +353,5 @@ On the OSS and Starter plans, Metabase adds a "Powered by Metabase" banner to gu
- [Guest embeds](./guest-embedding)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
-- [AI chat](./sdk/ai-chat)
+- [Modular embedding components](./components)
+- [Embed an AI chat](./ai-chat)
diff --git a/_docs/v0.63/embedding/components.md b/_docs/v0.63/embedding/components.md
index 066c3d787f..f91dba6012 100644
--- a/_docs/v0.63/embedding/components.md
+++ b/_docs/v0.63/embedding/components.md
@@ -7,34 +7,36 @@ category: Embedding
title: 'Modular embedding components'
source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/components.md'
layout: new-docs
-summary: 'Embed dashboards, questions, query builder, AI chat, and a collection browser.'
+summary: 'A map of the modular embedding docs: dashboards, charts, the query builder, a collection browser, and AI chat.'
---
# Modular embedding components
-There are different components you can embed, each with various options.
+Modular embedding gives you a handful of components you can drop into your app, with either web components or the React SDK. This page maps out the docs for each one.
-> While you can use component parameters to show or hide parts of the embedded component, these parameters are _not_ a substitute for [permissions](../permissions/start). Even if you hide stuff, people could still grab their token from the frontend and use it to query the Metabase API.
-
-This page covers what you can embed. For theming your embeds, see [Appearance](./appearance).
-
-> Depending on the framework you're using, you may need to stringify attributes before passing them to the embedded components.
+> While you can use attributes and props to show or hide parts of an embedded component, they're _not_ a substitute for [permissions](../permissions/start). Even if you hide stuff, people could still grab their token from the frontend and use it to query the Metabase API.
## Dashboard
+Embed a dashboard view-only, interactive, or editable, and let people create dashboards from your app.
+
- [Embed a dashboard](./dashboard)
- [Dashboard component reference](./dashboard-reference), for `` attributes and SDK props
## Question
+Embed a single chart, or the query builder and SQL editor so people can build questions from scratch.
+
- [Embed a chart](./chart)
-- [Embed a query editor](./query-builder)
+- [Embed the query builder](./query-builder)
- [Question component reference](./question-reference), for `` attributes and SDK props
## Browser
{% include plans-blockquote.html feature="Browser component" convert_pro_link_to_embedding=true%}
+Embed a browsable collection, so people can find and open dashboards and questions themselves.
+
- [Embed a collection browser](./browser)
- [Browser component reference](./browser-reference), for `` attributes and SDK props
@@ -42,33 +44,24 @@ This page covers what you can embed. For theming your embeds, see [Appearance](.
{% include plans-blockquote.html feature="AI chat component" convert_pro_link_to_embedding=true%}
-AI chat component is only available for authenticated modular embeds. It's unavailable for [Guest embeds](./guest-embedding).
-
-To render the AI chat interface:
-
-```html
-
-```
-
-If you're using the SDK, you can use either the [`MetabotQuestion`](./sdk/ai-chat#example) component or the [`useMetabot`](./sdk/ai-chat#building-custom-ai-chat-uis-with-usemetabot) hook for a custom UI.
-
-### Attributes
-
-{% include_file "{{ dirname }}/eajs/snippets/MetabaseMetabotAttributes.md" snippet="properties" %}
-
-## Customizing loader and error components
+Embed an AI chat, so people can ask questions of their data in natural language.
-{% include plans-blockquote.html feature="Customizing loader and error components" convert_pro_link_to_embedding=true%}
+- [Embed an AI chat](./ai-chat), for the `` attributes, the `MetabotQuestion` props, and the `useMetabot` hook
-If you're using the [modular embedding SDK](./sdk/introduction), you can provide your own components for loading and error states by specifying `loaderComponent` and `errorComponent` as props to `MetabaseProvider`.
+## Things you can only do with the React SDK
-```tsx
-{% include_file "{{ dirname }}/sdk/snippets/appearance/customizing-loader-and-components.tsx" snippet="imports" %}
+Web components cover the components above. A few features are React-only, because they take React components or hooks that an HTML attribute can't carry. For those, use the [Modular embedding SDK](./sdk/introduction).
-{% include_file "{{ dirname }}/sdk/snippets/appearance/customizing-loader-and-components.tsx" snippet="example" %}
-```
+- [Plugins](./sdk/plugins), to customize component menus and click actions
+- [Actions](./sdk/actions), to run Metabase actions from your app with the `useAction` hook
+- [Custom question layouts](./question-reference#customize-the-layout-of-an-interactive-chart), to lay out a question yourself with namespaced `InteractiveQuestion` components
+- [Customize loading, error, and empty states](./sdk/loading-and-errors), to swap in your own loading and error components, and your own no-results image
## Further reading
- [Appearance](./appearance)
-- [Modular embedding SDK](./sdk/introduction).
+- [Modular embedding SDK config](./sdk/config), for the `MetabaseProvider` props
+- [Modular embedding parameters](./parameters)
+- [Translating embeds](./translations)
+- [Authentication](./authentication)
+- [Modular embedding SDK](./sdk/introduction)
diff --git a/_docs/v0.63/embedding/dashboard.md b/_docs/v0.63/embedding/dashboard.md
index ad63e41e9b..fb33a159ac 100644
--- a/_docs/v0.63/embedding/dashboard.md
+++ b/_docs/v0.63/embedding/dashboard.md
@@ -560,4 +560,5 @@ On the OSS and Starter plans, Metabase adds a "Powered by Metabase" banner to gu
- [Guest embeds](./guest-embedding)
- [Authentication](./authentication)
- [Modular embedding SDK](./sdk/introduction)
-- [AI chat](./sdk/ai-chat)
+- [Modular embedding components](./components)
+- [Embed an AI chat](./ai-chat)
diff --git a/_docs/v0.63/embedding/guest-embedding.md b/_docs/v0.63/embedding/guest-embedding.md
index 24714e3324..4abbb74178 100644
--- a/_docs/v0.63/embedding/guest-embedding.md
+++ b/_docs/v0.63/embedding/guest-embedding.md
@@ -40,7 +40,7 @@ To create a guest embed:
3. Select **Embed**.
4. Under **Authentication**, select **Guest**.
5. Optional: [customize the appearance of the embed](./appearance)
-6. Optional: [Add parameters to the embed](./components).
+6. Optional: [set the visibility of each parameter](#configuring-parameters).
7. Click **Publish**.
8. Get the code snippet that the wizard generates and add it to your app.
@@ -48,7 +48,7 @@ To create a guest embed:
## Notes on the code the wizard generates
-You can edit the code (see [components](./components) and [appearance](./appearance)). But here's an overview of the code the wizard generates, and where to put it.
+You can edit the code (see the [question component reference](./question-reference), the [dashboard component reference](./dashboard-reference), and [appearance](./appearance)). But here's an overview of the code the wizard generates, and where to put it.
### Client-side code
@@ -120,7 +120,7 @@ You can set different attributes to enable/disable UI. Here are some example att
\* Disabling downloads is only available on [Pro](/product/pro) and [Enterprise](/product/enterprise) plans.
-Attributes will differ based on the type of thing you're embedding. Guest embeds have fewer options than embeds that use SSO. See more on [components and their attributes](./components).
+Attributes will differ based on the type of thing you're embedding. Guest embeds have fewer options than embeds that use SSO. For every attribute, see the [dashboard component reference](./dashboard-reference) and the [question component reference](./question-reference).
### Customizing appearance of guest embeds
@@ -547,7 +547,8 @@ Guest embeds can't take advantage of:
- [Drill-through](../questions/visualizations/drill-through)
- [Usage analytics](../usage-and-performance-tools/usage-analytics)
- [Query builder](../questions/query-builder/editor)
-- [AI chat](./sdk/ai-chat)
+- [AI chat](./ai-chat)
+- [Custom visualizations](./sdk/config#custom-visualizations)
For those features, check out [Modular embedding with SSO](./modular-embedding).
diff --git a/_docs/v0.63/embedding/images/custom-error.png b/_docs/v0.63/embedding/images/custom-error.png
new file mode 100644
index 0000000000..4a916b6f0c
Binary files /dev/null and b/_docs/v0.63/embedding/images/custom-error.png differ
diff --git a/_docs/v0.63/embedding/introduction.md b/_docs/v0.63/embedding/introduction.md
index 928d581822..bd080499a8 100644
--- a/_docs/v0.63/embedding/introduction.md
+++ b/_docs/v0.63/embedding/introduction.md
@@ -55,7 +55,7 @@ All SSO options require a Pro or Enterprise plan.
| [Drill-through menus](../questions/visualizations/drill-through) | ✅ | ❌ |
| [Query builder](../questions/query-builder/editor) | ✅ | ❌ |
| [SQL editor](../questions/native-editor/writing-sql) | ✅ | ❌ |
-| [AI chat](./sdk/ai-chat) | ✅ | ❌ |
+| [AI chat](./ai-chat) | ✅ | ❌ |
| [Collection browser](./browser) | ✅ | ❌ |
| Advanced [Tenant](./tenants) and [permissions](../permissions/embedding) management | ✅ | ❌ |
| [Advanced theming](./appearance) | ✅ | ❌ |
diff --git a/_docs/v0.63/embedding/modular-embedding.md b/_docs/v0.63/embedding/modular-embedding.md
index 57aa72bee8..c5d5ab67f7 100644
--- a/_docs/v0.63/embedding/modular-embedding.md
+++ b/_docs/v0.63/embedding/modular-embedding.md
@@ -113,7 +113,7 @@ The code snippet has three parts:
- Loading the modular embedding library from your Metabase instance.
- Setting global configuration settings, like the URL of your Metabase and the `theme`. See [Page-level config](#page-level-config).
-- The component(s) to embed, with their parameters. See [Components](./components).
+- The component(s) to embed, with their attributes. To pick a component, see [Modular embedding components](./components). For the attributes each one takes, see the [dashboard](./dashboard-reference), [question](./question-reference), and [browser](./browser-reference) component references.
Here's an example snippet:
@@ -169,7 +169,7 @@ In addition to this, we consider shared accounts to be unfair usage. Fair usage
The exact customization options you see will depend on which type of entity you're embedding. These are the settings for authenticated embeds available on [Pro](/product/pro) and [Enterprise](/product/enterprise) plans. For guest embeds (OSS and paid), see [guest embedding options](./guest-embedding#component-attributes).
-When you're creating a new embed using **Admin > Embedding > Setup guide > Embed in your code**, you'll see some or all of the following customization options in the interactive creation flow. These options correspond to parameters in [components](./components).
+When you're creating a new embed using **Admin > Embedding > Setup guide > Embed in your code**, you'll see some or all of the following customization options in the interactive creation flow. These options correspond to attributes on the component you're embedding. For each attribute, see the [dashboard](./dashboard-reference) and [question](./question-reference) component references.
- **Allow people to drill through on data points**: determines whether people can interact with the chart (or charts on a dashboard). Interactivity includes [drilling down](../questions/visualizations/drill-through) to individual records from aggregated questions, filtering on click, zooming in, etc. Disabling drill-through for an embedded _question_ also disables people's ability to add filters and summaries.
diff --git a/_docs/v0.63/embedding/sdk/ai-chat.md b/_docs/v0.63/embedding/sdk/ai-chat.md
deleted file mode 100644
index 4e309eeff2..0000000000
--- a/_docs/v0.63/embedding/sdk/ai-chat.md
+++ /dev/null
@@ -1,95 +0,0 @@
----
-version: v0.63
-has_magic_breadcrumbs: true
-show_category_breadcrumb: true
-show_title_breadcrumb: true
-category: Embedding
-title: 'Modular embedding SDK - AI chat'
-source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/sdk/ai-chat.md'
-layout: new-docs
-summary: 'Embed an AI chat component in your app that can create queries from natural language questions.'
----
-
-# Modular embedding SDK - AI chat
-
-
-
-{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
-
-You can embed an AI chat in your application similar to [Metabot](../modular-embedding) in Metabase.
-
-Embedded Metabot is a more focused version of [Metabot](../../ai/metabot) designed to work well in an embedded context. Embedded Metabot can only display ad-hoc questions and metrics; it doesn't know about dashboards.
-
-To help embedded Metabot more easily find and focus on the data you care about most, select the collection containing the models and metrics it should be able to use to create queries.
-
-If you're embedding the Metabot component in an app, you can specify a different collection that embedded Metabot is allowed to use for creating queries.
-
-## Chat preview
-
-You can check out a [demo of the AI chat component](https://embedded-analytics-sdk-demo.metabase.com/admin/analytics/new/ask-metabot) on our Shoppy demo site.
-
-## Example
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/ai-question.tsx" %}
-```
-
-## `MetabotQuestion` props
-
-{% include_file "{{ dirname }}/api/snippets/MetabotQuestionProps.md" snippet="properties" %}
-
-## API reference
-
-- [Component](./api/MetabotQuestion)
-- [Props](./api/MetabotQuestionProps)
-
-## Setting up AI chat
-
-To configure your embedded AI chat in your Metabase:
-
-1. Click the **grid** icon in the upper right.
-2. Select **Admin**.
-3. Click the **AI** tab.
-4. In the left sidebar, click **Embedded Metabot**.
-
-When embedding the Metabot component in your app, you should specify a collection that embedded Metabot is allowed to use for creating queries. Embedded Metabot will only have access to that collection.
-
-For tips and more, see [Metabot settings](../../ai/settings).
-
-## Layout
-
-Use the `layout` prop to specify which layout to use for the Metabot component:
-
-- `auto` (default): Metabot uses the `stacked` layout on mobile screens, and a `sidebar` layout on larger screens.
-- `stacked`: the question visualization stacks on top of the chat interface.
-- `sidebar`: the question visualization appears to the left of the chat interface, which is on a sidebar on the right.
-
-## Building custom AI chat UIs with `useMetabot`
-
-If `MetabotQuestion`'s built-in layouts don't fit your app, use the `useMetabot` hook to read Metabot's conversation state directly and render your own UI. The hook gives you the messages, the chart the agent most recently produced, processing and error state, and actions to submit, cancel, retry, or reset the conversation.
-
-### AI chat with inline charts
-
-
-
-When an agent responds, the message can contain a `Chart` component. You can walk the agent's messages and render charts inline alongside the chat transcript:
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/use-metabot-inline-charts.tsx" %}
-```
-
-### AI chat with dedicated chart panel
-
-
-
-The `CurrentChart` component is bound to the latest chart the agent produced. Render `CurrentChart` once, and it will swap in new charts as the agent creates them. You'll want to filter chart messages out of the transcript so they don't render twice:
-
-```typescript
-{% include_file "{{ dirname }}/snippets/questions/use-metabot-dedicated-chart.tsx" %}
-```
-
-### Notes on `useMetabot`
-
-- **Guard against null while waiting for the SDK bundle**: `useMetabot` returns `null` until the SDK bundle has loaded and `` has mounted. Always guard before use. If you don't guard it, the first render will throw `Cannot read properties of null` when you reach for `metabot.messages`, `metabot.submitMessage`, etc., because the SDK ships its Metabot internals via a code-split chunk that isn't available synchronously.
-- **Bring your own Markdown renderer**: `MetabotQuestion` renders agent text messages internally, including markdown formatting, transcript scrolling, and input styling. The `useMetabot` hook hands you the raw conversation state, which means you own the rendering. In particular, agent text messages (`message.type === 'text'`) contain **markdown**: links, bold, lists, inline code. The snippets above render `message.message` as plain text for brevity, but production usage should pass the text through a markdown renderer (`react-markdown`, `markdown-to-jsx`, or your own) so links and formatting display correctly.
-- **Strip links returned by the agent**: the agent text may include links pointing back to the host Metabase (like a link to a chart it created). Those links require an authenticated Metabase session, so people won't be able to view the links.
diff --git a/_docs/v0.63/embedding/sdk/config.md b/_docs/v0.63/embedding/sdk/config.md
index 79ed776a30..6aacf282f9 100644
--- a/_docs/v0.63/embedding/sdk/config.md
+++ b/_docs/v0.63/embedding/sdk/config.md
@@ -54,6 +54,10 @@ Omitting the prop, or passing an empty array, turns off custom visualizations. C
For security, the SDK runs each custom visualization's code in an isolated sandbox, so a visualization can't reach your app or make network requests. The sandbox doesn't block passive image loads, though. A visualization can still trigger outbound requests through `
` tags or CSS `url()`. To limit where custom visualizations can load images from, set a Content Security Policy with an `img-src` allowlist in your app (the core Metabase app does this with [Restrict image domains](../../configuring-metabase/settings#restrict-image-domains)). [Only add visualizations you trust](../../questions/visualizations/custom#only-add-visualizations-you-trust).
+## Loading and error components
+
+The `loaderComponent` and `errorComponent` props swap the SDK's default loading and error screens for your own React components. See [Customize loading, error, and empty states](./loading-and-errors).
+
## Global event handlers
You can listen for events by defining the `eventHandlers` prop for `MetabaseProvider`.
diff --git a/_docs/v0.63/embedding/sdk/introduction.md b/_docs/v0.63/embedding/sdk/introduction.md
index 499b08e1d2..e0b033bded 100644
--- a/_docs/v0.63/embedding/sdk/introduction.md
+++ b/_docs/v0.63/embedding/sdk/introduction.md
@@ -112,13 +112,14 @@ Start with one of the quickstarts, then see these pages for more info on compone
- [Authentication](../authentication)
- [Embed a chart](../chart)
-- [AI chat](./ai-chat)
+- [Embed an AI chat](../ai-chat)
- [Embed a dashboard](../dashboard)
- [Actions](./actions)
- [Appearance](../appearance)
- [Embed a collection browser](../browser)
- [Plugins](./plugins)
- [Config](./config)
+- [Customize loading, error, and empty states](./loading-and-errors)
- [Versioning](./version)
- [Notes on Next.js](./next-js)
diff --git a/_docs/v0.63/embedding/sdk/loading-and-errors.md b/_docs/v0.63/embedding/sdk/loading-and-errors.md
new file mode 100644
index 0000000000..94582ab8b1
--- /dev/null
+++ b/_docs/v0.63/embedding/sdk/loading-and-errors.md
@@ -0,0 +1,58 @@
+---
+version: v0.63
+has_magic_breadcrumbs: true
+show_category_breadcrumb: true
+show_title_breadcrumb: true
+category: Embedding
+title: 'Customize loading, error, and empty states'
+source_url: 'https://github.com/metabase/metabase/blob/master/docs/embedding/sdk/loading-and-errors.md'
+layout: new-docs
+summary: 'Replace the modular embedding SDK''s default loading screen, error screen, and no-results image with your own.'
+---
+
+# Customize loading, error, and empty states
+
+
+
+{% include plans-blockquote.html feature="Modular embedding SDK" sdk=true convert_pro_link_to_embedding=true %}
+
+While an embedded component is loading, when it fails, or when a query comes back with no rows, the [modular embedding SDK](./introduction) renders a screen of its own. You can swap in your own React components and images instead, so all three states match the rest of your app.
+
+These customizations are SDK only. `loaderComponent` and `errorComponent` are props on `MetabaseProvider`, the no-results image comes from a [plugin](./plugins), and there's no web component equivalent for any of them.
+
+## Replace the loading and error components
+
+Pass `loaderComponent` and `errorComponent` to `MetabaseProvider`. Every embedded component inside that provider picks them up.
+
+```tsx
+{% include_file "{{ dirname }}/snippets/appearance/customizing-loader-and-components.tsx" snippet="imports" %}
+
+{% include_file "{{ dirname }}/snippets/appearance/customizing-loader-and-components.tsx" snippet="example" %}
+```
+
+`loaderComponent` receives an optional `label` prop with the loading message, which you can render or ignore. `errorComponent` receives the error details, so you can decide how much of the error to show, and where to put it.
+
+## Error component props
+
+These are the props Metabase passes to your `errorComponent`. The `type` prop tells you how Metabase intended to display the error: `relative` errors sit in the flow of the component, while `fixed` errors are meant to overlay the page, like a toast.
+
+{% include_file "{{ dirname }}/api/snippets/SdkErrorComponentProps.md" snippet="properties" %}
+
+## Replace the no-results illustration
+
+By default, Metabase displays a sailboat image when a query returns no results. To use a different image, set the `getNoDataIllustration` and `getNoObjectIllustration` plugins.
+
+Unlike `loaderComponent` and `errorComponent`, these are [plugins](./plugins), so they go in `pluginsConfig` rather than being props of their own. And instead of a React component, each one returns a base64-encoded image:
+
+```typescript
+{% include_file "{{ dirname }}/snippets/plugins/custom-images.tsx" snippet="example" %}
+```
+
+`getNoDataIllustration` covers a query that came back with no rows. `getNoObjectIllustration` covers a search that turned up nothing, like a search page or an entity picker with no matches. Both can only be set [globally](./plugins#plugin-scope), on the provider.
+
+## Further reading
+
+- [Modular embedding SDK plugins](./plugins)
+- [Appearance](../appearance)
+- [Modular embedding SDK config](./config)
+- [Modular embedding components](../components)
diff --git a/_docs/v0.63/embedding/sdk/plugins.md b/_docs/v0.63/embedding/sdk/plugins.md
index afba1e042e..103404e4e5 100644
--- a/_docs/v0.63/embedding/sdk/plugins.md
+++ b/_docs/v0.63/embedding/sdk/plugins.md
@@ -65,15 +65,12 @@ To create clickable links in your table columns, set the column's formatting to
### `getNoDataIllustration` and `getNoObjectIllustration`
-By default, Metabase displays a sailboat image when a query returns no results. To use a different image, you can use `getNoDataIllustration` and `getNoObjectIllustration` plugins which can accept a custom base64-encoded image:
+By default, Metabase displays a sailboat image when a query returns no results. The plugins `getNoDataIllustration` and `getNoObjectIllustration` swap in an image of your own. Both can only be used [globally](#plugin-scope) on provider level.
-```typescript
-{% include_file "{{ dirname }}/snippets/plugins/custom-images.tsx" snippet="example" %}
-```
-
-The plugins `getNoDataIllustration` and `getNoObjectIllustration` can only be used [globally](#plugin-scope) on provider level.
+See [Replace the no-results illustration](./loading-and-errors#replace-the-no-results-illustration) for more information and examples.
## Further reading
- [Question plugins](../chart#customize-what-happens-when-someone-clicks-on-a-chart)
- [Dashboard plugins](../dashboard#customize-the-menu-on-dashboard-cards-react-sdk-only)
+- [Customize loading, error, and empty states](./loading-and-errors)
diff --git a/_docs/v0.63/embedding/start.md b/_docs/v0.63/embedding/start.md
index 0832f47c76..6e01ff7dbf 100644
--- a/_docs/v0.63/embedding/start.md
+++ b/_docs/v0.63/embedding/start.md
@@ -33,6 +33,10 @@ With the Modular embedding SDK, you can embed individual Metabase components wit
Jump to a SDK quickstart with a sample React application.
+### [Modular embedding components](./components)
+
+A map of the components you can embed, with links to each component's docs and its attribute and prop reference.
+
### [Embed a dashboard](./dashboard)
Embed a dashboard, view-only or interactive, with web components or the React SDK. Let people edit and create dashboards from your app.
@@ -61,10 +65,18 @@ Embed a browsable collection with web components or the React SDK, so people can
Every `` attribute and the `CollectionBrowser` props.
+### [Embed an AI chat](./ai-chat)
+
+Embed an AI chat with web components or the React SDK, so people can ask questions of their data in natural language.
+
### [Guest embedding](./guest-embedding)
Guest embedding is a secure way to embed charts and dashboards. Guest embeds are view-only; people won't be able to drill-through charts and tables.
+### [Customize loading, error, and empty states](./sdk/loading-and-errors)
+
+Replace the React SDK's default loading screen, error screen, and no-results image with your own.
+
### [Translating embeds](./translations)
Upload a translation dictionary to translate questions and dashboards in modular embeds.
diff --git a/_docs/v0.63/embedding/translations.md b/_docs/v0.63/embedding/translations.md
index 6842c81391..dad6882d2f 100644
--- a/_docs/v0.63/embedding/translations.md
+++ b/_docs/v0.63/embedding/translations.md
@@ -116,7 +116,7 @@ If the strings you want to translate include markdown formatting, you'll need to
## The AI chat component isn't translated
-One exception is that Metabase won't translate the text in the [AI chat component](./sdk/ai-chat). While Metabot can understand other languages, it works best in English.
+One exception is that Metabase won't translate the text in the [AI chat component](./ai-chat). While Metabot can understand other languages, it works best in English.
## Further reading