Rich Output Protocol: Media, Embeds, Audio, and Replies

This page explains the rich output protocol for structured media attachments, audio hints, embeds, and reply metadata. It is essential for developers building assistant responses that require delivery and rendering instructions.

Read this when

  • Changing assistant output rendering in the Control UI
  • Debugging `[embed ...]`, structured media, reply, or audio presentation directives

Assistant responses carry delivery and rendering instructions through a small set of dedicated channels:

  • Structured mediaUrl / mediaUrls fields handle attachment delivery.
  • [[audio_as_voice]] provides audio presentation hints.
  • Reply metadata travels via [[reply_to_current]] / [[reply_to:<id>]].
  • [embed ...] drives rich rendering for the Control UI.

Structured media fields and [[...]] tags serve as delivery metadata. [embed ...] is the distinct web-only rich-render path, not an alias for media.

Media attachments

Remote attachments require public https: URLs. Attachment directives reject http:, loopback, link-local, private, and internal hostnames; server-side media fetchers add their own network protections on top.

Local attachments accept absolute paths, workspace-relative paths, or home-relative ~/ paths. They still pass the agent file-read policy and media type checks before delivery.

Warning

Do not emit text commands for attachments from tools, plugins, streaming blocks, browser output, or message actions. Use structured media fields instead:

{ "message": "Here is your image.", "mediaUrl": "/workspace/image.png" }

Legacy final-reply text may still be normalized for compatibility, but this is not a general plugin/tool protocol.

Legacy MEDIA: lines

Legacy final assistant replies can still attach local media with a plain standalone MEDIA: line. The parser only recognizes lines whose trimmed text starts with MEDIA: outside Markdown wrappers and code fences.

Valid legacy final reply:

Here is the generated image.

MEDIA:/workspace/image.png

These remain ordinary text and do not attach media:

**MEDIA:/workspace/image.png**
`MEDIA:/workspace/image.png`
Here is your image: MEDIA:/workspace/image.png

Prefer structured mediaUrl / mediaUrls fields for tools, plugins, browser output, streaming blocks, and message actions.

Plain Markdown image syntax stays text by default. Channels that intentionally map Markdown image replies to media attachments opt in at their outbound adapter; Telegram does this so ![alt](url) can still become a media reply.

When block streaming is enabled, media must ride on structured payload fields. If the same media URL appears in a streamed block and again in the final assistant payload, OpenClaw delivers it once and strips the duplicate from the final payload.

[embed ...]

[embed ...] is the only agent-facing rich-render syntax for the Control UI. Self-closing example:

[embed ref="cv_123" title="Status" /]

Rules:

  • [view ...] is no longer valid for new output.
  • Embed shortcodes render only in the assistant message surface.
  • Only URL-backed embeds render; use ref="..." or url="...".
  • Block-form inline HTML embed shortcodes do not render.
  • The web UI strips the shortcode from visible text and renders the embed inline.

Stored rendering shape

The normalized/stored assistant content block is a structured canvas item:

{
  "type": "canvas",
  "preview": {
    "kind": "canvas",
    "surface": "assistant_message",
    "render": "url",
    "viewId": "cv_123",
    "url": "/__openclaw__/canvas/documents/cv_123/index.html",
    "title": "Status",
    "preferredHeight": 320
  }
}

present_view is not recognized; stored/rendered rich blocks always use this canvas shape.

474 words · updated Aug 4, 2026