Skip to content

Messages

Download .md

Sending one SMS, MMS or WhatsApp template, uploading its picture, reading it back, and pictures people text you. How-to: Send a text, Send a WhatsApp template.

Generated from /openapi/v1.json; interactive version: API reference.

methodpathwhat it doessuccess
GET/v1/messagesList messages you sent, newest first200
POST/v1/messagesSend one SMS or WhatsApp message202
GET/v1/messages/{id}Read a message’s status200
POST/v1/mediaUpload a picture or video to send as MMS201
GET/v1/media/{id}Get a download URL for a file someone sent in an MMS200

List messages you sent, newest first.

The SMS and WhatsApp template sends of the workspace (this API’s, the dashboard’s and campaigns’), newest first by created_at to the microsecond, ties by id. Not the person’s messages and not inbox replies: those are in GET /v1/conversations/{id}/messages. Without channel, both channels in one list, each item with its channel; with it, that channel only. to, from, campaign, status and the created_* times narrow it. Each item is the object GET /v1/messages/{id} answers. A cursor belongs to the list that answered it: one from channel=sms is refused without channel, and the reverse (invalid_cursor, with a hint). A cursor from the SMS list before 2026-10-16 (when it was ordered by hand-over time) is refused once with invalid_cursor: start again without after.

Parameters

nameintypedescription
limitqueryinteger
1–200
Page size, 1 to 200 (default 50).
afterquerystringThe previous page’s next_cursor.
channelquerystring
sms, whatsapp
Only this channel’s sends. Leave it out to list both channels together.
toquerystringOnly messages to this recipient, E.164.
campaignquerystringOnly this campaign’s messages: its ca-… id or UUID. On WhatsApp, the template sends the campaign made (also listed per person by GET /v1/campaigns/{id}/recipients).
fromquerystringOnly messages sent from this number: its E.164, pn-… id or the channel’s ch-… id, as GET /v1/senders lists them (on WhatsApp, the WhatsApp channel of that number). Without channel, a number that is only an SMS sender or only a WhatsApp number lists that channel’s sends.
statusquerystring
queued, sending, sent, delivered, read, undelivered, failed, skipped, unconfirmed
Only messages with this status (SMS has no read; WhatsApp has no undelivered). Without channel, read lists WhatsApp only and undelivered SMS only.
created_afterquerystringOnly messages created at or after this, exact to the microsecond, as an ISO 8601 time (2026-10-16T09:00:00Z). Not a sync feed: created_at is stamped as a message is accepted, before it is committed, so a later page can show an older created_at; overlap your window or use the webhooks.
created_beforequerystringOnly messages created before this, as an ISO 8601 time (2026-10-16T09:00:00Z).

Answers 200 with MessageList. Errors use the error envelope; common codes: SMS, uploads, WhatsApp, lists, every request.

Send one SMS or WhatsApp message.

Sends one text (channel: sms) or one approved WhatsApp template (channel: whatsapp). Answers 202 with the message: it is on its way, not yet delivered. Follow it with GET /v1/messages/{id} or the message.status_updated webhook. Opt-outs, sender readiness, credits and sending limits are all checked before anything is sent.

Parameters

nameintypedescription
idempotency-keyheaderstring
≤ 255 chars
Send one with every message. A retry with the same key never sends a second message: it answers the first one as it is now. Rules: Idempotency.

Request body: MessageCreate.

Answers 202 with Message. Errors use the error envelope; common codes: SMS, uploads, WhatsApp, lists, every request.

Read a message’s status.

Parameters

nameintypedescription
idpathstring, requiredThe message id from the send.

Answers 200 with Message. Errors use the error envelope; common codes: SMS, uploads, WhatsApp, lists, every request.

Upload a picture or video to send as MMS.

Upload one file as a multipart/form-data part named file, then pass the answer’s media as media on POST /v1/messages, as content.media on POST /v1/campaigns or PATCH /v1/campaigns/{id}, or as media on POST /v1/conversations/{id}/messages. Accepted: a JPEG, PNG or GIF image up to 5 MB (sent without its metadata, such as GPS), or an MP4, MOV or WebM video up to 50 MB, which is sent as an MP4 of at most 500 KB (transcoded: true). The bytes decide the type; the declared content type and file name extension are ignored. An upload that no message, campaign or template uses is deleted after 7 days (expires_at). There is no Idempotency-Key: a retried upload stores a second copy, which the same rule deletes if it goes unused.

Request body (multipart/form-data)

fieldtypedescription
filestring (binary), requiredThe picture or video. Any other form field is 400 unknown_field.

Example: see Send a text.

Answers 201 with UploadedMedia. Errors use the error envelope; common codes: SMS, uploads, WhatsApp, lists, every request.

Get a download URL for a file someone sent in an MMS.

Each message.received event lists the photos and files the person sent in media[], and each item’s url is this endpoint. We fetch every file from the carrier when the message arrives and keep our own copy, so the link keeps working after the carrier deletes theirs. The answer carries a short-lived download URL (see expires_in): fetch it when you need the bytes, rather than storing it. Scoped to the workspace that owns the API key; a file in another workspace returns 404, not 403. A file we could not fetch from the carrier is not listed in the event and has no media id.

Parameters

nameintypedescription
idpathstring, requiredThe att-… media id from message.received.media[].url.

Answers 200 with the object below. Errors use the error envelope; common codes: SMS, uploads, WhatsApp, lists, every request.

fieldtypedescription
objectstring, required
always media
idstring, required
message_idstring, required
conversation_idstring, required
content_typestring, required
sizeinteger, requiredBytes.
urlstring, requiredPresigned HTTPS URL. Treat as a secret — it grants read access until it expires.
expires_ininteger, requiredSeconds until url stops working.

Example: see Inbound pictures.

fieldtypedescription
idstring, required
objectstring, required
always message
channelstring, required
sms, whatsapp
fromstring or null, requiredThe sending number in E.164. null when it could not be read: a WhatsApp number whose channel has no display number yet, or a sender id that no longer resolves.
tostring, required
statusstring, required
queued, sending, sent, delivered, read, undelivered, failed, skipped, unconfirmed
queued, sending, sent (the carrier or Meta has it), delivered, read (WhatsApp), undelivered, failed, skipped (opted out just before sending) or unconfirmed (we could not confirm it went; do not resend).
conversation_idstring or null, required
campaign_idstring or null, required
segmentsinteger or null, requiredSMS only.
billed_creditsnumber or null, requiredCredits this message cost. SMS: null until it is billed. WhatsApp: always null (not reported per message).
errorobject or null, required
error.codestring or null, requiredThe carrier’s, Meta’s or the call’s own code, when there is one.
error.categorystring, required
opted_out, carrier_filtered, rate_limited, invalid_number, unreachable, account, unconfirmed, blocked_by_route, not_sent, other
Closed set: opted_out, carrier_filtered, rate_limited, invalid_number, unreachable, account, unconfirmed, blocked_by_route, not_sent, other.
error.messagestring, required
error.actionstring or null, requiredWhat to do about it.
templateobject or null, requiredWhatsApp only.
template.idstring, required
template.namestring, required
template.languagestring, required
textstring or null, requiredWhat was sent: an SMS’s text (for a long text sent in parts, this part’s), or a WhatsApp template’s text as the person got it. Null when there is none, or once the platform’s retention cleared a text’s body.
mediaarray of objects, requiredThe files sent with it (an MMS), as a conversation message lists them; [] for none.
media[].idstring or null, requiredThe file’s att-… id.
media[].urlstring or null, requiredGET it with your key for a download link (/v1/media/{id}). Null for a file kept only as a link.
media[].content_typestring or null, required
created_atstring or null, required
sent_atstring or null, required
delivered_atstring or null, required
read_atstring or null, required
updated_atstring or null, requiredWhen the status last changed. SMS only: always null on WhatsApp (follow sent_at, delivered_at and read_at).
metadatamap of string to string or null, requiredThe metadata you sent, as you sent it; null when you sent none (or {}).

Example: see Send a text.

One message. channel decides the rest of the body. More channels may be added later; ignore values you don’t know.

Fields for every channel:

fieldtypedescription
fromstring, required
1–64 chars
The number to send from, as GET /v1/senders lists it: its E.164 (+16285550100), its pn-… id or UUID, or the ch-… id of one of its channels. Every channel takes the same forms. The shared trial number sends no texts or campaigns (422 trial_sender_not_allowed).
tostring, required
1–32 chars
The recipient, in E.164: +, the country code and the number, no spaces or punctuation.
metadatamap of string to string
≤ 20 keys; keys 1–40 chars; values ≤ 500 chars
Your own key/value pairs, stored and returned as sent. String values only; at most 20 keys, each key 1–40 characters, each value at most 500 characters (400 invalid_request, param metadata or metadata.<key>). {} is the same as leaving it out.

When channel is sms (SmsMessageCreate):

fieldtypedescription
channelstring, required
always sms
textstring
≤ 1,600 chars
The text, up to 1,600 characters. Optional when media is set.
mediastring
≤ 512 chars
A picture or video: the media that POST /v1/media answers. MMS goes to +1 numbers only.
conversationstring
auto, none
auto (default) adds the message to the recipient’s conversation, so a reply lands next to it and your AI agent has the context. none records only the send.

Example (channel: sms):

{
"channel": "sms",
"from": "+16285550100",
"to": "+14155550100",
"text": "Your table is ready",
"conversation": "auto",
"metadata": {
"order_id": "1042",
"source": "checkout"
}
}

When channel is whatsapp (WhatsappMessageCreate):

fieldtypedescription
channelstring, required
always whatsapp
templateobject, required
template.idstring
1–64 chars
The template’s wt-… id or UUID. Or name it with name + language.
template.namestring
1–512 chars
The template’s name, as approved by Meta.
template.languagestring
2–15 chars
With name: the language code, e.g. en_US.
template.variablesmap of string to string or array of string
values 1–1,024 chars; ≤ 30 items; each 1–1,024 chars
The body variables. Named templates: an object ({ "first_name": "Mei" }). Positional templates ({{1}}): an array in order, or an object keyed "1", "2", ….
template.headerobject
template.header.typestring, required
text, image, video, document
template.header.textstring
1–1,024 chars
type: text: the header variable’s value.
template.header.urlstring (uri)
≤ 2,048 chars
image / video / document: an https URL Meta can fetch.
template.header.filenamestring
1–240 chars
document: the file name shown to the recipient.
template.buttonsarray of objects
≤ 10 items
template.buttons[].indexinteger, required
0–9
The button’s position in the template, from 0.
template.buttons[].url_suffixstring
1–1,024 chars
Dynamic URL button: the value appended to the URL.
template.buttons[].coupon_codestring
1–15 chars
Copy-code button: the code.
template.buttons[].payloadstring
1–256 chars
Quick-reply button: returned to you when the person taps it.
template.componentsarray of map of string to any
≤ 12 items
Meta’s raw components array, for shapes variables, header and buttons cannot express. It is still checked against the approved template (422 template_components_mismatch). Send this OR variables/header/buttons, not both.

Example (channel: whatsapp):

{
"channel": "whatsapp",
"from": "+16285550100",
"to": "+14155550100",
"template": {
"name": "order_ready",
"language": "en_US",
"variables": {
"first_name": "Mei"
}
}
}
fieldtypedescription
objectstring, required
always list
dataarray of Message, required
has_moreboolean, required
next_cursorstring or null, requiredPass as after for the next page; null on the last.

Example: see Send a text.

fieldtypedescription
objectstring, required
always uploaded_media
mediastring, requiredPass this exact string as media on POST /v1/messages, content.media on campaigns, or media on a conversation reply.
content_typestring, requiredThe stored file’s type: image/jpeg, image/png, image/gif or video/mp4 (every video is stored as an MP4).
sizeinteger, requiredBytes stored (after preparing).
transcodedboolean, requiredtrue for a video, which is re-encoded as an MP4 of at most 500 KB.
preview_urlstring or null, requiredA short-lived link to view the stored file. Treat as a secret. null when the link could not be made (the upload still succeeded).
preview_expires_ininteger or null, requiredSeconds until preview_url stops working; null with a null preview_url.
expires_atstring (date-time), requiredWhen the file is deleted if nothing uses it (7 days). A message, campaign or template that uses it keeps it.

Example: see Send a text.