diff --git a/site/content/integrate/faq/_index.md b/site/content/integrate/faq/_index.md index 019fd68242..b75d6734e1 100644 --- a/site/content/integrate/faq/_index.md +++ b/site/content/integrate/faq/_index.md @@ -34,6 +34,8 @@ If there's no translation layer, Mattermost won't understand the data you're sen When "attachments" are mentioned in the integrations documentation, it refers to Slack's Message Attachments. These "attachments" can be optionally added as an array in the data sent by an integration, and are used to customize the formatting of the message. +For new interactive content — buttons, menus, and structured layouts — use [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) instead of attachment `actions`. Legacy attachments remain supported and are translated into MM Blocks at render time. + We currently don't support the ability to attach files to a post made by an integration. ### Where can I find existing integrations? diff --git a/site/content/integrate/plugins/interactive-messages/_index.md b/site/content/integrate/plugins/interactive-messages/_index.md index 0d665fadf3..cb84a5fa0e 100644 --- a/site/content/integrate/plugins/interactive-messages/_index.md +++ b/site/content/integrate/plugins/interactive-messages/_index.md @@ -1,7 +1,7 @@ --- title: "Interactive messages" heading: "Interactive messages" -description: "Mattermost supports interactive message buttons and menus for incoming and outgoing webhooks, custom slash commands, and plugins via actions. They help make your integrations richer by completing common tasks inside Mattermost conversations, increasing user engagement and productivity." +description: "Mattermost supports interactive message buttons and menus through MM Blocks, markdown action buttons, and legacy message attachments. They help make your integrations richer by completing common tasks inside Mattermost conversations, increasing user engagement and productivity." weight: 80 aliases: - /integrate/admin-guide/admin-interactive-messages/ @@ -21,15 +21,27 @@ To try it out, you can use this {{< newtabref href="https://github.com/matterpol ![image](poll.png) +## MM Blocks (recommended) + +[MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) are the recommended way to build interactive integration posts. Send a block tree in `props.mm_blocks` for layout, text, images, buttons, and menus, and register action handlers in `props.mm_blocks_actions`. + +See [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) for the full block schema, action types, validation limits, and examples. + ## Markdown action buttons -In addition to message-attachment buttons and menus, you can embed interactive affordances directly in a post's markdown body using `mmaction://` links backed by a `mm_blocks_actions` post prop. This is useful when a short message reads naturally with an inline "Approve" or "Reject" link and a full message attachment isn't warranted. +Embed interactive affordances directly in a post's markdown body using `mmaction://` links backed by the same `mm_blocks_actions` registry. This is useful when a short message reads naturally with an inline "Approve" or "Reject" link. See [markdown action buttons]({{< ref "/integrate/reference/markdown-actions" >}}) for the full schema, limits, and end-to-end flow. +## Legacy message attachment buttons and menus + +{{}} +The sections below describe interactive buttons and menus placed inside legacy [message attachments]({{< ref "/integrate/reference/message-attachments" >}}). Existing integrations continue to work — Mattermost translates attachment actions into MM Blocks at render time — but new integrations should use [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) directly. +{{}} + ## Message buttons -Add message buttons as `actions` in your integration {{< newtabref href="https://docs.mattermost.com/developer/message-attachments.html" title="message attachments" >}}. +Add message buttons as `actions` in your integration [message attachments]({{< ref "/integrate/reference/message-attachments" >}}). The following payload gives an example that uses message buttons. @@ -89,7 +101,7 @@ To return a custom error message to the user, your integration can respond with } ``` -The error message will be displayed to the user below the message attachment. If no custom error message is provided, a default "Action failed to execute" message is shown. This feature is available in Mattermost v10.5 and later. +The error message will be displayed to the user below the interactive content. If no custom error message is provided, a default "Action failed to execute" message is shown. This feature is available in Mattermost v10.5 and later. ![image](interactive_message.gif) @@ -301,7 +313,7 @@ Specify `users` as your action's `data_source` as follows: ### Parameters -Below is a brief description of each parameter to help you customize the interactive message button and menu in Mattermost. For more information on message attachments, {{< newtabref href="https://docs.mattermost.com/developer/message-attachments.html" title="see our documentation" >}}. +Below is a brief description of each parameter to help you customize legacy attachment interactive buttons and menus in Mattermost. For new integrations, see [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}). For more information on message attachments, see [message attachments]({{< ref "/integrate/reference/message-attachments" >}}). **ID**
A per post unique identifier. @@ -406,7 +418,7 @@ In most cases, your integration will do one or both of these things: ## Error handling -When an action button integration fails, Mattermost automatically displays an error message to the user below the message attachment. This provides immediate feedback when button actions don't work as expected. +When an action button integration fails, Mattermost automatically displays an error message to the user below the interactive content. This provides immediate feedback when button actions don't work as expected. ![image](action-button-error.png) @@ -476,7 +488,7 @@ It is likely for one of three reasons: ### How do I manage properties of an interactive message? -Use `update.Props` in the following ways to manage properties (`Props`) of an interactive message after a user performs an action via an interactive button or menu: +Use `update.Props` in the following ways to manage properties (`Props`) of an interactive message after a user performs an action via an interactive button or menu. When using MM Blocks, include updated `mm_blocks` and `mm_blocks_actions` in `update.props` as needed: - `update.Props == nil` - Do not update `Props` field. - `update.Props == {}` - Clear all properties, except the username and icon of the original message, as well as whether the message was pinned to channel or contained emoji reactions. diff --git a/site/content/integrate/reference/markdown-actions/_index.md b/site/content/integrate/reference/markdown-actions/_index.md index b81631c727..7a6f1b1202 100644 --- a/site/content/integrate/reference/markdown-actions/_index.md +++ b/site/content/integrate/reference/markdown-actions/_index.md @@ -3,10 +3,11 @@ title: "Markdown action buttons" heading: "Use markdown action buttons" description: "Markdown action buttons let an integration turn an inline post-markdown link into an action affordance. Clicking the link dispatches a post action to the integration's endpoint instead of navigating away, expanding interactivity beyond message attachments." weight: 45 +mermaid: true --- -{{}} -Markdown action buttons are one binding surface in a broader Interactive Messages framework under active development. Additional binding surfaces and action types are planned for future iterations. +{{}} +Markdown action buttons are one binding surface in the Interactive Messages framework alongside [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}). All surfaces share the same `mm_blocks_actions` action registry. {{}} Use markdown action buttons to add inline, in-text affordances to a post — without using a message attachment. They're useful when: @@ -15,7 +16,7 @@ Use markdown action buttons to add inline, in-text affordances to a post — wit - An integration wants to mix narrative text and action affordances in the same post body. - The visual weight of a full message attachment isn't warranted. -For attachment-style buttons and menus, see [interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}). +For block-style buttons and menus, see [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}). For legacy attachment actions, see [interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}). ## How it works @@ -137,14 +138,14 @@ Plugin updates to `mm_blocks_actions` via `UpdatePost` are accepted only when th The link text rendered as the button label. **``**
-The host portion of the URL. Must match a key in `props.mm_blocks_actions`. Must be alphanumeric (`[A-Za-z0-9]+`), matched case-sensitively. +The host portion of the URL. Must match a key in `props.mm_blocks_actions`. Must contain only letters, numbers, underscores, or hyphens (`[A-Za-z0-9_-]+`), matched case-sensitively, and may be up to 64 characters long. **``** (optional)
-`key=value` pairs that are forwarded with the dispatched action and merged into the target URL's query string by the server. Link-supplied values override registry-supplied values on key conflict. +`key=value` pairs that are forwarded with the dispatched action and merged into the target URL's query string by the server. Link-supplied values override registry-supplied values on key conflict. Up to 50 entries; each key may be up to 128 characters and each value up to 2048 characters. ## The `mm_blocks_actions` registry -The `mm_blocks_actions` post prop is a map keyed by action ID. Each entry describes how the server should handle clicks on that action. +The `mm_blocks_actions` post prop is a map keyed by action ID. Each entry describes how the server should handle clicks on that action. The registry supports up to 50 action entries. ```json { @@ -163,8 +164,8 @@ The `mm_blocks_actions` post prop is a map keyed by action ID. Each entry descri | --- | --- | --- | | `type` | yes | Action type. See [Action types](#action-types) below. | | `url` | depends on type | Target URL for the integration's callback endpoint. Required for `external`. | -| `context` | no | Object of server-side context values forwarded to the integration in the post-action request body. Not visible to the client. | -| `query` | no | Static `string -> string` map merged into the target URL's query string by the server. Combined with any query parameters supplied in the `mmaction://` link — link values win on key conflict. | +| `context` | no | Object of server-side context values forwarded to the integration in the post-action request body. Not visible to the client. Up to 50 entries; each key may be up to 128 characters. | +| `query` | no | Static `string -> string` map merged into the target URL's query string by the server. Combined with any query parameters supplied in the `mmaction://` link — link values win on key conflict. Up to 50 entries; each key may be up to 128 characters and each value up to 2048 characters. | ### Action types @@ -178,6 +179,22 @@ Additional action types may be introduced as the broader Interactive Messages fr The diagram below describes the lifecycle of a single click on a markdown action button. +{{< mermaid >}} +sequenceDiagram + participant Integration + participant Server as Mattermost server + participant Client + Integration->>Server: Create post (mmaction link + mm_blocks_actions) + Server->>Server: Validate post and store action registry + Server->>Client: Deliver post + Client->>Client: Render link as button + Client->>Server: POST /api/v4/posts/{id}/actions/{action_id} + Server->>Server: Merge query parameters into action URL + Server->>Integration: POST action callback + Integration->>Server: Post-action response + Server->>Client: Update post / ephemeral message +{{}} + 1. **Integration** creates a post with an `mmaction://` link and a matching `mm_blocks_actions[]` entry. 2. **Mattermost server** validates the post (`mm_blocks_actions` schema and limits) and stores it. 3. **Client** renders the link as a button. Clicking it dispatches `POST /api/v4/posts/{post_id}/actions/{action_id}` with the link's query string in the request body. @@ -186,20 +203,7 @@ The diagram below describes the lifecycle of a single click on a markdown action ## Receiving action callbacks -When a user clicks a markdown action button, the Mattermost server sends an HTTP POST request to the `url` configured in the matching `mm_blocks_actions` entry. The request body follows the same `PostActionIntegrationRequest` shape used by [message attachment]({{< ref "/integrate/reference/message-attachments" >}}) buttons — the integration responds with the same post-action response format. - -## Validation limits - -Posts that exceed any of the following limits are rejected at create or update time. - -| Limit | Value | -| --- | --- | -| Maximum entries in `mm_blocks_actions` | 50 | -| Maximum length of an action ID (map key) | 64 characters | -| Action ID character set | `[A-Za-z0-9]+` | -| Maximum entries in `query` (link or registry) | 50 | -| Maximum length of a query key | 128 characters | -| Maximum length of a query value | 2048 characters | +When a user clicks a markdown action button, the Mattermost server sends an HTTP POST request to the `url` configured in the matching `mm_blocks_actions` entry. The request body follows the same `PostActionIntegrationRequest` shape used by [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) and legacy [message attachment]({{< ref "/integrate/reference/message-attachments" >}}) buttons — the integration responds with the same post-action response format. ## Error reference @@ -207,7 +211,7 @@ The following error IDs may be returned by the post-action and post-create APIs | Error ID | Cause | | --- | --- | -| `api.post.do_action.query.app_error` | The query parameters supplied with the action click exceeded one of the limits above. | +| `api.post.do_action.query.app_error` | The query parameters supplied with the action click exceeded one of the limits described in [Link syntax](#link-syntax) or [The `mm_blocks_actions` registry](#the-mm_blocks_actions-registry). | | `api.post.do_action.merge_query.app_error` | The server could not merge the supplied query parameters into the action's target URL — typically because the URL is malformed. | ## Security considerations @@ -216,12 +220,13 @@ Markdown action buttons follow the same security model as message attachment act - The action `url` is invoked server-to-server, never directly from the client. - `context` values are server-only and are not exposed to the rendering client. -- Action IDs are validated as alphanumeric strings and matched case-sensitively. +- Action IDs must match the pattern `[A-Za-z0-9_-]+` — alphanumerics, underscore, and hyphen — and are matched case-sensitively. - Action entries with malformed or unknown `type` values are rejected at post-create time and never reach the click-dispatch path. ## See also -- [Interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}) — message-attachment buttons and menus. +- [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) — block-based buttons, menus, and layout. +- [Interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}) — overview and legacy attachment actions. - [Message attachments]({{< ref "/integrate/reference/message-attachments" >}}) — broader message format reference. - [Incoming webhooks]({{< ref "/integrate/webhooks/incoming" >}}) — submitting posts via webhooks. - {{< newtabref href="https://api.mattermost.com/#operation/CreatePost" title="REST API: create post" >}} diff --git a/site/content/integrate/reference/message-attachments/_index.md b/site/content/integrate/reference/message-attachments/_index.md index 6537570866..c42dfbca02 100644 --- a/site/content/integrate/reference/message-attachments/_index.md +++ b/site/content/integrate/reference/message-attachments/_index.md @@ -9,7 +9,11 @@ aliases: For additional formatting options, and for compatibility with Slack non-markdown integrations, an `attachments` array can be sent by integrations and rendered by Mattermost. -You can also add interactive message buttons as part of attachments. They help make your integrations richer by completing common tasks inside Mattermost conversations, increasing user engagement and productivity. For more information, see [documentation]({{< ref "/integrate/plugins/interactive-messages" >}}). +{{}} +For new integrations, prefer [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) over attachment `actions` for buttons and menus. Legacy attachments remain fully supported — Mattermost translates them into MM Blocks at render time — but native MM Blocks give you more layout control and a unified action registry in `props.mm_blocks_actions`. +{{}} + +You can also add interactive message buttons as part of attachments. They help make your integrations richer by completing common tasks inside Mattermost conversations, increasing user engagement and productivity. For more information, see [interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}) and [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}). ## Attachment options @@ -139,7 +143,9 @@ And here is how it renders in Mattermost: Yes, you can use the {{< newtabref href="https://api.mattermost.com/#operation/CreatePost" title="create post RESTful API" >}}. -You need to add an `attachments` key to the post’s `props` JSON field. The value is an array of message attachments you want attached to the post. See below for an example curl command. +You need to add an `attachments` key to the post's `props` JSON field. The value is an array of message attachments you want attached to the post. See below for an example curl command. + +For new interactive content, prefer `props.mm_blocks` and `props.mm_blocks_actions` instead. See [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}). `curl -i -X POST -H 'Content-Type: application/json' -d '{"channel_id":"qmd5oqtwoibz8cuzxzg5ekshgr", "message":"Test message #testing", "props":{"attachments": [{"pretext": "This is the attachment pretext.","text": "This is the attachment text."}]}}' https://{your-mattermost-site}/api/v4/posts` diff --git a/site/content/integrate/reference/mm-blocks/_index.md b/site/content/integrate/reference/mm-blocks/_index.md new file mode 100644 index 0000000000..851a6984b2 --- /dev/null +++ b/site/content/integrate/reference/mm-blocks/_index.md @@ -0,0 +1,496 @@ +--- +title: "MM Blocks" +heading: "Use MM Blocks" +description: "MM Blocks are Mattermost's canonical format for structured, interactive integration posts. Use props.mm_blocks for layout and controls, and props.mm_blocks_actions for server-side action dispatch." +weight: 42 +mermaid: true +--- + +MM Blocks are Mattermost's structured post format for integration messages. An integration sends a block tree in `props.mm_blocks` to define layout, text, images, buttons, and menus, and registers action handlers in `props.mm_blocks_actions` so the server can dispatch clicks and menu selections back to the integration. + +{{}} +MM Blocks are controlled by the `MmBlocksEnabled` feature flag (enabled by default). When disabled, MM Blocks payloads are not rendered and MM Blocks action cookies are rejected. +{{}} + +## How it works + +An interactive MM Blocks post has two parts: + +1. **`props.mm_blocks`** — an array of block objects that define layout, text, images, buttons, and menus. A post may contain up to 100 blocks in total, counting nested blocks throughout the tree. +2. **`props.mm_blocks_actions`** — a map keyed by action ID. Each entry tells the server what to do when a user clicks a button, selects a menu option, or activates a [markdown action button]({{< ref "/integrate/reference/markdown-actions" >}}). + +When the post is stored, the server validates that every referenced action ID has a matching registry entry (and no unused entries remain). It then encrypts the action registry into an opaque cookie string that clients send back when dispatching actions. + +## Example post payload + +The following payload posts a message with text, a primary button, and a static select menu: + +```json +{ + "channel_id": "qmd5oqtwoibz8cuzxzg5ekshgr", + "message": "Deployment #42 finished.", + "props": { + "mm_blocks": [ + { + "type": "text", + "text": "Deployed `main` to **staging**. Choose a follow-up action:" + }, + { + "type": "container", + "flow": "horizontal", + "gap": "small", + "content": [ + { + "type": "button", + "text": "View logs", + "style": "primary", + "action_id": "view_logs" + }, + { + "type": "button", + "text": "Rollback", + "style": "danger", + "action_id": "rollback" + }, + { + "type": "static_select", + "action_id": "next_step", + "placeholder": "Select next step…", + "options": [ + {"text": "Promote to production", "value": "promote"}, + {"text": "Run smoke tests", "value": "smoke"} + ] + } + ] + } + ], + "mm_blocks_actions": { + "view_logs": { + "type": "external", + "url": "https://integration.example.com/actions/view-logs", + "context": {"deployment_id": "42"} + }, + "rollback": { + "type": "external", + "url": "https://integration.example.com/actions/rollback", + "context": {"deployment_id": "42"} + }, + "next_step": { + "type": "external", + "url": "https://integration.example.com/actions/next-step", + "context": {"deployment_id": "42"} + } + } + } +} +``` + +You can send this payload using the {{< newtabref href="https://api.mattermost.com/#operation/CreatePost" title="create post REST API" >}}, an [incoming webhook]({{< ref "/integrate/webhooks/incoming" >}}), a [custom slash command]({{< ref "/integrate/slash-commands/custom" >}}), or from a [plugin]({{< ref "/integrate/plugins/components/server" >}}). + +### Submit using an incoming webhook + +```bash +curl -X POST $MM_URL/hooks/$WEBHOOK_ID \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Deployment #42 finished.", + "props": { + "mm_blocks": [ + {"type": "text", "text": "Deployed `main` to **staging**."}, + { + "type": "button", + "text": "View logs", + "style": "primary", + "action_id": "view_logs" + } + ], + "mm_blocks_actions": { + "view_logs": { + "type": "external", + "url": "https://integration.example.com/actions/view-logs", + "context": {"deployment_id": "42"} + } + } + } + }' +``` + +For webhook payloads, place `mm_blocks` and `mm_blocks_actions` inside `props`. The top-level `attachments` array remains available for legacy integrations. + +### Submit from a plugin + +```golang +post := &model.Post{ + ChannelId: channelID, + UserId: p.botID, + Message: "Deployment #42 finished.", + Props: model.StringInterface{ + "mm_blocks": []any{ + map[string]any{ + "type": "text", + "text": "Deployed `main` to **staging**.", + }, + map[string]any{ + "type": "button", + "text": "View logs", + "style": "primary", + "action_id": "view_logs", + }, + }, + "mm_blocks_actions": map[string]any{ + "view_logs": map[string]any{ + "type": "external", + "url": fmt.Sprintf("/plugins/%s/actions/view-logs", manifest.Id), + "context": map[string]any{"deployment_id": "42"}, + }, + }, + }, +} +_, err := p.API.CreatePost(post) +``` + +## Block types + +Each element of `props.mm_blocks` is an object with a required `type` field. The supported block types are: + +| Type | Purpose | +| --- | --- | +| [`text`](#text) | Markdown-formatted text | +| [`image`](#image) | Remote image with optional sizing and alignment | +| [`divider`](#divider) | Horizontal rule between blocks | +| [`button`](#button) | Interactive button | +| [`static_select`](#static-select) | Dropdown menu with static options or dynamic data sources | +| [`container`](#container) | Groups blocks with optional border, accent bar, background, and layout flow | +| [`collapsible`](#collapsible) | Expandable section with separate header and content block arrays | +| [`column_set`](#column-set-and-column) | Horizontal row of columns | +| [`column`](#column-set-and-column) | Column inside a `column_set` (not valid as a top-level block) | + +Nested layout blocks (`container`, `collapsible`, `column_set`, and `column`) may nest up to 32 levels deep. + +Malformed blocks are omitted at render time; valid sibling blocks still display. + +### Text + +```json +{ + "type": "text", + "text": "Hello **from** MM Blocks.", + "is_subtle": false, + "size": "default" +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `text` | yes | Markdown-formatted content. Supports @mentions. All `text` and [button](#button) `text` fields in a post share a combined limit of 16,000 characters. | +| `is_subtle` | no | When `true`, renders in a muted color. Does not change font size. | +| `size` | no | Typography scale: `small` or `default`. Omitted is equivalent to `default`. | + +### Image + +```json +{ + "type": "image", + "url": "https://example.com/logo.png", + "alt_text": "Company logo", + "title": "Logo", + "size": "medium", + "max_width": 400, + "max_height": 300, + "image_style": "default", + "horizontal_alignment": "center" +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `url` | yes | Image URL (GIF, JPEG, PNG, BMP, or SVG). | +| `alt_text` | no | Accessible description. | +| `title` | no | Plain-text tooltip shown on hover. | +| `size` | no | Preset sizing: `auto`, `xsmall`, `small`, `medium`, `large`, or `stretch`. Defaults to `stretch`. | +| `max_width` | no | Maximum width in pixels. | +| `max_height` | no | Maximum height in pixels. | +| `image_style` | no | `default` or `person` (avatar-style crop). | +| `horizontal_alignment` | no | `left`, `center`, or `right`. | + +### Divider + +```json +{"type": "divider"} +``` + +### Button + +```json +{ + "type": "button", + "text": "Approve", + "action_id": "approve", + "style": "primary", + "tooltip": "Approve this change", + "disabled": false, + "query": {"ticket": "ISS-101"} +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `text` | yes | Button label. Supports Markdown. Counts toward the combined 16,000-character limit for [text](#text) and button blocks. | +| `action_id` | yes | Must match a key in `mm_blocks_actions`. | +| `style` | no | Semantic color: `default`, `primary`, `danger`, `good`, `success`, or `warning`. Hex colors such as `#2d81ff` are also accepted. | +| `tooltip` | no | Help text shown on hover. | +| `disabled` | no | When `true`, the button renders but cannot be clicked. | +| `query` | no | Static query parameters merged into the action URL when clicked. Up to 50 entries; each key may be up to 128 characters and each value up to 2048 characters. | + +### Static select + +```json +{ + "type": "static_select", + "action_id": "pick_region", + "placeholder": "Pick a region", + "options": [ + {"text": "North", "value": "north"}, + {"text": "South", "value": "south"} + ], + "initial_option": "north", + "disabled": false, + "data_source": "channels" +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `action_id` | yes | Must match a key in `mm_blocks_actions`. | +| `placeholder` | yes | Placeholder text for the menu. | +| `options` | depends on | Array of `{text, value}` pairs. Required unless `data_source` is set. | +| `initial_option` | no | Pre-selected option value. | +| `disabled` | no | When `true`, the menu renders but cannot be used. | +| `data_source` | no | Dynamic option source: `channels` or `users`. When set, `options` is optional. Users can only select public channels in their teams. | + +When a user selects an option, the integration callback receives `selected_option` in the request `context` with the chosen value (or user/channel ID for dynamic data sources). + +### Container + +```json +{ + "type": "container", + "content": [ + {"type": "text", "text": "Container title"}, + {"type": "divider"}, + {"type": "text", "text": "Body copy", "is_subtle": true, "size": "small"} + ], + "border": true, + "accent_color": "primary", + "background": "gray", + "flow": "vertical", + "gap": "small", + "max_height": "medium" +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `content` | yes | Array of nested blocks. | +| `border` | no | When `true`, draws a border around the container. | +| `accent_color` | no | Left accent bar color. Semantic values: `default`, `primary`, `good`, `warning`, or `danger`. CSS colors such as `#439FE0` are also accepted. | +| `background` | no | `none` (default) or `gray`. | +| `flow` | no | Child layout direction: `horizontal` or `vertical`. Defaults to `vertical`. | +| `gap` | no | Spacing between children: `none`, `small`, `medium`, `large`, or `xlarge`. Defaults to `none`. | +| `max_height` | no | Maximum height preset: `none`, `small`, `medium`, or `large`. Overflowing content scrolls inside the container. On mobile, users can open scrollable content in a dedicated full-screen view. | + +### Collapsible + +```json +{ + "type": "collapsible", + "collapsed": false, + "header": [ + {"type": "text", "text": "**Details**"} + ], + "content": [ + {"type": "text", "text": "Expanded content goes here."} + ] +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `header` | yes | Blocks shown in the always-visible header row. | +| `content` | yes | Blocks shown when expanded. | +| `collapsed` | no | Initial collapsed state. Defaults to `false`. | + +### Column set and column + +```json +{ + "type": "column_set", + "gap": "medium", + "columns": [ + { + "type": "column", + "width": "stretch", + "gap": "small", + "items": [ + {"type": "text", "text": "Left column"} + ] + }, + { + "type": "column", + "width": "auto", + "items": [ + {"type": "text", "text": "Right column"} + ] + } + ] +} +``` + +`column` blocks are only valid inside a `column_set`. Each column has an `items` array of nested blocks. + +## The `mm_blocks_actions` registry + +The `mm_blocks_actions` post prop is a map keyed by action ID. Each entry describes how the server handles clicks on that action. The registry supports up to 50 action entries. Each action ID must match `[A-Za-z0-9_-]+`, may be up to 64 characters long, and is matched case-sensitively. + +```json +{ + "mm_blocks_actions": { + "": { + "type": "", + "url": "...", + "context": { ... }, + "query": { ... } + } + } +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `type` | yes | Action type. See [Action types](#action-types) below. | +| `url` | depends on type | Target URL. Required for `external` and `openURL`. | +| `context` | no | Server-side context forwarded to the integration in the post-action request body. Not visible to clients. Up to 50 entries; each key may be up to 128 characters. | +| `query` | no | Static `string → string` map merged into the target URL's query string. Combined with any per-control `query` on the block — block values win on key conflict. Up to 50 entries; each key may be up to 128 characters and each value up to 2048 characters. | + +Every action ID referenced by interactive content — MM Blocks controls, markdown `mmaction://` links, Block Kit actions, or Adaptive Card actions — must have a matching registry entry. Unused registry entries are rejected at post-create time. + +After the post is stored, clients receive an encrypted cookie string in place of the plaintext registry map. + +### Action types + +| Type | Behavior | +| --- | --- | +| `external` | The server sends an HTTP POST request to `url` with the standard post-action request body. The integration responds with a post-action response (update, ephemeral message, or navigation). Relative plugin paths such as `/plugins/myplugin/action` are supported. | +| `openURL` | Navigates the user without calling an integration. Relative paths (for example `/myteam/channels/off-topic`) navigate inside Mattermost. `http://` and `https://` URLs open in a new browser tab. Plugin paths are not allowed. | + +Additional action types may be introduced in future releases. Entries with an unknown `type` value are rejected at post-create time. + +## Action dispatch flow + +{{< mermaid >}} +sequenceDiagram + participant Integration + participant Server as Mattermost server + participant Client + Integration->>Server: Create post (mm_blocks + mm_blocks_actions) + Server->>Server: Validate and encrypt action registry + Server->>Client: Store and deliver post + Client->>Client: Render blocks + Client->>Server: POST /api/v4/posts/{id}/actions/{action_id} + alt openURL + Server->>Client: Navigate user + else external + Server->>Integration: POST action callback + Integration->>Server: Post-action response + Server->>Client: Update post / ephemeral message + end +{{}} + +1. **Integration** creates a post with `mm_blocks` controls and matching `mm_blocks_actions` entries. +2. **Mattermost server** validates the pairing, encrypts the action registry, and stores the post. +3. **Client** renders the blocks. When the user clicks a button or selects a menu option, the client sends `POST /api/v4/posts/{post_id}/actions/{action_id}` with the encrypted cookie, optional `query`, `selected_option` (for menus), and `integration_format: "mm_block"`. +4. **Mattermost server** decrypts the cookie, resolves the action, merges query parameters, and either navigates (`openURL`) or POSTs to the integration endpoint (`external`). +5. **Integration** responds with a standard post-action response. + +Action IDs in the URL path must match `[A-Za-z0-9_-]+`. + +## Receiving action callbacks + +When a user activates an `external` action, the Mattermost server sends an HTTP POST request to the configured `url`. The request body uses the same `PostActionIntegrationRequest` shape as [legacy message attachment]({{< ref "/integrate/reference/message-attachments" >}}) buttons: + +```json +{ + "user_id": "rd49ehbqyjytddasoownkuqrxe", + "user_name": "alice", + "channel_id": "j6j53p28k6urx15fpcgsr20psq", + "channel_name": "town-square", + "team_id": "5xxzt146eax4tul69409opqjlf", + "team_domain": "myteam", + "post_id": "gqrnh3675jfxzftnjyjfe4udeh", + "trigger_id": "...", + "type": "button", + "context": { + "deployment_id": "42", + "selected_option": "promote" + } +} +``` + +For static select menus, the selected value is added to `context.selected_option`. + +Integrations respond with the same post-action response format used by attachment actions: + +```json +{ + "update": { + "message": "Updated!", + "props": { + "mm_blocks": [ + {"type": "text", "text": "Deployment promoted to production."} + ] + } + }, + "ephemeral_text": "Promotion started.", + "goto_location": "/myteam/channels/releases" +} +``` + +| Response field | Description | +| --- | --- | +| `update` | Replaces the original post message and props. Use `update.props.mm_blocks` to refresh the block layout. | +| `ephemeral_text` | Sends a private message visible only to the user who clicked. | +| `goto_location` | Navigates the user to a URL after the action completes. Supports in-app paths and external URLs. | +| `error` | Returns a custom error message displayed below the interactive content. | +| `skip_slack_parsing` | Set to `true` to bypass Slack-compatibility parsing of `ephemeral_text`. | + +See [interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}) for error handling details and `update.props` semantics. + +## Legacy format compatibility + +Mattermost continues to accept these older payload formats: + +| Prop | Format | Notes | +| --- | --- | --- | +| `attachments` | Legacy [message attachments]({{< ref "/integrate/reference/message-attachments" >}}) | Attachment `actions` arrays are translated into MM Blocks buttons and selects at render time. | +| `blocks` | Slack Block Kit | Translated into MM Blocks. Interactive Block Kit elements require matching `mm_blocks_actions` entries keyed by `action_id`. | +| `cards` | Microsoft Adaptive Cards | Translated into MM Blocks. Interactive card actions require matching `mm_blocks_actions` entries keyed by action `id`. | + +New integrations should prefer native `mm_blocks` for full control over layout and action registration. + +## Security considerations + +MM Blocks follow the same security model as legacy interactive messages: + +- Integration `url` values are invoked server-to-server, never directly from the client. +- `context` values are server-only and are not exposed to rendering clients. +- After create, the plaintext `mm_blocks_actions` map is replaced with an encrypted cookie. +- Action IDs are validated and must match referenced interactive content exactly. +- `openURL` actions reject plugin paths and path-traversal segments. + +## See also + +- [Interactive messages]({{< ref "/integrate/plugins/interactive-messages" >}}) — overview, error handling, and legacy attachment actions. +- [Markdown action buttons]({{< ref "/integrate/reference/markdown-actions" >}}) — inline `mmaction://` links using the same action registry. +- [Message attachments]({{< ref "/integrate/reference/message-attachments" >}}) — legacy attachment format reference. +- [Incoming webhooks]({{< ref "/integrate/webhooks/incoming" >}}) — submitting posts via webhooks. +- {{< newtabref href="https://api.mattermost.com/#operation/CreatePost" title="REST API: create post" >}} diff --git a/site/content/integrate/slash-commands/custom/_index.md b/site/content/integrate/slash-commands/custom/_index.md index 8a3ba6dd02..f7f4633739 100644 --- a/site/content/integrate/slash-commands/custom/_index.md +++ b/site/content/integrate/slash-commands/custom/_index.md @@ -58,7 +58,7 @@ You can follow these general guidelines to set up a custom Mattermost slash comm 6. To have your application post a message back to `town-square`, it can respond to the HTTP `POST` or `GET` request with a JSON payload. - Mattermost supports several [parameters](#response-parameters) in the response to fine-tune the user's experience. For instance, you can override the username and profile picture the messages post as, or specify a custom post type when sending a webhook message for use by [plugins]({{< ref "/integrate/plugins" >}}). Messages with advanced formatting can be created by including an [attachment array]({{< ref "/integrate/reference/message-attachments" >}}) and [interactive message buttons]({{< ref "/integrate/plugins/interactive-messages" >}}) in the response payload. + Mattermost supports several [parameters](#response-parameters) in the response to fine-tune the user's experience. For instance, you can override the username and profile picture the messages post as, or specify a custom post type in the slash command response payload for use by [plugins]({{< ref "/integrate/plugins" >}}). Messages with advanced formatting can be created by including [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}), an [attachment array]({{< ref "/integrate/reference/message-attachments" >}}), or [interactive message buttons]({{< ref "/integrate/plugins/interactive-messages" >}}) in the response payload. Our external weather application could respond with a JSON payload like so: @@ -101,7 +101,7 @@ Slash command responses support more than just the `text` field. Here is a full | `type` | Sets the post `type`, mainly for use by plugins.
If not blank, must begin with `custom_`. Passing `attachments` will ignore this field and set the type to `slack_attachment`. | No | | `extra_responses` | An array of responses used to send more than one post in your response. Each item in this array takes the shape of its own command response, so it can include any of the other parameters listed here, except `goto_location` and `extra_responses` itself. Available from Mattermost v5.6. | No | | `skip_slack_parsing` | If set to `true` Mattermost will skip the [Slack compatibility]({{< ref "/integrate/slash-commands/slack" >}}) handling. Useful if the post contains text or code which is incorrectly handled by the Slack compatibility logic. Available from Mattermost v5.20. | No | -| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post. Mainly used by other integrations accessing posts through the {{< newtabref title="REST API" href="https://api.mattermost.com" >}}.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url` and `attachments`. | No | +| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post.
Mainly used by other integrations accessing posts through the {{< newtabref title="REST API" href="https://api.mattermost.com" >}}.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url`, and `attachments`.
Use `props.mm_blocks` (array of block objects for layout and interactive controls) and `props.mm_blocks_actions` (map of action IDs to server-side handlers) for [MM Blocks]({{< ref "/integrate/reference/mm-blocks" >}}) interactive content. | No | An response payload using several parameters could look like this: diff --git a/site/content/integrate/webhooks/incoming/_index.md b/site/content/integrate/webhooks/incoming/_index.md index da7715ef4c..a363a48dc2 100644 --- a/site/content/integrate/webhooks/incoming/_index.md +++ b/site/content/integrate/webhooks/incoming/_index.md @@ -81,7 +81,7 @@ Incoming webhooks support more than just the `text` field. Here is a full list o | `icon_emoji` | Overrides the profile picture and `icon_url` parameter.
Defaults to none and is not set during webhook creation.
The expected value is an emoji name as typed in a message, either with or without colons (`:`).
The {{}} configuration setting must be enabled for the override to take effect.. | No | | `attachments` | [Message attachments]({{}}) used for richer formatting options. | If `text` is not set, yes | | `type` | Sets the post `type`, mainly for use by plugins.
If not blank, must begin with `custom_` unless set to `burn_on_read`. | No | -| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post.
Mainly used by other integrations accessing posts through the REST API.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url`, `override_icon_emoji`, `webhook_display_name`, `card`, and `attachments`.
Props `card` allows for extra information (Markdown-formatted text) to be sent to Mattermost that will only be displayed in the RHS panel after a user selects the **info** icon displayed alongside the post.
The **info** icon cannot be customized and is only rendered visible to the user if there is `card` data passed into the message.
This property is available from Mattermost v5.14.
There is currently no Mobile support for `card` functionality. | No | +| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post.
Mainly used by other integrations accessing posts through the REST API.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url`, `override_icon_emoji`, `webhook_display_name`, `card`, and `attachments`.
Use `props.mm_blocks` and `props.mm_blocks_actions` for [MM Blocks]({{}}) interactive content.
Props `card` allows for extra information (Markdown-formatted text) to be sent to Mattermost that will only be displayed in the RHS panel after a user selects the **info** icon displayed alongside the post.
The **info** icon cannot be customized and is only rendered visible to the user if there is `card` data passed into the message.
This property is available from Mattermost v5.14.
There is currently no Mobile support for `card` functionality. | No | | `priority` | Set the priority of the message. See [Message Priority](/integrate/reference/message-priority/) | No | An example request using more parameters would look like this: diff --git a/site/content/integrate/webhooks/outgoing/_index.md b/site/content/integrate/webhooks/outgoing/_index.md index 76248c219b..d1ff7f26f6 100644 --- a/site/content/integrate/webhooks/outgoing/_index.md +++ b/site/content/integrate/webhooks/outgoing/_index.md @@ -96,7 +96,7 @@ Outgoing webhooks support more than just the `text` field. Here is a full list o | `icon_url` | Overrides the profile picture the message posts with.
Defaults to the URL set during webhook creation; if no icon was set during creation, the standard webhook icon ({{}}) is displayed.
The {{}} configuration setting must be enabled for the icon override to take effect. | No | | `attachments` | [Message attachments]({{}}) used for richer formatting options. | If `text` is not set, yes | | `type` | Sets the post `type`, mainly for use by plugins.
If not blank, must begin with "`custom_`".
Specifying a value for the `attachments` property will cause this field to be ignored, and the `type` value set to `slack_attachment`. | No | -| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post.
Mainly used by other integrations accessing posts through the REST API.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url`, `webhook_display_name`, and `attachments`. | No | +| `props` | Sets the post `props`, a JSON property bag for storing extra or meta data on the post.
Mainly used by other integrations accessing posts through the REST API.
The following keys are reserved: `from_webhook`, `override_username`, `override_icon_url`, `webhook_display_name`, and `attachments`.
Use `props.mm_blocks` and `props.mm_blocks_actions` for [MM Blocks]({{}}) interactive content. | No | | `priority` | Set the priority of the message. See [Message Priority](/integrate/reference/message-priority/) | No | An example response using more parameters would look like this: @@ -125,4 +125,4 @@ The response would produce a message like the following: ![`test-automation` bot showing test results](outgoing_webhooks_full_example.png) -Messages with advanced formatting can be created by including an [attachment array]({{< ref "/integrate/reference/message-attachments" >}}) and [interactive message buttons]({{< ref "/integrate/plugins/interactive-messages" >}}) in the JSON payload. +Messages with advanced formatting can be created by including [MM Blocks]({{}}), an [attachment array]({{}}), or [interactive message buttons]({{}}) in the JSON payload.