Rich messages
Beyond plain text, Lane.Chat can send rich messages: structured bubbles that the widget renders natively and that degrade to sensible fallbacks on channels without rich support. Rich messages are authored in Flows (the send_rich_message action).
There are six rich types:
| Type | Renders as |
|---|---|
markdown | Formatted markdown text |
card | A card with title, description, image, and buttons |
buttons | A group of tappable buttons |
carousel | A horizontally scrollable set of cards |
form | An inline form the visitor fills in and submits |
link | A link preview with title and description |
The wire envelope
Every rich message is delivered as a normal chat message with three coordinated fields:
message_type— the rich type (card,carousel, ...).content— the fallback text (shown in list previews and on channels without rich rendering).metadata—{ schema_version: 1, type, fallback_text, payload }, wherepayloadholds the per-type shape.
{
"type": "message",
"message_type": "card",
"content": "Pro plan — $29/mo",
"metadata": {
"schema_version": 1,
"type": "card",
"fallback_text": "Pro plan — $29/mo",
"payload": {
"title": "Pro plan",
"description": "Everything in Starter, plus priority support.",
"image_url": "https://cdn.example.com/pro.png",
"buttons": [
{ "text": "Choose Pro", "action": { "type": "postback", "value": "select_pro" } },
{ "text": "Learn more", "action": { "type": "url", "url": "https://example.com/pricing" } }
]
}
}
}Payload shapes
Field names below are the ones the widget renderer reads. Human-visible text fields also accept a {lang: text} map for per-language copy (see Template variables).
card
{
"title": "Pro plan",
"description": "Priority support and higher limits.",
"image_url": "https://cdn.example.com/pro.png",
"buttons": [
{ "text": "Choose Pro", "action": { "type": "postback", "value": "select_pro" } },
{ "text": "Pricing", "action": { "type": "url", "url": "https://example.com/pricing" } }
]
}buttons
Every button carries an action object. type is one of postback (fires a button_click flow event carrying value), url (opens url), phone, submit_form, or next_step. layout is auto (default), 1col, 2col, or 3col.
{
"text": "How can we help?",
"layout": "1col",
"buttons": [
{ "text": "Sales", "action": { "type": "postback", "value": "sales" } },
{ "text": "Support", "action": { "type": "postback", "value": "support" } }
]
}carousel
{
"cards": [
{ "title": "Starter", "description": "$9/mo", "buttons": [ { "text": "Pick", "action": { "type": "postback", "value": "starter" } } ] },
{ "title": "Pro", "description": "$29/mo", "buttons": [ { "text": "Pick", "action": { "type": "postback", "value": "pro" } } ] }
]
}form
{
"title": "Book a demo",
"fields": [
{ "id": "email", "label": "Work email", "type": "email", "required": true },
{ "id": "size", "label": "Team size", "type": "select", "options": [
{ "label": "1–10", "value": "s" },
{ "label": "11–50", "value": "m" }
] }
],
"submit_label": "Request demo",
"submit_action": { "type": "submit_form", "flow_step_id": "..." }
}See Forms for how submissions are ingested.
link
{
"url": "https://example.com/changelog",
"title": "What's new",
"description": "This month's product updates."
}markdown
{ "markdown": "**Heads up:** maintenance tonight 22:00–23:00 UTC." }Button clicks and submissions
- Button postbacks travel back over the WebSocket
messageframe inmetadata.button_click({ button_id, button_value, button_text }) and trigger thebutton_clickevent in your Flow. - Form submissions are posted to
POST /v1/forms/submit— see Forms.
Cross-channel degradation
On channels without native rich rendering, Lane.Chat degrades automatically: Telegram and Slack translate buttons/cards to their native interactive formats, and any channel without an equivalent falls back to content (the fallback text). If a rich payload is malformed but has usable fallback text, the message degrades to plain text rather than failing.