Event Log
Server-sent event streams — the same events the webhooks carry, held open instead of delivered.
List account events
ModelsExpand Collapse
AccountEvent object { id, data, timestamp, type } One canonical account event envelope. Account sequence is transport metadata and is not a member.
One canonical account event envelope. Account sequence is transport metadata and is not a member.
data: object { message } Extensible event-specific data.
Extensible event-specific data.
Frozen message snapshot on newly materialized incoming-message events, including canonical parts and available authenticated attachment URLs. Older retained events omit it; use the chat transcript for history.
Frozen message snapshot on newly materialized incoming-message events, including canonical parts and available authenticated attachment URLs. Older retained events omit it; use the chat transcript for history.
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.
parts: array of object { body, type, preview_url } or object { kind, type, caption, 4 more } or object { language, name, type, 3 more } or 14 moreThe message content — sendable parts, plus the inbound-only types on
inbound messages. Every message carries at least one part.
The message content — sendable parts, plus the inbound-only types on inbound messages. Every message carries at least one part.
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.
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 moreWhich kind of media this is. Captions apply to image, video and document only; filename applies to document only.
Which kind of media this is. Captions apply to image, video and document only; filename applies to document only.
Caption rendered with the media. Image, video and document only — a caption on an audio or sticker part is rejected, never dropped.
Display filename. Documents only — a filename on any other kind is rejected, never dropped.
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.
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.
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.
The approved template’s name. Lowercase letters, digits and underscores only.
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.
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.
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.
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.
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.
The list’s sections, each holding selectable rows. At most 10 rows in total across ALL sections combined — not 10 per section.
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.
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.
InteractiveCtaURL object { body, display_text, type, 3 more } An interactive call-to-action message: body text plus one button that opens a URL.
An interactive call-to-action message: body text plus one button that opens a URL.
LocationRequest object { body, type } Asks the customer to share a location. The response arrives as a location part.
Asks the customer to share a location. The response arrives as a location part.
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.
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.
Contacts object { contacts, type } One or more contact cards.
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.
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.
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.
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.
A single emoji. Send an empty string to remove a previous reaction to the same message.
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.
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.
AddressReply object { type, values, saved_address_id } Inbound only: the structured values submitted in response to an address request.
Inbound only: the structured values submitted in response to an address request.
Order object { catalog_id, items, type, text } Inbound only — never sendable. An order the customer placed from a product catalog.
Inbound only — never sendable. An order the customer placed from a product catalog.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
AccountEventPage object { data, has_more, next_cursor }
data: object { message } Extensible event-specific data.
Extensible event-specific data.
Frozen message snapshot on newly materialized incoming-message events, including canonical parts and available authenticated attachment URLs. Older retained events omit it; use the chat transcript for history.
Frozen message snapshot on newly materialized incoming-message events, including canonical parts and available authenticated attachment URLs. Older retained events omit it; use the chat transcript for history.
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.
parts: array of object { body, type, preview_url } or object { kind, type, caption, 4 more } or object { language, name, type, 3 more } or 14 moreThe message content — sendable parts, plus the inbound-only types on
inbound messages. Every message carries at least one part.
The message content — sendable parts, plus the inbound-only types on inbound messages. Every message carries at least one part.
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.
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 moreWhich kind of media this is. Captions apply to image, video and document only; filename applies to document only.
Which kind of media this is. Captions apply to image, video and document only; filename applies to document only.
Caption rendered with the media. Image, video and document only — a caption on an audio or sticker part is rejected, never dropped.
Display filename. Documents only — a filename on any other kind is rejected, never dropped.
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.
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.
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.
The approved template’s name. Lowercase letters, digits and underscores only.
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.
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.
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.
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.
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.
The list’s sections, each holding selectable rows. At most 10 rows in total across ALL sections combined — not 10 per section.
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.
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.
InteractiveCtaURL object { body, display_text, type, 3 more } An interactive call-to-action message: body text plus one button that opens a URL.
An interactive call-to-action message: body text plus one button that opens a URL.
LocationRequest object { body, type } Asks the customer to share a location. The response arrives as a location part.
Asks the customer to share a location. The response arrives as a location part.
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.
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.
Contacts object { contacts, type } One or more contact cards.
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.
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.
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.
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.
A single emoji. Send an empty string to remove a previous reaction to the same message.
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.
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.
AddressReply object { type, values, saved_address_id } Inbound only: the structured values submitted in response to an address request.
Inbound only: the structured values submitted in response to an address request.
Order object { catalog_id, items, type, text } Inbound only — never sendable. An order the customer placed from a product catalog.
Inbound only — never sendable. An order the customer placed from a product catalog.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.