diff --git a/docs/develop/contribute/more-info/mobile/developer-setup/index.md b/docs/develop/contribute/more-info/mobile/developer-setup/index.md index 79b20e1833d7..2f6fc56f8fc6 100644 --- a/docs/develop/contribute/more-info/mobile/developer-setup/index.md +++ b/docs/develop/contribute/more-info/mobile/developer-setup/index.md @@ -1,5 +1,7 @@ --- -title: "Developer setup" +title: "Developer setup for Mattermost mobile apps" +description: "Set up a development environment for the React Native Mattermost mobile apps for iOS and Android." +sidebar_label: "Developer setup" sidebar_position: 1 --- @@ -110,7 +112,7 @@ Install [Xcode](https://apps.apple.com/us/app/xcode/id497799835?ls=1&mt=12) to b ### Install Ruby -A version of Ruby is automatically installed on macOS, but Mattermost React Native app development requires Ruby 3.2.0. You can check the current version of Ruby by running the following command. +A version of Ruby is automatically installed on macOS, but Mattermost React Native app development requires the Ruby version pinned in the repository's `.ruby-version` file (currently 3.2.11). You can check the current version of Ruby by running the following command. ```sh ruby --version ``` @@ -132,13 +134,30 @@ If it isn't, we recommend using [Ruby Version Manager](https://rvm.io) or your p ``` 4. Install the required version of Ruby ```sh - rvm install 3.2.0 + rvm install 3.2.11 ``` -5. (Optional) If you don't need to use a different version of Ruby for anything else, you'll want to change the default version of Ruby. Without this, you'll need to run `rvm use 3.2.0` any time you want to work on the mobile app. +5. (Optional) If you don't need to use a different version of Ruby for anything else, you'll want to change the default version of Ruby. Without this, you'll need to run `rvm use 3.2.11` any time you want to work on the mobile app. ```sh - rvm alias create default 3.2.0 + rvm alias create default 3.2.11 ``` +:::note[Apple Silicon] +On recent Apple Silicon Macs, installing Ruby through RVM can fail with repeated OpenSSL errors. If you hit those, [`rv`](https://github.com/spinel-coop/rv) (a Ruby version manager) is a reliable alternative: + +```sh +brew install rv +rv ruby install 3.2.11 +``` + +To have `rv` switch Ruby versions automatically, install its shell integration once. For example, for `zsh`: + +```sh +echo 'eval "$(rv shell init zsh)"' >> ~/.zshrc +``` + +Run `rv shell ` (`bash`, `fish`, etc.) to see the equivalent for your shell, then open a new terminal. After that, `rv` reads the repository's `.ruby-version` file and selects the pinned version automatically when you `cd` into the project. +::: + ## Additional setup for Android ### Download and install Android Studio or Android SDK CLI tools diff --git a/docs/develop/integrate/faq/index.md b/docs/develop/integrate/faq/index.md index 1f34dbaf9e64..a9b626ae24c2 100644 --- a/docs/develop/integrate/faq/index.md +++ b/docs/develop/integrate/faq/index.md @@ -30,6 +30,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 [Mattermost Blocks](/developers/integrate/reference/mm-blocks) instead of attachment `actions`. Legacy attachments remain supported and are translated into Mattermost 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/docs/develop/integrate/plugins/interactive-dialogs/index.md b/docs/develop/integrate/plugins/interactive-dialogs/index.md index 4eb8b09bcac8..2a75f3472c96 100644 --- a/docs/develop/integrate/plugins/interactive-dialogs/index.md +++ b/docs/develop/integrate/plugins/interactive-dialogs/index.md @@ -1,5 +1,7 @@ --- -title: "Interactive dialogs" +title: "Use interactive dialogs" +description: "Interactive dialogs let applications gather structured input from Mattermost users and submit it back to an integration through single-step forms or multi-step workflows." +sidebar_label: "Interactive dialogs" sidebar_position: 70 --- @@ -8,6 +10,8 @@ Integrations open dialogs by sending an `HTTP POST`, containing some data in the Moreover, [plugins](/developers/integrate/plugins/using-and-managing-plugins) can trigger a dialog based on user actions. For instance, if a plugin adds a button in the channel header, clicking that button may open a dialog. +From Mattermost v11.10, dialogs can be stacked: an [`action_button`](#action-button-elements) element inside a dialog lets a user open a child dialog on top of the current one. Up to three dialogs may be open at once; any request to open a dialog beyond this limit is silently ignored by the client. + Here is an example of what a dialog looks like for creating a Jira issue within the Mattermost user interface: ![image](interactive-dialog-example.png) @@ -64,6 +68,8 @@ Each dialog supports elements for users to enter information. - `radio`: Radio button option. Use this to quickly select an option from pre-selected choices. - `date`: Date picker field. Use this for selecting dates without time information. - `datetime`: Date and time picker field. Use this for selecting both date and time with timezone support. +- `file`: File upload field. Use this to allow users to attach one or more files as part of a dialog submission. +- `action_button`: Clickable button that opens a child (stacked) dialog. Use this to branch into a follow-up dialog without submitting the current one. (Minimum server version 11.10.) Each element is required by default, otherwise the client will return an error as shown below. Note that the error message will appear below the help text, if one is specified. To make an element optional, set the field `"optional": "true"`. @@ -436,6 +442,8 @@ The full list of supported fields for `date` elements is included below: | `help_text` | String | (Optional) Help text displayed below the field. Maximum 150 characters. | | `optional` | Boolean | (Optional) Set to `true` if this form element is not required. Default is `false`. | | `datetime_config` | Object | (Optional) Nested date configuration object. See [datetime_config object](#datetime_config-object) for supported properties. | +| `min_date` | String | (Deprecated — use `datetime_config.min_date`.) Earliest selectable date. Supports ISO date format (YYYY-MM-DD) or relative formats (`today`, `tomorrow`, `+1d`, `-7d`, etc.). Full ISO datetime strings are accepted, but only the date part is parsed; timezone information is ignored. | +| `max_date` | String | (Deprecated — use `datetime_config.max_date`.) Latest selectable date. Supports ISO date format (YYYY-MM-DD) or relative formats (`today`, `+30d`, `+1y`, etc.). Full ISO datetime strings are accepted, but only the date part is parsed; timezone information is ignored. | #### Date field usage examples @@ -503,6 +511,9 @@ The full list of supported fields for `datetime` elements is included below: | `help_text` | String | (Optional) Help text displayed below the field. Maximum 150 characters. | | `optional` | Boolean | (Optional) Set to `true` if this form element is not required. Default is `false`. | | `datetime_config` | Object | (Optional) Nested datetime configuration object. See [datetime_config object](#datetime_config-object) for supported properties. | +| `min_date` | String | (Deprecated — use `datetime_config.min_date`.) Earliest selectable date. Supports ISO format or relative formats (`today`, `tomorrow`, `+1d`, `-7d`, etc.). | +| `max_date` | String | (Deprecated — use `datetime_config.max_date`.) Latest selectable date. Supports ISO format or relative formats (`today`, `+30d`, `+1y`, etc.). | +| `time_interval` | Integer | (Deprecated — use `datetime_config.time_interval`.) Time selection interval in minutes. Must be between 1 and 1440, and must be a divisor of 1440 to create evenly spaced intervals throughout the day. Common values: 15, 30, 60, 90, 120. Default is 60. | #### DateTime field usage examples @@ -565,8 +576,9 @@ The `datetime_config` object groups date/datetime configuration into a single ne | `time_interval` | Integer | `datetime` | 11.6 | (Optional) Time selection interval in minutes. Must be between 1 and 1440, and must be a divisor of 1440. Default is 60. | | `location_timezone` | String | `datetime` | 11.6 | (Optional) IANA timezone used to display and submit the time (e.g. `America/Denver`, `Asia/Tokyo`). When set, all users see the same wall-clock time regardless of their own timezone. Defaults to the viewing user's timezone. | | `manual_time_entry` | Boolean | `datetime` | 11.8 | (Optional) When `true`, users can type the time directly in addition to using the dropdown. Default is `false`. | +| `allow_manual_time_entry` | Boolean | `datetime` | 11.6 (deprecated in 11.8) | (Deprecated — use `manual_time_entry`.) When both are set, either enabling turns the feature on. | -> **Breaking change (12.0):** The top-level `min_date`, `max_date`, and `time_interval` fields on `date`/`datetime` elements, and the `datetime_config.allow_manual_time_entry` field, have been removed. Integrations must send these values under `datetime_config` (using `manual_time_entry` instead of `allow_manual_time_entry`) or they will be silently ignored. +**Backward compatibility (new in 11.8):** The top-level `min_date`, `max_date`, and `time_interval` fields on `date` and `datetime` elements are still accepted for existing integrations, but are deprecated in favor of `datetime_config`. When both are provided on the same element, values inside `datetime_config` take precedence over the legacy top-level values. #### Date and DateTime field specifications @@ -607,6 +619,118 @@ The `datetime_config` object groups date/datetime configuration into a single ne - `"time_interval": 60` creates options: 00:00, 01:00, 02:00, 03:00, etc. - Invalid: `"time_interval": 7` (7 is not a divisor of 1440) +### File elements +#### Minimum Server Version: 11.10 + +File elements allow users to upload one or more files as part of an interactive dialog submission. Below is an example of a `file` element that requests a single file attachment. + +```json +{ + "display_name": "Attachment", + "name": "attachment", + "type": "file", + "optional": true +} +``` + +To allow multiple file uploads, set `allow_multiple` to `true`: + +```json +{ + "display_name": "Attachments", + "name": "attachments", + "type": "file", + "allow_multiple": true, + "optional": true +} +``` + +The full list of supported fields is included below: + +| Field | Type | Description | +|------------------|---------|------------------------------------------------------------------------------------------------------------------------------------| +| `display_name` | String | Display name of the field shown to the user in the dialog. Maximum 24 characters. | +| `name` | String | Name of the field element used by the integration. Maximum 300 characters. You should use unique `name` fields in the same dialog. | +| `type` | String | Set this value to `file` for a file upload element. | +| `optional` | Boolean | (Optional) Set to `true` if this form element is not required. Default is `false`. | +| `help_text` | String | (Optional) Set help text for this form element. Maximum 150 characters. | +| `default` | String | (Optional) Comma-separated list of file IDs, from files the user previously uploaded, used to pre-populate the field. Limited to a single ID unless `allow_multiple` is `true`, and no more than 10 IDs. Maximum 300 characters. | +| `placeholder` | String | (Optional) Hint text shown beneath the upload button while no file is selected. Maximum 300 characters. | +| `allow_multiple` | Boolean | (Optional) Set to `true` to allow the user to upload more than one file. Default is `false`. | + +#### File upload limits + +- A maximum of 10 file IDs may be submitted per dialog submission. +- The server validates that each submitted file ID was uploaded by the submitting user. Referencing a file uploaded by a different user will be rejected. +- Duplicate file IDs are automatically deduplicated before validation. + +#### File IDs in the submission payload + +When a dialog containing file elements is submitted, the uploaded file IDs are sent in a top-level `file_ids` array alongside the standard `submission` map. Use `file_ids` as the authoritative source of uploaded file IDs — each ID can be used with the [Files API](https://api.mattermost.com/#tag/files) to attach files to posts or retrieve file metadata. + +### Action button elements +#### Minimum Server Version: 11.10 + +Action button elements render a clickable button inside a dialog. Instead of submitting the current dialog, clicking the button asks your integration to open a *child* dialog that is stacked on top of the current one. Use this to branch into a follow-up workflow (for example, "Add another item" or "Configure advanced options") while preserving the dialog the user is already working in. + +```json +{ + "display_name": "Add attachment", + "name": "add_attachment", + "type": "action_button", + "action_button": { + "url": "https://your-mattermost-server.com/plugins/your-plugin-id/api/attach", + "context": { + "action": "open_attachment_dialog" + } + } +} +``` + +The full list of supported fields is included below: + +| Field | Type | Description | +|-----------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `display_name` | String | Display name shown on the button. Maximum 24 characters. | +| `name` | String | Name of the field element used by the integration. Maximum 300 characters. You should use unique `name` fields in the same dialog. | +| `type` | String | Set this value to `action_button` for an action button element. | +| `action_button` | Object | Action button configuration. Required for this element type. See the fields below. | + +The `action_button` object supports: + +| Field | Type | Description | +|-----------|--------|---------------------------------------------------------------------------------------------------------------------------------------| +| `url` | String | The URL the action is sent to when the button is clicked. Required. Must be a valid lookup URL — an HTTPS URL or a `/plugins/` path. | +| `context` | Object | (Optional) String map of arbitrary key/value pairs forwarded to your integration when the button is clicked. Use this to identify which button was pressed or to carry state. | + +Action button elements are not input fields: their values are never included in the dialog submission payload, and they are not subject to the required/optional validation applied to other elements. + +#### Action button flow + +When a user clicks an action button, the Mattermost client sends an `HTTP POST` to the `/api/v4/actions/dialogs/execute` endpoint. The server validates the request, generates a new trigger ID, and forwards the button's `context` to the `url` configured on the button. The payload your integration receives is a `PostActionIntegrationRequest`: + +```json +{ + "type": "dialog_action", + "trigger_id": "", + "user_id": "", + "user_name": "", + "channel_id": "", + "channel_name": "", + "team_id": "", + "team_name": "", + "context": { + "action": "open_attachment_dialog" + } +} +``` + +Use the `trigger_id` from this payload to [open a child dialog](#open-a-dialog) — send an `HTTP POST` to `/api/v4/actions/dialogs/open` exactly as you would for a top-level dialog. The child dialog is displayed stacked on top of the dialog the user clicked the button in. + +:::note +Up to three dialogs may be open at once. If three dialogs are already open, the request to open a further child dialog is silently ignored by the client, so design your workflows to stay within this limit. +::: + ## Dialog submission When a user submits a dialog, Mattermost will perform client-side input validation to make sure: @@ -623,15 +747,20 @@ The submission payload sent to the integration is: "state": "", "user_id": "", "channel_id": "", - "team_id": "", + "team_id": "", "submission": { "some_element_name": "", "some_other_element": "" }, + "file_ids": ["", ""], "cancelled": false } ``` +:::note[`team_id` behavior change (server version 11.10)] +For the `/api/v4/actions/dialogs/submit`, `/api/v4/actions/dialogs/lookup`, and `/api/v4/actions/dialogs/execute` endpoints, the `team_id` forwarded to your integration is now always derived by the server from the channel the dialog was opened in, rather than from the value supplied by the client. Any client-supplied `team_id` is ignored. For direct (DM) and group (GM) message channels — which do not belong to a team — `team_id` is sent as an empty string. If your integration previously relied on a client-supplied `team_id` for team-specific logic, update it to use this channel-derived value (and to handle the empty case for DM/GM channels). +::: + Optionally, the dialog can send an event back to the integration if `notify_on_cancel` parameter is set to `true`. If this happens, `cancelled` will be set to `true` on the above payload, and `submission` will be empty. Moreover, Mattermost also allows the integration itself to perform input validation. This can be done by responding to the dialog submission request with a JSON body containing an `errors` field. The `errors` field can contain a JSON object, mapping input field names to string error messages you would like to display to the user. For example, if you have a field named `num_between_0_and_10`, you can enforce the user to enter a number between 0 and 10 by returning the following response body if the condition isn't satisfied: diff --git a/docs/develop/integrate/plugins/interactive-messages/index.md b/docs/develop/integrate/plugins/interactive-messages/index.md index ec6f9bfd2f4e..7f0f5546095a 100644 --- a/docs/develop/integrate/plugins/interactive-messages/index.md +++ b/docs/develop/integrate/plugins/interactive-messages/index.md @@ -1,5 +1,6 @@ --- title: "Interactive messages" +description: "Mattermost supports interactive message buttons and menus through Mattermost Blocks, markdown action buttons, and legacy message attachments so integrations can complete common tasks inside conversations." sidebar_position: 80 --- @@ -17,15 +18,27 @@ To try it out, you can use this [Matterpoll plugin](https://github.com/matterpol ![image](poll.png) +## Mattermost Blocks (recommended) + +[Mattermost Blocks](/developers/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 [Mattermost Blocks](/developers/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](/developers/integrate/reference/markdown-actions) for the full schema, limits, and end-to-end flow. +## Legacy message attachment buttons and menus + +:::note[Legacy format] +The sections below describe interactive buttons and menus placed inside legacy [message attachments](/developers/integrate/reference/message-attachments). Existing integrations continue to work — Mattermost translates attachment actions into Mattermost Blocks at render time — but new integrations should use [Mattermost Blocks](/developers/integrate/reference/mm-blocks) directly. +::: + ## Message buttons -Add message buttons as `actions` in your integration [message attachments](https://docs.mattermost.com/developer/message-attachments.html). +Add message buttons as `actions` in your integration [message attachments](/developers/integrate/reference/message-attachments). The following payload gives an example that uses message buttons. @@ -85,7 +98,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) @@ -297,7 +310,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, [see our documentation](https://docs.mattermost.com/developer/message-attachments.html). +Below is a brief description of each parameter to help you customize legacy attachment interactive buttons and menus in Mattermost. For new integrations, see [Mattermost Blocks](/developers/integrate/reference/mm-blocks). For more information on message attachments, see [message attachments](/developers/integrate/reference/message-attachments). **ID**
A per post unique identifier. @@ -402,7 +415,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) @@ -472,7 +485,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 Mattermost 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/docs/develop/integrate/plugins/using-and-managing-plugins.md b/docs/develop/integrate/plugins/using-and-managing-plugins.md index 33d12f6ec21b..0f25cea31824 100644 --- a/docs/develop/integrate/plugins/using-and-managing-plugins.md +++ b/docs/develop/integrate/plugins/using-and-managing-plugins.md @@ -1,5 +1,7 @@ --- -title: "Use and manage plugins" +title: "Use plugins with Mattermost" +description: "Mattermost supports plugins to customize and extend the platform." +sidebar_label: "Use and manage plugins" sidebar_position: 50 --- @@ -98,9 +100,9 @@ You should only install custom plugins from sources you trust to avoid compromis ## Plugin signing -The Marketplace allows System Admins to download and install plugins from a central repository. Plugins installed via the Marketplace must be signed by a public key certificate trusted by the local Mattermost server. +The Marketplace allows System Admins to download and install plugins from a central repository. Plugins installed via the Marketplace must be signed by a public key trusted by the local Mattermost server. -While the server ships with a default certificate used to verify plugins from the default Mattermost Marketplace, the server can be configured to trust different certificates and point at a different plugin marketplace. This document outlines the steps for generating a public key certificate and signing plugins for use with a custom plugin marketplace. It assumes access to the [GNU Privacy Guard (GPG)](https://gnupg.org) tool. +While the server ships with a default public key used to verify plugins from the default Mattermost Marketplace, the server can be configured to trust different public keys and point at a different plugin marketplace. This document outlines the steps for generating a public key pair and signing plugins for use with a custom plugin marketplace. It assumes access to the [GNU Privacy Guard (GPG)](https://gnupg.org) tool. ### Configuration @@ -191,21 +193,17 @@ This command will generate `com.mattermost.demo-plugin-0.1.0.tar.gz.sig`, which ## Plugin verification -Mattermost server will verify plugin signatures downloaded from the Marketplace. To add custom public keys, run the following command on the Mattermost server: +Mattermost server will verify plugin signatures downloaded from the Marketplace. Plugins are verified against a hard-coded Mattermost public key, as well as any additional public keys configured on the server. -`mattermost plugin add key my-pub-key` +To add custom public keys for plugin signature verification, add the key file names to the `PluginSettings.SignaturePublicKeyFiles` setting in your `config.json`: -Multiple public keys can be added to the Mattermost server: - -`mattermost plugin add key my-pk-file1 my-pk-file2` - -To list the names of all public keys installed on your Mattermost server, use: - -`mattermost plugin keys` - -To delete public key(s) from your Mattermost server, use: +```json +"PluginSettings": { + "SignaturePublicKeyFiles": ["my-pub-key-1", "my-pub-key-2"] +} +``` -`mattermost plugin delete key my-pk-file1 my-pk-file2` +The key files themselves must be stored in the Mattermost server's config directory. ### Implementation diff --git a/docs/develop/integrate/reference/markdown-actions/index.md b/docs/develop/integrate/reference/markdown-actions/index.md index 51af5547f7c2..388f6f7601ce 100644 --- a/docs/develop/integrate/reference/markdown-actions/index.md +++ b/docs/develop/integrate/reference/markdown-actions/index.md @@ -1,12 +1,12 @@ --- -title: "Markdown action buttons" +title: "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." sidebar_label: "Markdown action buttons" sidebar_position: 45 --- -:::note Part of a broader framework -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. +:::note[Part of the Interactive Messages framework] +Markdown action buttons are one binding surface in the Interactive Messages framework alongside [Mattermost Blocks](/developers/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 +15,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](/developers/integrate/plugins/interactive-messages). +For block-style buttons and menus, see [Mattermost Blocks](/developers/integrate/reference/mm-blocks). For legacy attachment actions, see [interactive messages](/developers/integrate/plugins/interactive-messages). ## How it works @@ -125,7 +125,7 @@ post := &model.Post{ _, err := p.API.CreatePost(post) ``` -Plugin updates to `mm_blocks_actions` via `UpdatePost` are accepted only when the updated value passes validation. Removal of the `mm_blocks_actions` prop by non-integration sessions is restricted to prevent dropping or corrupting actions on posts owned by another integration. +A plugin can also change markdown actions later via `API.UpdatePost`. See [Updating and removing actions](#updating-and-removing-actions) for who may change `mm_blocks_actions` and how actions are removed. ## Link syntax @@ -137,14 +137,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 +163,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 +178,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,7 +202,13 @@ 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](/developers/integrate/reference/message-attachments) buttons — the integration responds with the same post-action response format. +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 [Mattermost Blocks](/developers/integrate/reference/mm-blocks) and legacy [message attachment](/developers/integrate/reference/message-attachments) buttons — the integration responds with the same post-action response format. + +## Updating and removing actions + +- An integration session (bot account, personal access token, or OAuth app) may add, replace, or remove `mm_blocks_actions` on a post **it authored**, via the update and patch post endpoints. Other sessions can edit the message but not another author's actions. +- `mm_blocks_actions` is an exception to the usual update rule that omitted fields are cleared: the server preserves the post's existing actions when an update leaves the prop out, so a message-only edit never wipes buttons. (Sending an empty or altered value still replaces them.) +- Removing a button's `mmaction://` link from the message revokes its action: the entry is pruned and later clicks return a not-found error. ## Validation limits @@ -196,7 +218,7 @@ Posts that exceed any of the following limits are rejected at create or update t | --- | --- | | 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]+` | +| 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 | @@ -207,7 +229,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 +238,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](/developers/integrate/plugins/interactive-messages) — message-attachment buttons and menus. +- [Mattermost Blocks](/developers/integrate/reference/mm-blocks) — block-based buttons, menus, and layout. +- [Interactive messages](/developers/integrate/plugins/interactive-messages) — overview and legacy attachment actions. - [Message attachments](/developers/integrate/reference/message-attachments) — broader message format reference. - [Incoming webhooks](/developers/integrate/webhooks/incoming) — submitting posts via webhooks. - [REST API: create post](https://api.mattermost.com/#operation/CreatePost) diff --git a/docs/develop/integrate/reference/message-attachments/index.md b/docs/develop/integrate/reference/message-attachments/index.md index 912daa9ac484..704fc63a1d33 100644 --- a/docs/develop/integrate/reference/message-attachments/index.md +++ b/docs/develop/integrate/reference/message-attachments/index.md @@ -1,11 +1,17 @@ --- -title: "Message attachments" +title: "Use message attachments" +description: "Message attachments provide rich formatting options for integrations, including bold and italic text, headings, inline images, and tables." +sidebar_label: "Message attachments" sidebar_position: 40 --- 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](/developers/integrate/plugins/interactive-messages). +:::note[Legacy interactive content] +For new integrations, prefer [Mattermost Blocks](/developers/integrate/reference/mm-blocks) over attachment `actions` for buttons and menus. Legacy attachments remain fully supported — Mattermost translates them into Mattermost Blocks at render time — but native Mattermost 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](/developers/integrate/plugins/interactive-messages) and [Mattermost Blocks](/developers/integrate/reference/mm-blocks). ## Attachment options @@ -129,13 +135,29 @@ And here is how it renders in Mattermost: 1. The footer timestamp field (`ts`) is not yet supported. 2. Message Attachment contents do not show up in search. +## Trigger AI agent responses + +To have Mattermost AI agents respond to a post created by a bot or plugin integration, set the `activate_ai` property to `"true"` in the post's `props` field. + +```json +{ + "channel_id": "qmd5oqtwoibz8cuzxzg5ekshgr", + "message": "Summarize the latest deployment status.", + "props": { + "activate_ai": "true" + } +} +``` + ## Frequently asked questions ### Can I send a message attachment via the API? Yes, you can use the [create post RESTful API](https://api.mattermost.com/#operation/CreatePost). -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 [Mattermost Blocks](/developers/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/docs/develop/integrate/reference/message-priority/index.md b/docs/develop/integrate/reference/message-priority/index.md index 44739c19ad97..9dbd0ce6e9d9 100644 --- a/docs/develop/integrate/reference/message-priority/index.md +++ b/docs/develop/integrate/reference/message-priority/index.md @@ -1,5 +1,7 @@ --- -title: "Message priority" +title: "Use message priority" +description: "Set message priority levels so users can identify important messages and optionally request acknowledgments or persistent notifications." +sidebar_label: "Message priority" sidebar_position: 50 --- @@ -13,7 +15,7 @@ When sending a message, you can use any of the following to format how you want `requested_ack`: If set to `true`, the message will be marked as requiring an acknowledgment from the users by displaying a checkmark icon next to the message. Keep in mind that this requires the message priority to be set to _Important_ or _Urgent_. -`persistent_notifications`: Only for _Urgent_ messages. If set to `true` recipients will receive a persistent notification every five minutes until they acknowledge the message. +`persistent_notifications`: Only for _Urgent_ messages. If set to `true` recipients will receive a persistent notification every five minutes until they acknowledge the message. Mutually exclusive with the [`silent`](/developers/integrate/webhooks/incoming#parameters) field on incoming webhooks — a post cannot be both persistent and silent. ## Example of post priority diff --git a/docs/develop/integrate/reference/mm-blocks/index.md b/docs/develop/integrate/reference/mm-blocks/index.md new file mode 100644 index 000000000000..e7d8c4e1d717 --- /dev/null +++ b/docs/develop/integrate/reference/mm-blocks/index.md @@ -0,0 +1,496 @@ +--- +title: "Mattermost Blocks" +description: "Mattermost Blocks are the 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." +sidebar_label: "Mattermost Blocks" +sidebar_position: 42 +--- + +Mattermost Blocks are the 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. + +:::note[Feature flag] +Mattermost Blocks are controlled by the `MmBlocksEnabled` feature flag (enabled by default). When disabled, Mattermost Blocks payloads are not rendered and Mattermost Blocks action cookies are rejected. +::: + +## How it works + +An interactive Mattermost 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](/developers/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 [create post REST API](https://api.mattermost.com/#operation/CreatePost), an [incoming webhook](/developers/integrate/webhooks/incoming), a [custom slash command](/developers/integrate/slash-commands/custom), or from a [plugin](/developers/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** Mattermost 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 — Mattermost 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](/developers/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](/developers/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](/developers/integrate/reference/message-attachments) | Attachment `actions` arrays are translated into Mattermost Blocks buttons and selects at render time. | +| `blocks` | Slack Block Kit | Translated into Mattermost Blocks. Interactive Block Kit elements require matching `mm_blocks_actions` entries keyed by `action_id`. | +| `cards` | Microsoft Adaptive Cards | Translated into Mattermost 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 + +Mattermost 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 + +- [Troubleshoot Mattermost Blocks](/developers/integrate/reference/mm-blocks/troubleshooting) — common rendering, action, and mobile issues. +- [Interactive messages](/developers/integrate/plugins/interactive-messages) — overview, error handling, and legacy attachment actions. +- [Markdown action buttons](/developers/integrate/reference/markdown-actions) — inline `mmaction://` links using the same action registry. +- [Message attachments](/developers/integrate/reference/message-attachments) — legacy attachment format reference. +- [Incoming webhooks](/developers/integrate/webhooks/incoming) — submitting posts via webhooks. +- [REST API: create post](https://api.mattermost.com/#operation/CreatePost) diff --git a/docs/develop/integrate/reference/mm-blocks/troubleshooting.md b/docs/develop/integrate/reference/mm-blocks/troubleshooting.md new file mode 100644 index 000000000000..9c7da23c92db --- /dev/null +++ b/docs/develop/integrate/reference/mm-blocks/troubleshooting.md @@ -0,0 +1,88 @@ +--- +title: "Troubleshoot Mattermost Blocks" +description: "Common issues when Mattermost Blocks posts do not render or respond as expected, with guidance for integration developers and mobile client behavior." +sidebar_label: "Troubleshoot Mattermost Blocks" +sidebar_position: 43 +--- + +Integration posts that use Mattermost Blocks show structured content—including text, images, buttons, and menus—directly in a channel. This page covers common issues when Mattermost Blocks do not render or respond as expected. Use it alongside the [Mattermost Blocks reference](/developers/integrate/reference/mm-blocks) when debugging payloads, action handlers, and client rendering. + +## Mattermost Blocks content does not appear + +**Symptoms:** A post shows only plain text (or no content) and the expected buttons, images, or content blocks are missing. + +**Try the following:** + +1. **Confirm the integration payload.** The post must include a non-empty `props.mm_blocks` array (or a legacy format such as [message attachments](/developers/integrate/reference/message-attachments) that the client translates). Verify the webhook, bot, plugin, or REST API payload before testing in a client. +2. **Check the feature flag (self-hosted admins).** Mattermost Blocks are controlled by the `MmBlocksEnabled` feature flag (enabled by default). Self-hosted deployments can disable Mattermost Blocks by setting `MM_FEATUREFLAGS_MMBLOCKSENABLED=false`. When disabled, native Mattermost Blocks payloads are not rendered and their actions are rejected. +3. **Update the client.** Mattermost Blocks require a current Mattermost web, desktop, or mobile app. See [client availability](https://docs.mattermost.com/end-user-guide/access/client-availability.html) in the product documentation for platform support. +4. **Reload the channel.** Pull to refresh on mobile, or switch channels and return, to fetch the latest post data. + +## Buttons or menus do not respond + +**Symptoms:** Buttons appear disabled, or tapping a button or menu option has no effect. + +**Try the following:** + +1. **Validate the action registry.** Every `action_id` on a button, `static_select`, or markdown action must have a matching entry in `props.mm_blocks_actions`. Unused registry entries and unreferenced action IDs are rejected at post-create time. See [The `mm_blocks_actions` registry](/developers/integrate/reference/mm-blocks#the-mm_blocks_actions-registry). +2. **Wait for the action to finish.** Some integrations show a loading state while Mattermost calls an external service. Slow integrations may time out based on your server's [integration request timeout](https://docs.mattermost.com/configure/configuration-settings.html#integration-request-timeout) setting. +3. **Check whether the control is disabled.** Integrations can send buttons and menus with `"disabled": true`. Disabled controls cannot be activated. +4. **Verify outbound server connectivity.** External actions require Mattermost **server nodes** (not end-user devices) to make **outbound** requests to each integration action endpoint URL configured in the post. These endpoints typically use **HTTPS on TCP port 443**; if a URL uses another scheme or port, that destination must be reachable outbound from the server. Allow only the specific integration endpoints your organization uses—this is not an inbound connection requirement. For server-side 400 errors, see [Why does an interactive button or menu return a 400 error?](/developers/integrate/plugins/interactive-messages#why-does-an-interactive-button-or-menu-return-a-400-error). +5. **Look for follow-up messages.** Successful actions may update the original post or return an ephemeral reply visible only to the user who clicked. Check the thread panel if the post is part of a thread. + +## Scrollable content issues on mobile + +**Symptoms:** Clipped integration content cannot be expanded, or the **Scrollable content** screen is empty. + +Some integration posts limit the height of a content region using a container `max_height` value. When content overflows, Mattermost mobile shows a clipped preview with an expand control in the corner of the region. + +**To view the full content on mobile:** + +1. Locate the clipped region in the post (a fade at the bottom indicates more content below). +2. Select the expand control in the bottom-right corner of the clipped area. +3. Mattermost opens a full-screen **Scrollable content** view where you can scroll through the complete block content. +4. Use the back gesture or navigation control to return to the channel. + +**If the Scrollable content screen shows "Cannot display content":** + +- Return to the channel and open the expand control again. This screen appears when the expanded payload is no longer available (for example, after navigating away before the view loaded). +- Update to the latest mobile app build if the issue persists across multiple posts. + +On web and desktop, the same clipped regions scroll inside the post; a separate full-screen view is not used. + +## Collapsible sections or images look wrong + +**Symptoms:** A section will not expand, an image fails to load, or part of the layout is missing. + +**Try the following:** + +1. **Collapsible sections:** Select the section header to toggle between expanded and collapsed states. If the header is missing or empty, the integration payload may be incomplete. Both `header` and `content` arrays are required on `collapsible` blocks. +2. **Images:** External images require a valid URL and may be blocked by your server's image proxy or SVG settings. Contact your system admin if images from other integrations load but Mattermost Blocks images do not. +3. **Partial content:** Clients skip individual malformed blocks and still render valid ones in the same post. If only some elements are missing, the integration payload likely contains invalid block entries. Compare the payload against the [block types](/developers/integrate/reference/mm-blocks#block-types) reference. + +## Not all blocks appear or text is cut off + +**Symptoms:** Only part of an integration post renders, blocks at the end of the post are missing, or text in a block or on a button label ends abruptly. + +Mattermost enforces size limits on Mattermost Blocks payloads. Content that exceeds a limit is truncated when the post is rendered. + +**Try the following:** + +1. **Review payload limits.** A single post is limited to: + - **100 blocks** in the `props.mm_blocks` array + - **32 levels** of nesting depth across nested block structures + - **16,000 characters** total across all text in the payload, including text blocks and button labels +2. **Reduce payload size.** Split long content across multiple posts, shorten labels, flatten deeply nested structures, or remove optional blocks. +3. **Validate after content changes.** If truncation appears only for certain posts or started after an integration update, compare the payload against these limits. + +## Legacy message attachments + +Older integrations that use [message attachments](/developers/integrate/reference/message-attachments) are translated into the Mattermost Blocks UI at render time. Button and menu behavior should match native Mattermost Blocks posts. If an attachment-based post behaves differently from a native Mattermost Blocks post, compare the attachment `actions` array and callback URLs against an equivalent `mm_blocks` payload. + +## Get more help + +- **Payload format and actions:** See the [Mattermost Blocks reference](/developers/integrate/reference/mm-blocks) for block schema, action types, and migration guidance. +- **Interactive message errors:** See [interactive messages](/developers/integrate/plugins/interactive-messages) for post-action responses, error handling, and legacy attachment actions. +- **Mobile deployment issues:** See [mobile deployment troubleshooting](https://docs.mattermost.com/deploy/mobile-troubleshoot.html) for connectivity, push notification, and app install problems unrelated to Mattermost Blocks content. + +If you continue to experience issues, visit the [Mattermost Troubleshooting forum](https://forum.mattermost.com/c/trouble-shoot/16) or contact your system administrator. diff --git a/docs/develop/integrate/slash-commands/custom/index.md b/docs/develop/integrate/slash-commands/custom/index.md index f8144bfbf511..5ef4f6ca8f7c 100644 --- a/docs/develop/integrate/slash-commands/custom/index.md +++ b/docs/develop/integrate/slash-commands/custom/index.md @@ -1,5 +1,6 @@ --- -title: "Custom commands" +title: "Custom slash commands" +sidebar_label: "Custom commands" sidebar_position: 20 --- Suppose you want to write an external application that is able to check the weather for certain cities. By creating a custom slash command and setting up the application to handle the HTTP `POST` or `GET` from the command, you can let your users check the weather in their city for the week using your command, say `/weather toronto week`. @@ -59,7 +60,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](/developers/integrate/plugins). Messages with advanced formatting can be created by including an [attachment array](/developers/integrate/reference/message-attachments) and [interactive message buttons](/developers/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](/developers/integrate/plugins). Messages with advanced formatting can be created by including [Mattermost Blocks](/developers/integrate/reference/mm-blocks), an [attachment array](/developers/integrate/reference/message-attachments), or [interactive message buttons](/developers/integrate/plugins/interactive-messages) in the response payload. Our external weather application could respond with a JSON payload like so: @@ -102,7 +103,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](/developers/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 [REST API](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 [REST API](https://api.mattermost.com).
The following keys are reserved and ignored if supplied: `from_webhook`, `from_bot`, `from_oauth_app`, `from_plugin`, `force_notification`, `silent_notification`, `override_username`, `override_icon_url`, `override_icon_emoji`, `webhook_display_name`, `card`, 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 [Mattermost Blocks](/developers/integrate/reference/mm-blocks) interactive content. | No | An response payload using several parameters could look like this: diff --git a/docs/develop/integrate/webhooks/incoming/index.md b/docs/develop/integrate/webhooks/incoming/index.md index eefaec511182..5c288948eb6c 100644 --- a/docs/develop/integrate/webhooks/incoming/index.md +++ b/docs/develop/integrate/webhooks/incoming/index.md @@ -77,8 +77,9 @@ 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 [Enable integrations to override profile picture icons](https://docs.mattermost.com/configure/configuration-settings.html#enable-integrations-to-override-profile-picture-icons) configuration setting must be enabled for the override to take effect.. | No | | `attachments` | [Message attachments](/developers/integrate/reference/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 | -| `priority` | Set the priority of the message. See [Message Priority](/integrate/reference/message-priority/) | 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 and ignored if supplied: `from_webhook`, `from_bot`, `from_oauth_app`, `from_plugin`, `force_notification`, `silent_notification` (use the top-level `silent` field instead), `override_username`, `override_icon_url`, `override_icon_emoji`, `webhook_display_name`, `card`, and `attachments`.
Use `props.mm_blocks` and `props.mm_blocks_actions` for [Mattermost Blocks](/developers/integrate/reference/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](/developers/integrate/reference/message-priority) | No | +| `silent` | When `true`, the post is delivered silently: visible in the channel but produces no desktop/push/email notifications, no unread or mention count increments, and no "New Messages" line. Use for routine bot status updates or audit-trail posts. Mutually exclusive with `priority.persistent_notifications`. Defaults to `false`. | No | An example request using more parameters would look like this: diff --git a/docs/develop/integrate/webhooks/outgoing/index.md b/docs/develop/integrate/webhooks/outgoing/index.md index 5f452417bc1d..c2787c9af100 100644 --- a/docs/develop/integrate/webhooks/outgoing/index.md +++ b/docs/develop/integrate/webhooks/outgoing/index.md @@ -75,11 +75,9 @@ If you leave the channel field blank, the webhook will respond to trigger words "} ``` -```text which would render in Mattermost as: -![Test results for Server, Web Client and iOS client](/integrate/faq/images/webhooksTable.png) -``` +![Test results for Server, Web Client and iOS client](/images/webhooksTable.png) You're all set! @@ -96,8 +94,8 @@ 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 [Enable integrations to override profile picture icons](https://docs.mattermost.com/configure/configuration-settings.html#enable-integrations-to-override-profile-picture-icons) configuration setting must be enabled for the icon override to take effect. | No | | `attachments` | [Message attachments](/developers/integrate/reference/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 | -| `priority` | Set the priority of the message. See [Message Priority](/integrate/reference/message-priority/) | 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 [Mattermost Blocks](/developers/integrate/reference/mm-blocks) interactive content. | No | +| `priority` | Set the priority of the message. See [Message Priority](/developers/integrate/reference/message-priority) | No | An example response using more parameters would look like this: @@ -125,4 +123,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](/developers/integrate/reference/message-attachments) and [interactive message buttons](/developers/integrate/plugins/interactive-messages) in the JSON payload. +Messages with advanced formatting can be created by including [Mattermost Blocks](/developers/integrate/reference/mm-blocks), an [attachment array](/developers/integrate/reference/message-attachments), or [interactive message buttons](/developers/integrate/plugins/interactive-messages) in the JSON payload.