diff --git a/site/content/integrate/plugins/interactive-messages/_index.md b/site/content/integrate/plugins/interactive-messages/_index.md
index 182f968b0d..0d665fadf3 100644
--- a/site/content/integrate/plugins/interactive-messages/_index.md
+++ b/site/content/integrate/plugins/interactive-messages/_index.md
@@ -21,6 +21,12 @@ To try it out, you can use this {{< newtabref href="https://github.com/matterpol

+## 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.
+
+See [markdown action buttons]({{< ref "/integrate/reference/markdown-actions" >}}) for the full schema, limits, and end-to-end flow.
+
## Message buttons
Add message buttons as `actions` in your integration {{< newtabref href="https://docs.mattermost.com/developer/message-attachments.html" title="message attachments" >}}.
diff --git a/site/content/integrate/reference/markdown-actions/_index.md b/site/content/integrate/reference/markdown-actions/_index.md
new file mode 100644
index 0000000000..b81631c727
--- /dev/null
+++ b/site/content/integrate/reference/markdown-actions/_index.md
@@ -0,0 +1,227 @@
+---
+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
+---
+
+{{}}
+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.
+{{}}
+
+Use markdown action buttons to add inline, in-text affordances to a post — without using a message attachment. They're useful when:
+
+- A short message reads naturally with an "Approve" or "Reject" inline link.
+- 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" >}}).
+
+## How it works
+
+A markdown action has two parts:
+
+1. A markdown link in the post body using the `mmaction://` scheme, where the link host is the action ID:
+
+ ```text
+ [Approve](mmaction://approve?ticket=ISS-101)
+ ```
+
+2. A matching entry in the post's `props.mm_blocks_actions` registry that tells the server what to do when the link is clicked:
+
+ ```json
+ {
+ "mm_blocks_actions": {
+ "approve": {
+ "type": "external",
+ "url": "https://integration.example.com/hook/approve",
+ "context": {"project": "Demo Project"}
+ }
+ }
+ }
+ ```
+
+The client renders the link as a button. Clicking it dispatches a request to the Mattermost server, which forwards the call to the integration's `url` along with merged query parameters and any server-side context.
+
+## Example post payload
+
+The following payload posts a message with two markdown action buttons. The body markdown references action IDs defined in `mm_blocks_actions`.
+
+```json
+{
+ "channel_id": "qmd5oqtwoibz8cuzxzg5ekshgr",
+ "message": "Ticket ISS-101 needs review: [Approve](mmaction://approve?ticket=ISS-101) [Reject](mmaction://reject?ticket=ISS-101)",
+ "props": {
+ "mm_blocks_actions": {
+ "approve": {
+ "type": "external",
+ "url": "https://integration.example.com/hook/approve",
+ "context": {"project": "Demo Project"}
+ },
+ "reject": {
+ "type": "external",
+ "url": "https://integration.example.com/hook/reject",
+ "context": {"project": "Demo Project"}
+ }
+ }
+ }
+}
+```
+
+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" >}}), or from a [plugin]({{< ref "/integrate/plugins/components/server" >}}).
+
+### Submit using the REST API
+
+```bash
+curl -X POST $MM_URL/api/v4/posts \
+ -H "Authorization: Bearer $BOT_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "channel_id": "'$CHANNEL_ID'",
+ "message": "Ticket ISS-101 needs review: [Approve](mmaction://approve?ticket=ISS-101) [Reject](mmaction://reject?ticket=ISS-101)",
+ "props": {
+ "mm_blocks_actions": {
+ "approve": {
+ "type": "external",
+ "url": "https://integration.example.com/hook/approve",
+ "context": {"project": "Demo Project"}
+ },
+ "reject": {
+ "type": "external",
+ "url": "https://integration.example.com/hook/reject",
+ "context": {"project": "Demo Project"}
+ }
+ }
+ }
+ }'
+```
+
+### Submit from a plugin
+
+A server-side plugin creates posts with markdown actions through the existing `API.CreatePost` / `API.UpdatePost` interfaces. The action `url` typically points at the plugin's own HTTP handler.
+
+```go
+post := &model.Post{
+ ChannelId: channelID,
+ UserId: p.botID,
+ Message: "Ticket ISS-101 needs review: " +
+ "[Approve](mmaction://approve?ticket=ISS-101) " +
+ "[Reject](mmaction://reject?ticket=ISS-101)",
+ Props: model.StringInterface{
+ "mm_blocks_actions": map[string]any{
+ "approve": map[string]any{
+ "type": "external",
+ "url": fmt.Sprintf("/plugins/%s/inline_action/approve", manifest.Id),
+ "context": map[string]any{"project": "Demo Project"},
+ },
+ "reject": map[string]any{
+ "type": "external",
+ "url": fmt.Sprintf("/plugins/%s/inline_action/reject", manifest.Id),
+ "context": map[string]any{"project": "Demo Project"},
+ },
+ },
+ },
+}
+_, 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.
+
+## Link syntax
+
+```text
+[