Skip to main content

Message Blocks

A message can carry plain text (body), structured rich content blocks (blocks), or both. Blocks let bots and agents send buttons, cards, carousels, images, files and translations rather than only flat text.

blocks is an array of a discriminated union keyed by the type field. Every block object declares its own type; the remaining fields depend on that type. The machine-readable contract is the MessageBlock schema in apps/api/openapi/openapi.yaml (and its generated TypeScript union in @smaibot/ts-contracts); this page is the narrative reference.

Sending blocks​

Blocks are sent through the same endpoint as plain messages:

POST /api/conversations/{conversationId}/messages
{
"blocks": [
{ "type": "text", "text": "Here are your options:" },
{
"type": "buttons",
"buttons": [
{ "title": "Open docs", "url": "https://example.com/docs" },
{ "title": "Talk to sales", "payload": "SALES" }
]
}
]
}

A message must include a non-empty body, a non-empty blocks array, or both — sending neither is a 422. The sender identity is always derived from the authenticated agent and is never read from the request body.

Validation​

Each block is validated as a discriminated union in two steps:

  1. Discriminator. type must be one of the known block types. An unknown type fails with a validation error on blocks.{index}.type.
  2. Payload. The fields of the block are validated against the rules for that specific type. For example a text block requires a non-empty text, and an image block requires a valid url. A bad payload fails with an error on the offending field, e.g. blocks.0.text or blocks.2.buttons.0.title.

Validation failures use the standard 422 ValidationError envelope (see Errors), so the client can map each error back to the exact block and field.

Block types​

All blocks share the type discriminator. The payload fields are listed below.

text​

A plain-text block.

FieldTypeRequiredNotes
textstringyesNon-empty content.
{ "type": "text", "text": "Hello there" }

buttons​

A row of actionable buttons, optionally introduced by a text prompt.

FieldTypeRequiredNotes
textstringnoPrompt shown above the row.
buttonsButton[]yesAt least one button.

A Button has a title plus either a url (link) or a payload (postback sent back to the bot):

FieldTypeRequiredNotes
titlestringyesVisible label.
urlstringnoValid URL for link buttons.
payloadstringnoPostback value for the bot.
{
"type": "buttons",
"text": "Pick one",
"buttons": [
{ "title": "Docs", "url": "https://example.com" },
{ "title": "More", "payload": "MORE" }
]
}

card​

A card with a title and optional subtitle, image and buttons.

FieldTypeRequiredNotes
titlestringyesNon-empty.
subtitlestringno
image_urlstringnoValid URL.
buttonsButton[]noSame shape as above.
{
"type": "card",
"title": "Pro plan",
"subtitle": "Everything you need",
"image_url": "https://example.com/pro.png",
"buttons": [{ "title": "Upgrade", "payload": "UPGRADE_PRO" }]
}

slider​

A horizontally-scrollable carousel of card blocks.

FieldTypeRequiredNotes
cardsCard[]yesAt least one card.
{
"type": "slider",
"cards": [
{ "type": "card", "title": "Starter" },
{ "type": "card", "title": "Pro", "subtitle": "Most popular" }
]
}

image​

An inline image referenced by URL.

FieldTypeRequiredNotes
urlstringyesValid URL.
altstringnoAlt text.
widthintegernoPixels.
heightintegernoPixels.
{ "type": "image", "url": "https://example.com/p.png", "alt": "Screenshot" }

file​

A downloadable file reference, rendered as an attachment chip.

FieldTypeRequiredNotes
urlstringyesValid URL.
filenamestringyesNon-empty.
mimestringnoMIME type.
sizeintegernoBytes.
{
"type": "file",
"url": "https://example.com/report.pdf",
"filename": "report.pdf",
"mime": "application/pdf",
"size": 20480
}

translation​

A machine translation carrying both the original and translated text.

FieldTypeRequiredNotes
originalstringyesSource text.
translatedstringyesTranslated text.
localestringyesTarget locale.
{
"type": "translation",
"original": "Merhaba",
"translated": "Hello",
"locale": "en"
}

How blocks are returned​

The Message resource returns blocks in the same structured shape that was sent, normalized through the typed block model — so the round-trip is predictable and unknown fields are dropped. blocks is null when a message has none.

Webhooks​

Outbound message webhooks carry the same blocks array in their payload. A consumer should switch on type and treat any unrecognized type defensively (new block types may be added over time).