Skip to content
Linq Copy agent prompt

Destination Messages

Send a message into a chat. Content is the sealed parts union; delivery is asynchronous and reported on the chat’s event sequence.

Send a message
POST/v1/messages
ModelsExpand Collapse
AcceptedMessage object { id, chat_id, created_at, 7 more }

An accepted send: the message, plus how its sending number was chosen.

id: string

This API’s message id. Once outbound reactions are available, put this value in their message_id. Mark-as-read targets the latest unread inbound message in a chat, so it takes the chat id.

chat_id: string

The chat this message belongs to.

created_at: string
formatdate-time
direction: "inbound" or "outbound"

inbound (from the customer) or outbound (sent by you).

One of the following:
"inbound"
"outbound"
parts: array of object { body, type, preview_url } or object { kind, type, caption, 4 more } or object { language, name, type, 3 more } or 14 more

The message content — sendable parts, plus the inbound-only types on inbound messages. Every message carries at least one part.

One of the following:
Text object { body, type, preview_url }

A plain text message.

body: string

The message text. URLs render as tappable links.

maxLength4096
type: "text"
preview_url: optional boolean

Render a preview card for the first URL in body. Defaults to false.

Media object { kind, type, caption, 4 more }

A media message — image, video, audio, document, or sticker. The two identifiers are distinct namespaces: inbound media carries the channel’s transient media_id; outbound media carries our durable upload_ref from POST /v1/media.

kind: "image" or "video" or "audio" or 2 more

Which kind of media this is. Captions apply to image, video and document only; filename applies to document only.

One of the following:
"image"
"video"
"audio"
"document"
"sticker"
type: "media"
caption: optional string

Caption rendered with the media. Image, video and document only — a caption on an audio or sticker part is rejected, never dropped.

maxLength1024
filename: optional string

Display filename. Documents only — a filename on any other kind is rejected, never dropped.

maxLength240
media_id: optional string

Inbound only: the channel’s transient media identifier. It expires after seven days and is never an outbound address; an outbound value is rejected with a pointer to media_id and upload_ref as the remedy.

maxLength256
upload_ref: optional string

Outbound only: our durable handle returned by POST /v1/media. Handles visibly begin upload. and are reusable within the account and test/live partition that created them.

maxLength256
url: optional string

Response only: stable attachment download URL requiring the account Bearer key on every request. Pending capture returns 409 with Retry-After; retained bytes expire 30 days after server receipt. Historical unbound messages omit this field. Never accepted on sends.

maxLength2048
Template object { language, name, type, 3 more }

An approved message template — the one part type sendable outside an open customer service window. name and language identify the approved template; parameters fills its named placeholders. Authentication templates use this same generic part unchanged: the channel supplies the OTP substitution, so do not invent or pass a code parameter unless the template’s published parameter schema explicitly names one. Every sendable template is registered and approved ahead of the send and publishes its own parameter schema — a send referencing an unknown template, a missing or unknown parameter, or a value that breaks the template’s rules is rejected with a 422 naming the exact field.

language: string

The template’s language-and-locale code, e.g. en_US.

maxLength32
name: string

The approved template’s name. Lowercase letters, digits and underscores only.

maxLength512
type: "template"
cards: optional array of object { card_index, kind, upload_ref, 2 more }

Per-card values for an approved media-card carousel. The approved template’s send_schema provides structural bounds and an x-rule listing the exact card order, media kind, named body values, and dynamic button slots enforced by runtime L2 validation. Omit for non-carousel templates.

card_index: number
kind: "image" or "video"
One of the following:
"image"
"video"
upload_ref: string

A durable handle returned by POST /v1/media. It is resolved within this key’s account and partition, then consumed into a transient channel media identifier only at delivery.

maxLength256
buttons: optional array of object { index, payload, type }
index: number
payload: string
maxLength256
type: "quick_reply" or "url"
One of the following:
"quick_reply"
"url"
parameters: optional unknown

Named values for this card’s body placeholders.

header: optional object { kind, upload_ref }

The uploaded media used for this approved template’s media header. Omit unless this template’s send_schema requires it.

kind: "image" or "video" or "document"
One of the following:
"image"
"video"
"document"
upload_ref: string

A durable handle returned by POST /v1/media.

maxLength256
parameters: optional unknown

Named values for the template’s placeholders. The shape is per-template: each registered template publishes its own parameter schema — hand THAT schema to your model when composing a specific template. Omit for templates with no placeholders.

InteractiveList object { body, button, sections, 3 more }

An interactive list message: body text plus a button that opens a sectioned list of selectable rows. The customer’s selection arrives as an inbound interactive_reply part with kind list_reply, carrying the chosen row’s id.

body: string

The message body.

maxLength1024
button: string

Label of the button that opens the list.

maxLength20
sections: array of object { rows, title }

The list’s sections, each holding selectable rows. At most 10 rows in total across ALL sections combined — not 10 per section.

rows: array of object { id, title, description }

This section’s rows. The 10-row limit is on the TOTAL across all sections, so a second section reduces what this one may hold.

id: string

Your identifier for the row — echoed back as the interactive_reply’s id.

maxLength200
title: string

The row’s visible title.

maxLength24
description: optional string

Secondary text under the title.

maxLength72
title: optional string

Section title. Required when the list has more than one section.

maxLength24
type: "interactive_list"
header: optional string

Header text above the body.

maxLength60
InteractiveButtons object { body, buttons, type, 3 more }

An interactive reply-buttons message: body text plus up to three tappable buttons. The customer’s tap arrives as an inbound interactive_reply part with kind button_reply, carrying the tapped button’s id.

body: string

The message body.

maxLength1024
buttons: array of object { id, title }

The tappable buttons — at most three.

id: string

Your identifier for the button — echoed back as the interactive_reply’s id.

maxLength256
title: string

The button’s visible label.

maxLength20
type: "interactive_buttons"
header: optional string

Header text above the body.

maxLength60
media_header: optional object { kind, upload_ref }

Uploaded image, video, or document above the body. Use either header or media_header, never both.

kind: "image" or "video" or "document"
One of the following:
"image"
"video"
"document"
upload_ref: string

A durable handle returned by POST /v1/media.

maxLength256
InteractiveCtaURL object { body, display_text, type, 3 more }

An interactive call-to-action message: body text plus one button that opens a URL.

body: string

The message body.

maxLength1024
display_text: string

The button’s visible label.

maxLength20
type: "interactive_cta_url"
url: string

The URL the button opens. Must be absolute, with an http or https scheme.

maxLength2000
header: optional string

Header text above the body.

maxLength60
LocationRequest object { body, type }

Asks the customer to share a location. The response arrives as a location part.

body: string

Prompt shown above the send-location button.

maxLength1024
type: "location_request"
AddressRequest object { body, country, type }

Reserved for asking an eligible customer in India to submit a structured address. Sending this part is not available until business and customer eligibility can be verified at acceptance. Address responses already arrive as address_reply parts.

body: string

Prompt shown above the address form.

maxLength1024
country: "IN"

ISO country code. Structured address requests are currently available only in India.

type: "address_request"
Contacts object { contacts, type }

One or more contact cards.

contacts: array of object { name, addresses, birthday, 4 more }

The contact cards to send. The channel permits far more; this API caps a message at five, because a message carrying hundreds of cards is a mistake rather than a use case.

name: object { formatted_name, first_name, last_name, 3 more }
formatted_name: string

The contact’s full display name.

maxLength512
first_name: optional string
maxLength256
last_name: optional string
maxLength256
middle_name: optional string
maxLength256
prefix: optional string
maxLength256
suffix: optional string
maxLength256
addresses: optional array of object { city, country, country_code, 4 more }
city: optional string
maxLength256
country: optional string
maxLength256
country_code: optional string
maxLength64
kind: optional string

A label for the address, e.g. WORK, HOME.

maxLength64
state: optional string
maxLength256
street: optional string
maxLength256
zip: optional string
maxLength256
birthday: optional string

The contact’s birthday as YYYY-MM-DD.

emails: optional array of object { email, kind }
email: string
maxLength256
kind: optional string

A label for the address, e.g. WORK, HOME.

maxLength64
org: optional object { company, department, title }
company: optional string
maxLength256
department: optional string
maxLength256
title: optional string
maxLength256
phones: optional array of object { phone, kind, messaging_id }
phone: string

The phone number, ideally in +E.164 form.

maxLength256
kind: optional string

A label for the number, e.g. CELL, MAIN, WORK, HOME.

maxLength64
messaging_id: optional string

The contact’s messaging-account identifier on this channel, when known — makes the card openable in the messaging app.

maxLength256
urls: optional array of object { url, kind }
url: string
maxLength256
kind: optional string

A label for the URL, e.g. WORK, HOME.

maxLength64
type: "contacts"
Location object { latitude, longitude, type, 2 more }

A location pin.

latitude: number

Latitude in decimal degrees.

maximum90
minimum-90
longitude: number

Longitude in decimal degrees.

maximum180
minimum-180
type: "location"
address: optional string

The place’s address, shown under the name.

maxLength1000
name: optional string

The place’s name, shown on the pin.

maxLength1000
Reaction object { emoji, type, channel_message_id, message_id }

An emoji reaction to an earlier message in the chat. The two identifiers are distinct namespaces: inbound reactions carry the channel’s opaque channel_message_id; outbound reactions will carry this API’s message_id once sending them becomes available.

emoji: string

A single emoji. Send an empty string to remove a previous reaction to the same message.

maxLength32
type: "reaction"
channel_message_id: optional string

Inbound only: the channel’s opaque identifier for the message being reacted to. Match it literally to Message.channel_message_id in the chat transcript; never parse it or compare it with Message.id.

maxLength128
message_id: optional string

Outbound only: this API’s Message.id for the message being reacted to. Outbound reactions are not available yet; when they land, the API resolves this identifier into the channel namespace before sending.

maxLength256
InteractiveReply object { id, kind, title, 2 more }

Inbound only — never sendable. The customer’s selection from an interactive_list (kind list_reply) or interactive_buttons (kind button_reply) message, carrying the id you assigned to the chosen row or button.

id: string

The id you assigned to the chosen row or button.

maxLength200
kind: "list_reply" or "button_reply"

Which interactive message kind was answered.

One of the following:
"list_reply"
"button_reply"
title: string

The chosen row’s or button’s visible title.

maxLength24
type: "interactive_reply"
description: optional string

The chosen list row’s secondary text, when it had one.

maxLength72
AddressReply object { type, values, saved_address_id }

Inbound only: the structured values submitted in response to an address request.

type: "address_reply"
values: object { address, building_name, city, 8 more }
address: optional string
maxLength1024
building_name: optional string
maxLength1024
city: optional string
maxLength1024
floor_number: optional string
maxLength1024
house_number: optional string
maxLength1024
landmark_area: optional string
maxLength1024
name: optional string
maxLength1024
phone_number: optional string
maxLength64
pin_code: optional string
maxLength6
state: optional string
maxLength1024
tower_number: optional string
maxLength1024
saved_address_id: optional string

Identifier of the selected saved address, when one was selected.

maxLength256
Order object { catalog_id, items, type, text }

Inbound only — never sendable. An order the customer placed from a product catalog.

catalog_id: string

The catalog the ordered items belong to.

maxLength256
items: array of object { currency, price, product_retailer_id, quantity }

The ordered items.

currency: string

ISO 4217 currency code for price.

maxLength8
price: number

The per-item price at order time.

minimum0
product_retailer_id: string

Your identifier for the product, as registered in the catalog.

maxLength256
quantity: number
minimum0
type: "order"
text: optional string

Free text the customer attached to the order, when any.

maxLength4096
Referral object { type, body, click_id, 7 more }

Inbound only — never sendable. The ad or post context a customer’s first message arrived from (for example an ad whose call to action opens a chat). A referral also opens a free-entry-point customer window — see the chat’s customer_window.

type: "referral"
body: optional string

The ad’s body text at click time.

maxLength4096
click_id: optional string

The click identifier assigned by the ad platform, for attribution.

maxLength256
headline: optional string

The ad’s headline at click time.

maxLength4096
image_url: optional string

URL of the ad’s image creative, when media_kind is image.

maxLength2048
media_kind: optional string

The ad creative’s media kind, e.g. image or video.

maxLength64
source_id: optional string

The ad or post id.

maxLength256
source_kind: optional string

What the source was, e.g. ad or post.

maxLength64
source_url: optional string

The URL of the ad or post the customer came from.

maxLength2048
video_url: optional string

URL of the ad’s video creative, when media_kind is video.

maxLength2048
System object { body, type, event }

Inbound only — never sendable. A system event in the chat, such as the customer changing their number. New event kinds appear over time; body is always present and human-readable.

body: string

Human-readable description of the system event.

maxLength4096
type: "system"
event: optional string

The system event’s kind, when the channel identifies one. New kinds appear over time — treat unknown values as informational.

maxLength64
Unsupported object { raw, type, kind }

Inbound only — never sendable. A message kind this API does not yet type natively, carried as a typed passthrough: raw holds the channel payload, so a new message kind is never a black box or a silent drop. Native part types for popular kinds are added over time; this part is the compatibility guarantee in the meantime.

raw: unknown

The channel payload — any JSON value: object, array, string, number, boolean, or null. Deliberately unconstrained, because the whole point of this part is to carry a shape this API does not yet know. For rows written by API version 1.3.0 or later, object member order, number formatting and duplicated members survive storage and read-back. The canonical encoder may compact insignificant whitespace before storage. Rows written before API version 1.3.0 retain normalized JSON only: member order and number formatting may differ, and only the last duplicated member survives.

type: "unsupported"
kind: optional string

The channel’s name for the message kind, when it declares one.

maxLength64
status: string

The message’s delivery state as last reported.

Inbound messages read received: they arrived, and no delivery of ours ran. A message you sent starts accepted and moves through sent, delivered and read as the channel reports them, with failed as the definitive negative outcome.

unknown means exactly that: the send is INDETERMINATE and we decline to guess. It is NOT terminal — an indeterminate send is never re-sent, and it resolves to sent, delivered, read or failed when the channel’s own report arrives — so keep observing rather than treating it as an outcome.

States are reported asynchronously on the chat’s event sequence, and the set grows additively: treat a value you do not recognise as “no information” rather than failing on it.

Retention of this published status follows the chat transcript policy: there is no scheduled pruning during beta, but there is no fixed minimum availability guarantee. The present absence of a scheduled age-based sweep is not a promise of indefinite availability.

UNDER A sk_test_ KEY THESE ARE SIMULATED. A test-key send is reported sent and then delivered within milliseconds, every time; delivered there means a simulator accepted it, not that a device received it. Live delivery can lag by hours, can never report delivered at all, and can fail after the channel accepted the message — so do not calibrate timeouts or “delivered means it arrived” logic against a test key.

channel_message_id: optional string

The channel’s opaque identifier for this message, when the channel has assigned one. Match an inbound reaction’s channel_message_id to this field literally; never parse it or compare it with id. It is absent while an accepted outbound message has not yet received a channel identifier.

failure: optional object { code, message }

Why a failed message failed, when we have a reason worth publishing.

Present only on status failed, and not on every one of those: not every channel refusal has a reason in the published vocabulary, so a message that failed on the wire may carry no failure at all. Its absence means “no published reason”, never “no reason”.

status stays the thing to branch on. This is additive detail beside it, so an integration written before this field existed still sees a terminal failed and behaves exactly as it did.

code: string

The specific reason.

sender_deregistered — the number this message was going out from is no longer a registered sending number, and the message never left. This is terminal: it is not a pause and it does not clear, so retrying the same message cannot succeed and that conversation is over. Reach this customer by starting a new one on another of your numbers with a template, exactly as you would any customer whose 24-hour window has closed; GET /v1/chats shows which number each conversation uses. You are not billed for a message that failed this way.

quality_hold_expired — this marketing message remained held by an automatic safety control on its sending line for ten minutes and was not sent. This is terminal: retrying the same message cannot succeed on that conversation. Send marketing from another of your numbers with a template, or contact support about the sending line. You are not billed for a message that failed this way.

message: string

Human-readable, written for a person reading it. Do not branch on it.

from: optional string

The number this message left by, or arrived on, in E.164 (leading +). Every message in one conversation carries the same value — a conversation never moves to another number. Absent only if the number is no longer one of yours.

from_selection: optional object { reason }

Present only when this service chose the sending number for you — that is, when you omitted from. A send that named from omits this rather than reporting a choice nobody made.

reason: string

Why this number was used. Exactly one of:

  • reused_active_chat — this customer already had a conversation and it continues on the same number.
  • new_best_number — there was no existing conversation, so the best available number was chosen.

Like every other vocabulary here it is a plain string that may grow additively; treat a value you do not recognise as “chosen for you”.

Message object { id, chat_id, created_at, 6 more }

One message, in either direction.

id: string

This API’s message id. Once outbound reactions are available, put this value in their message_id. Mark-as-read targets the latest unread inbound message in a chat, so it takes the chat id.

chat_id: string

The chat this message belongs to.

created_at: string
formatdate-time
direction: "inbound" or "outbound"

inbound (from the customer) or outbound (sent by you).

One of the following:
"inbound"
"outbound"
parts: array of object { body, type, preview_url } or object { kind, type, caption, 4 more } or object { language, name, type, 3 more } or 14 more

The message content — sendable parts, plus the inbound-only types on inbound messages. Every message carries at least one part.

One of the following:
Text object { body, type, preview_url }

A plain text message.

body: string

The message text. URLs render as tappable links.

maxLength4096
type: "text"
preview_url: optional boolean

Render a preview card for the first URL in body. Defaults to false.

Media object { kind, type, caption, 4 more }

A media message — image, video, audio, document, or sticker. The two identifiers are distinct namespaces: inbound media carries the channel’s transient media_id; outbound media carries our durable upload_ref from POST /v1/media.

kind: "image" or "video" or "audio" or 2 more

Which kind of media this is. Captions apply to image, video and document only; filename applies to document only.

One of the following:
"image"
"video"
"audio"
"document"
"sticker"
type: "media"
caption: optional string

Caption rendered with the media. Image, video and document only — a caption on an audio or sticker part is rejected, never dropped.

maxLength1024
filename: optional string

Display filename. Documents only — a filename on any other kind is rejected, never dropped.

maxLength240
media_id: optional string

Inbound only: the channel’s transient media identifier. It expires after seven days and is never an outbound address; an outbound value is rejected with a pointer to media_id and upload_ref as the remedy.

maxLength256
upload_ref: optional string

Outbound only: our durable handle returned by POST /v1/media. Handles visibly begin upload. and are reusable within the account and test/live partition that created them.

maxLength256
url: optional string

Response only: stable attachment download URL requiring the account Bearer key on every request. Pending capture returns 409 with Retry-After; retained bytes expire 30 days after server receipt. Historical unbound messages omit this field. Never accepted on sends.

maxLength2048
Template object { language, name, type, 3 more }

An approved message template — the one part type sendable outside an open customer service window. name and language identify the approved template; parameters fills its named placeholders. Authentication templates use this same generic part unchanged: the channel supplies the OTP substitution, so do not invent or pass a code parameter unless the template’s published parameter schema explicitly names one. Every sendable template is registered and approved ahead of the send and publishes its own parameter schema — a send referencing an unknown template, a missing or unknown parameter, or a value that breaks the template’s rules is rejected with a 422 naming the exact field.

language: string

The template’s language-and-locale code, e.g. en_US.

maxLength32
name: string

The approved template’s name. Lowercase letters, digits and underscores only.

maxLength512
type: "template"
cards: optional array of object { card_index, kind, upload_ref, 2 more }

Per-card values for an approved media-card carousel. The approved template’s send_schema provides structural bounds and an x-rule listing the exact card order, media kind, named body values, and dynamic button slots enforced by runtime L2 validation. Omit for non-carousel templates.

card_index: number
kind: "image" or "video"
One of the following:
"image"
"video"
upload_ref: string

A durable handle returned by POST /v1/media. It is resolved within this key’s account and partition, then consumed into a transient channel media identifier only at delivery.

maxLength256
buttons: optional array of object { index, payload, type }
index: number
payload: string
maxLength256
type: "quick_reply" or "url"
One of the following:
"quick_reply"
"url"
parameters: optional unknown

Named values for this card’s body placeholders.

header: optional object { kind, upload_ref }

The uploaded media used for this approved template’s media header. Omit unless this template’s send_schema requires it.

kind: "image" or "video" or "document"
One of the following:
"image"
"video"
"document"
upload_ref: string

A durable handle returned by POST /v1/media.

maxLength256
parameters: optional unknown

Named values for the template’s placeholders. The shape is per-template: each registered template publishes its own parameter schema — hand THAT schema to your model when composing a specific template. Omit for templates with no placeholders.

InteractiveList object { body, button, sections, 3 more }

An interactive list message: body text plus a button that opens a sectioned list of selectable rows. The customer’s selection arrives as an inbound interactive_reply part with kind list_reply, carrying the chosen row’s id.

body: string

The message body.

maxLength1024
button: string

Label of the button that opens the list.

maxLength20
sections: array of object { rows, title }

The list’s sections, each holding selectable rows. At most 10 rows in total across ALL sections combined — not 10 per section.

rows: array of object { id, title, description }

This section’s rows. The 10-row limit is on the TOTAL across all sections, so a second section reduces what this one may hold.

id: string

Your identifier for the row — echoed back as the interactive_reply’s id.

maxLength200
title: string

The row’s visible title.

maxLength24
description: optional string

Secondary text under the title.

maxLength72
title: optional string

Section title. Required when the list has more than one section.

maxLength24
type: "interactive_list"
header: optional string

Header text above the body.

maxLength60
InteractiveButtons object { body, buttons, type, 3 more }

An interactive reply-buttons message: body text plus up to three tappable buttons. The customer’s tap arrives as an inbound interactive_reply part with kind button_reply, carrying the tapped button’s id.

body: string

The message body.

maxLength1024
buttons: array of object { id, title }

The tappable buttons — at most three.

id: string

Your identifier for the button — echoed back as the interactive_reply’s id.

maxLength256
title: string

The button’s visible label.

maxLength20
type: "interactive_buttons"
header: optional string

Header text above the body.

maxLength60
media_header: optional object { kind, upload_ref }

Uploaded image, video, or document above the body. Use either header or media_header, never both.

kind: "image" or "video" or "document"
One of the following:
"image"
"video"
"document"
upload_ref: string

A durable handle returned by POST /v1/media.

maxLength256
InteractiveCtaURL object { body, display_text, type, 3 more }

An interactive call-to-action message: body text plus one button that opens a URL.

body: string

The message body.

maxLength1024
display_text: string

The button’s visible label.

maxLength20
type: "interactive_cta_url"
url: string

The URL the button opens. Must be absolute, with an http or https scheme.

maxLength2000
header: optional string

Header text above the body.

maxLength60
LocationRequest object { body, type }

Asks the customer to share a location. The response arrives as a location part.

body: string

Prompt shown above the send-location button.

maxLength1024
type: "location_request"
AddressRequest object { body, country, type }

Reserved for asking an eligible customer in India to submit a structured address. Sending this part is not available until business and customer eligibility can be verified at acceptance. Address responses already arrive as address_reply parts.

body: string

Prompt shown above the address form.

maxLength1024
country: "IN"

ISO country code. Structured address requests are currently available only in India.

type: "address_request"
Contacts object { contacts, type }

One or more contact cards.

contacts: array of object { name, addresses, birthday, 4 more }

The contact cards to send. The channel permits far more; this API caps a message at five, because a message carrying hundreds of cards is a mistake rather than a use case.

name: object { formatted_name, first_name, last_name, 3 more }
formatted_name: string

The contact’s full display name.

maxLength512
first_name: optional string
maxLength256
last_name: optional string
maxLength256
middle_name: optional string
maxLength256
prefix: optional string
maxLength256
suffix: optional string
maxLength256
addresses: optional array of object { city, country, country_code, 4 more }
city: optional string
maxLength256
country: optional string
maxLength256
country_code: optional string
maxLength64
kind: optional string

A label for the address, e.g. WORK, HOME.

maxLength64
state: optional string
maxLength256
street: optional string
maxLength256
zip: optional string
maxLength256
birthday: optional string

The contact’s birthday as YYYY-MM-DD.

emails: optional array of object { email, kind }
email: string
maxLength256
kind: optional string

A label for the address, e.g. WORK, HOME.

maxLength64
org: optional object { company, department, title }
company: optional string
maxLength256
department: optional string
maxLength256
title: optional string
maxLength256
phones: optional array of object { phone, kind, messaging_id }
phone: string

The phone number, ideally in +E.164 form.

maxLength256
kind: optional string

A label for the number, e.g. CELL, MAIN, WORK, HOME.

maxLength64
messaging_id: optional string

The contact’s messaging-account identifier on this channel, when known — makes the card openable in the messaging app.

maxLength256
urls: optional array of object { url, kind }
url: string
maxLength256
kind: optional string

A label for the URL, e.g. WORK, HOME.

maxLength64
type: "contacts"
Location object { latitude, longitude, type, 2 more }

A location pin.

latitude: number

Latitude in decimal degrees.

maximum90
minimum-90
longitude: number

Longitude in decimal degrees.

maximum180
minimum-180
type: "location"
address: optional string

The place’s address, shown under the name.

maxLength1000
name: optional string

The place’s name, shown on the pin.

maxLength1000
Reaction object { emoji, type, channel_message_id, message_id }

An emoji reaction to an earlier message in the chat. The two identifiers are distinct namespaces: inbound reactions carry the channel’s opaque channel_message_id; outbound reactions will carry this API’s message_id once sending them becomes available.

emoji: string

A single emoji. Send an empty string to remove a previous reaction to the same message.

maxLength32
type: "reaction"
channel_message_id: optional string

Inbound only: the channel’s opaque identifier for the message being reacted to. Match it literally to Message.channel_message_id in the chat transcript; never parse it or compare it with Message.id.

maxLength128
message_id: optional string

Outbound only: this API’s Message.id for the message being reacted to. Outbound reactions are not available yet; when they land, the API resolves this identifier into the channel namespace before sending.

maxLength256
InteractiveReply object { id, kind, title, 2 more }

Inbound only — never sendable. The customer’s selection from an interactive_list (kind list_reply) or interactive_buttons (kind button_reply) message, carrying the id you assigned to the chosen row or button.

id: string

The id you assigned to the chosen row or button.

maxLength200
kind: "list_reply" or "button_reply"

Which interactive message kind was answered.

One of the following:
"list_reply"
"button_reply"
title: string

The chosen row’s or button’s visible title.

maxLength24
type: "interactive_reply"
description: optional string

The chosen list row’s secondary text, when it had one.

maxLength72
AddressReply object { type, values, saved_address_id }

Inbound only: the structured values submitted in response to an address request.

type: "address_reply"
values: object { address, building_name, city, 8 more }
address: optional string
maxLength1024
building_name: optional string
maxLength1024
city: optional string
maxLength1024
floor_number: optional string
maxLength1024
house_number: optional string
maxLength1024
landmark_area: optional string
maxLength1024
name: optional string
maxLength1024
phone_number: optional string
maxLength64
pin_code: optional string
maxLength6
state: optional string
maxLength1024
tower_number: optional string
maxLength1024
saved_address_id: optional string

Identifier of the selected saved address, when one was selected.

maxLength256
Order object { catalog_id, items, type, text }

Inbound only — never sendable. An order the customer placed from a product catalog.

catalog_id: string

The catalog the ordered items belong to.

maxLength256
items: array of object { currency, price, product_retailer_id, quantity }

The ordered items.

currency: string

ISO 4217 currency code for price.

maxLength8
price: number

The per-item price at order time.

minimum0
product_retailer_id: string

Your identifier for the product, as registered in the catalog.

maxLength256
quantity: number
minimum0
type: "order"
text: optional string

Free text the customer attached to the order, when any.

maxLength4096
Referral object { type, body, click_id, 7 more }

Inbound only — never sendable. The ad or post context a customer’s first message arrived from (for example an ad whose call to action opens a chat). A referral also opens a free-entry-point customer window — see the chat’s customer_window.

type: "referral"
body: optional string

The ad’s body text at click time.

maxLength4096
click_id: optional string

The click identifier assigned by the ad platform, for attribution.

maxLength256
headline: optional string

The ad’s headline at click time.

maxLength4096
image_url: optional string

URL of the ad’s image creative, when media_kind is image.

maxLength2048
media_kind: optional string

The ad creative’s media kind, e.g. image or video.

maxLength64
source_id: optional string

The ad or post id.

maxLength256
source_kind: optional string

What the source was, e.g. ad or post.

maxLength64
source_url: optional string

The URL of the ad or post the customer came from.

maxLength2048
video_url: optional string

URL of the ad’s video creative, when media_kind is video.

maxLength2048
System object { body, type, event }

Inbound only — never sendable. A system event in the chat, such as the customer changing their number. New event kinds appear over time; body is always present and human-readable.

body: string

Human-readable description of the system event.

maxLength4096
type: "system"
event: optional string

The system event’s kind, when the channel identifies one. New kinds appear over time — treat unknown values as informational.

maxLength64
Unsupported object { raw, type, kind }

Inbound only — never sendable. A message kind this API does not yet type natively, carried as a typed passthrough: raw holds the channel payload, so a new message kind is never a black box or a silent drop. Native part types for popular kinds are added over time; this part is the compatibility guarantee in the meantime.

raw: unknown

The channel payload — any JSON value: object, array, string, number, boolean, or null. Deliberately unconstrained, because the whole point of this part is to carry a shape this API does not yet know. For rows written by API version 1.3.0 or later, object member order, number formatting and duplicated members survive storage and read-back. The canonical encoder may compact insignificant whitespace before storage. Rows written before API version 1.3.0 retain normalized JSON only: member order and number formatting may differ, and only the last duplicated member survives.

type: "unsupported"
kind: optional string

The channel’s name for the message kind, when it declares one.

maxLength64
status: string

The message’s delivery state as last reported.

Inbound messages read received: they arrived, and no delivery of ours ran. A message you sent starts accepted and moves through sent, delivered and read as the channel reports them, with failed as the definitive negative outcome.

unknown means exactly that: the send is INDETERMINATE and we decline to guess. It is NOT terminal — an indeterminate send is never re-sent, and it resolves to sent, delivered, read or failed when the channel’s own report arrives — so keep observing rather than treating it as an outcome.

States are reported asynchronously on the chat’s event sequence, and the set grows additively: treat a value you do not recognise as “no information” rather than failing on it.

Retention of this published status follows the chat transcript policy: there is no scheduled pruning during beta, but there is no fixed minimum availability guarantee. The present absence of a scheduled age-based sweep is not a promise of indefinite availability.

UNDER A sk_test_ KEY THESE ARE SIMULATED. A test-key send is reported sent and then delivered within milliseconds, every time; delivered there means a simulator accepted it, not that a device received it. Live delivery can lag by hours, can never report delivered at all, and can fail after the channel accepted the message — so do not calibrate timeouts or “delivered means it arrived” logic against a test key.

channel_message_id: optional string

The channel’s opaque identifier for this message, when the channel has assigned one. Match an inbound reaction’s channel_message_id to this field literally; never parse it or compare it with id. It is absent while an accepted outbound message has not yet received a channel identifier.

failure: optional object { code, message }

Why a failed message failed, when we have a reason worth publishing.

Present only on status failed, and not on every one of those: not every channel refusal has a reason in the published vocabulary, so a message that failed on the wire may carry no failure at all. Its absence means “no published reason”, never “no reason”.

status stays the thing to branch on. This is additive detail beside it, so an integration written before this field existed still sees a terminal failed and behaves exactly as it did.

code: string

The specific reason.

sender_deregistered — the number this message was going out from is no longer a registered sending number, and the message never left. This is terminal: it is not a pause and it does not clear, so retrying the same message cannot succeed and that conversation is over. Reach this customer by starting a new one on another of your numbers with a template, exactly as you would any customer whose 24-hour window has closed; GET /v1/chats shows which number each conversation uses. You are not billed for a message that failed this way.

quality_hold_expired — this marketing message remained held by an automatic safety control on its sending line for ten minutes and was not sent. This is terminal: retrying the same message cannot succeed on that conversation. Send marketing from another of your numbers with a template, or contact support about the sending line. You are not billed for a message that failed this way.

message: string

Human-readable, written for a person reading it. Do not branch on it.

from: optional string

The number this message left by, or arrived on, in E.164 (leading +). Every message in one conversation carries the same value — a conversation never moves to another number. Absent only if the number is no longer one of yours.