Skip to content

Messages

Sends one message.

Scope: messages:send · Accepts Idempotency-Key

One endpoint handles every message type, discriminated by type, following the shape of Meta’s own Cloud API so a developer who has seen that already knows this one.

FieldTypeNotes
tostringPhone number in international format. A contact is created if none exists.
contact_idstringAddress an existing contact instead. One of to or contact_id.
fromstringWhich WhatsApp number to send from. Defaults to the template’s, then the contact’s, then the organization default.
typestringtext, template, image, video, audio or document.
textobject{ "body": "..." } when type is text.
templateobjectSee below.
image / video / audio / documentobjectSee below.

Unknown fields are rejected with 400, so a typo tells you the field name is wrong rather than that a required one is missing.

{
"to": "+966500000000",
"type": "text",
"text": { "body": "Your order has shipped." }
}
{
"to": "+966500000000",
"type": "template",
"template": {
"name": "order_shipped",
"parameters": { "name": "Sara", "tracking": "SA123456789" }
}
}
FieldNotes
nameTemplate name. Or use id.
parametersBody variables, keyed by name, or by position ("1", "2") for positional templates.
header_parametersHeader variables. Meta indexes these separately from body variables, so header {{1}} and body {{1}} are different values.
button_parametersDynamic URL or copy-code button values, keyed by button index.
header_media{ "id": "..." } or { "link": "https://...", "filename": "..." } for templates whose header is an image, video or document.

Every variable a template declares must be supplied. Missing ones are refused with missing_template_parameter, naming what is missing and what the template expects. WhatsApp itself does not check this: it delivers the message with an empty gap in the sentence and reports success, so this is the only place it can be caught.

GET /v1/templates lists each template’s variables.

For authentication templates with a copy-code button, passing the code as parameter "1" fills the button automatically.

{
"to": "+966500000000",
"type": "document",
"document": {
"link": "https://example.com/invoices/1001.pdf",
"filename": "Invoice-1001.pdf",
"caption": "Your invoice"
}
}
FieldNotes
linkA public URL. We fetch it and upload it to WhatsApp. Maximum 15 MB.
idA media id already uploaded to Meta. Use this for anything larger, or to avoid re-uploading.
captionShown under the media. Not supported for audio.
filenameDocuments only. WhatsApp shows it to the recipient.
voiceAudio only. true sends it as a push-to-talk voice note.

Links must be public http or https. Private and internal addresses are refused, and refused again at connection time if DNS resolves to one.

201
{
"id": "6f1c2d3e-...",
"contact_id": "3f2a...",
"to": "+966500000000",
"from": "Main",
"type": "text",
"direction": "outgoing",
"status": "pending",
"content": "Your order has shipped.",
"has_media": false,
"created_at": "2026-08-20T09:15:00Z"
}

pending means accepted, not delivered. The message is stored and queued; WhatsApp has not confirmed anything. Subscribe to message.delivered, message.read and message.failed, or poll the endpoint below.

One message and its current status.

Scope: messages:read

200
{
"id": "6f1c2d3e-...",
"contact_id": "3f2a...",
"to": "+966500000000",
"from": "Main",
"type": "template",
"direction": "outgoing",
"status": "delivered",
"template_name": "order_shipped",
"wamid": "wamid.HBgMOTY2NTAwMDAwMDAwFQIAERgS...",
"has_media": false,
"created_at": "2026-08-20T09:15:00Z",
"updated_at": "2026-08-20T09:15:04Z"
}
StatusMeaning
pendingAccepted by us, not yet handed to WhatsApp
sentWhatsApp accepted it
deliveredIt reached the recipient’s device
readThe recipient opened it
failedWhatsApp refused it. error_message says why.

A message can go from pending straight to failed. Statuses never move backwards, so read will not revert to delivered.

Streams the bytes of a media message. Returns the file, not JSON, with the original content type and filename.

Scope: messages:read

Terminal window
curl https://k-message.kerneltics.com/v1/media/6f1c2d3e-... \
-H "Authorization: Bearer km_live_YOUR_KEY" \
-o invoice.pdf

404 if the message has no media attached, or is not yours.