::: <name> and ends with :::. The model writes the block as plain text; each channel then turns it into what that channel can show: cards in the web widget, an interactive list on WhatsApp, an Adaptive Card in Teams, an attachment in email. Components exist so one agent can answer every channel from one persona and one set of skills without your code knowing where a reply is displayed.
From reply text to rendered message
The reply is ordinary Markdown with blocks embedded in it:Channels.send to WhatsApp, Messenger, Instagram, and MessageBird. The web widget parses blocks in the browser and renders them while the reply streams. A channel that can’t render a block drops it or sends it as raw text; the matrix under Rendering by channel says which.
Taught blocks and instructed blocks
On every text channel the platform prompt teaches the model the syntax forlist-item, horizontal-list-item, actions, links, images, and payment; on every channel except web it also teaches flow (WhatsApp only) and reaction. The model decides when to use them, so for predictable output say when and how in the persona or a skill’s context. Two blocks are never taught: documents and navigate appear only when your instructions include their syntax.
Two cases change the rules. On the web channel (Lua Desktop and the admin dashboard’s chat) the model is told to prefer plain Markdown, to use cards only for options the end user picks from, and never to emit flow; the web widget is not on that channel (it reports pop) and gets the same rulebook as the messaging channels. A client that declares the plain-markdown capability gets plain Markdown and no blocks at all: lua chat declares it, and the HTTP chat API accepts it as clientCapabilities: ["plain-markdown"]. Voice turns get no blocks either; the same data goes through tools that show it on screen.
Rendered previews before release
Nothing shows a sandbox version with its blocks rendered.lua chat declares plain-markdown in both environments, so it prints Markdown and never a card, and lua chat --agent-version runs against production. Two clients render blocks, and both talk to the agent’s production version: the web widget, and the chat panel in the admin dashboard’s agent workspace, which is on the web channel and so gets the plain-Markdown-first rulebook; a block your instructions ask for still renders there. To see a reply on a real messaging app, test on Lua’s channels over WhatsApp, Messenger, Instagram, or Slack, which also deliver production. The HTTP chat API returns blocks as raw markers for your own client to render.
Rendering by channel
Three terms recur in the matrix. The messaging channels are WhatsApp, Messenger, Instagram, Slack, Teams, RCS, MessageBird, and iMessage; they share one extractor. Email has two forms: a generated inbox is the address Lua issues, and your own address is a mailbox you connected. A block a channel can’t render is either dropped, meaning the block is removed and the surrounding text is still sent, or reaches the reader as raw text, meaning the::: lines are sent as written.
SMS and Front have no extractor: a
reaction block becomes its emoji, and every other block reaches the reader as raw text, so keep instructions for those channels to plain text. Your own address renders blocks into HTML; a generated inbox runs the messaging channels’ extraction, sends images and documents as attachments, and drops the rest.
Channel-scoped instructions
A block that works on one channel can leak on another, so scope the instruction: “On WhatsApp, send the verification flow …”, or in a tool readLua.request.channel and return the block only where it renders (see Lua). When in doubt, list-item, actions, images, and payment travel furthest; every other block has a channel list to check first.
Limits
Next steps
list-item
The component most replies start with.
Write skill context
Where component instructions belong.
Send proactive messages
Blocks in messages the agent starts.
Channels
Every channel and its inbound and outbound names.

