Skip to content

Messages & Blocks

Relay messages are envelopes with ordered content blocks. Durable transcript content lives in content. Typing, receipts, reactions, and live status move as events around the message instead of fake transcript rows.

{
"schema": "relay.message.v1",
"id": "msg_01JZ7S8D9R0A",
"conversationId": "conv_trip",
"sender": {
"kind": "agent",
"id": "agent_travel",
"displayName": "Travel"
},
"role": "agent",
"direction": "inbound",
"createdAt": "2026-07-06T16:20:00Z",
"revision": 1,
"status": "sent",
"fallbackText": "I found two flight options and need your approval to book.",
"content": [
{
"id": "blk_summary",
"type": "text",
"text": "I found two flight options under your target price."
},
{
"id": "blk_options",
"type": "actions",
"layout": "quick_replies",
"actions": [
{
"actionId": "act_show_options",
"label": "Show options",
"style": "primary",
"behavior": { "type": "postback", "value": "show_flight_options" }
}
]
}
]
}
  • content is an ordered array.
  • Every block has a stable id.
  • Every message has fallbackText for notifications, search, accessibility, and older clients.
  • Button actions use IDs, not labels.
  • Unsupported blocks downgrade to fallback text.
  • V1 blocks render as native SwiftUI primitives, not webviews.
{
"id": "blk_text_1",
"type": "text",
"text": "Dinner is booked for 7:30 PM.",
"entities": [
{
"type": "phone",
"range": { "start": 0, "length": 0 },
"value": "+12125550123"
}
]
}
{
"id": "blk_image_1",
"type": "image",
"asset": {
"url": "https://cdn.relay.app/attachments/img_123.png",
"mimeType": "image/png",
"width": 1600,
"height": 1200,
"sizeBytes": 482102,
"thumbnailUrl": "https://cdn.relay.app/attachments/img_123_thumb.jpg"
},
"altText": "A hotel room with a desk facing the window.",
"caption": "Room option from the hotel listing.",
"presentation": "inline"
}
{
"id": "blk_file_1",
"type": "file",
"file": {
"id": "file_invoice_123",
"name": "Invoice-1842.pdf",
"mimeType": "application/pdf",
"sizeBytes": 248120,
"icon": "pdf",
"downloadUrl": "https://cdn.relay.app/files/invoice-1842.pdf",
"checksum": "sha256:7a7d..."
},
"preview": {
"type": "pdf",
"thumbnail": {
"url": "https://cdn.relay.app/files/invoice-1842-page1.jpg",
"mimeType": "image/jpeg",
"width": 612,
"height": 792
},
"pageCount": 2
},
"actions": [
{
"actionId": "act_download_invoice",
"label": "Download",
"behavior": {
"type": "download_file",
"fileId": "file_invoice_123",
"url": "https://cdn.relay.app/files/invoice-1842.pdf"
}
}
]
}
{
"id": "blk_link_1",
"type": "link_preview",
"url": "https://example.com/listing/42",
"canonicalUrl": "https://example.com/listing/42",
"title": "Modern apartment near Union Square",
"description": "Two bedrooms, in-unit laundry, available August 1.",
"siteName": "Example Homes",
"faviconUrl": "https://example.com/favicon.ico",
"image": {
"url": "https://example.com/listing/42/cover.jpg",
"mimeType": "image/jpeg",
"width": 1200,
"height": 630
}
}
{
"id": "blk_card_1",
"type": "card",
"title": "Delta 2481",
"subtitle": "Detroit to New York, 7:40 PM",
"body": "Refundable main cabin fare. Seat 14A is available.",
"image": {
"url": "https://cdn.relay.app/cards/delta-2481.png",
"mimeType": "image/png",
"width": 1200,
"height": 630
},
"badges": [
{ "text": "Refundable", "tone": "success" },
{ "text": "Under budget", "tone": "info" }
],
"fields": [
{ "label": "Total", "value": "$412.18" },
{ "label": "Arrives", "value": "10:15 PM" }
],
"actions": [
{
"actionId": "act_view_flight",
"label": "View",
"behavior": {
"type": "open_url",
"url": "https://relay.app/flights/quote_123"
}
}
]
}

Buttons are the actions block. Use layout: "quick_replies" for iMessage-style reply chips and layout: "button_row" for command buttons.

{
"id": "blk_quick_replies_1",
"type": "actions",
"layout": "quick_replies",
"actions": [
{
"actionId": "act_yes",
"label": "Yes",
"style": "primary",
"behavior": { "type": "send_text", "text": "Yes" }
},
{
"actionId": "act_no",
"label": "No",
"behavior": { "type": "send_text", "text": "No" }
},
{
"actionId": "act_more",
"label": "Show more",
"behavior": {
"type": "postback",
"value": "show_more_options",
"data": { "source": "flight_search" }
}
}
]
}
{
"id": "blk_call_1",
"type": "call_event",
"callId": "call_abc123",
"callType": "voice",
"event": "ended",
"startedAt": "2026-07-06T16:00:00Z",
"endedAt": "2026-07-06T16:07:34Z",
"durationMs": 454000,
"participants": [
{ "kind": "user", "id": "user_advait", "displayName": "Advait" },
{ "kind": "agent", "id": "agent_scheduler", "displayName": "Scheduler" }
],
"fallbackText": "Voice call ended after 7 minutes."
}

When a client cannot render a block type, Relay renders block.fallbackText when present. Otherwise it generates a fallback from block fields:

Block Generated fallback
image Image: {caption or altText}
file {name}, {formatted size}
link_preview {title} - {url}
card {title}: {subtitle}
actions Message fallback plus supported actions
call_event {Voice or Video} call {event}