Skip to content

POST /v1/{phone_number_id}/messages

Sends one WhatsApp message to one recipient. The request body is the WhatsApp Cloud API message body, unchanged: if you already integrate directly with https://graph.facebook.com/<version>/<PHONE_NUMBER_ID>/messages, you can switch to Hello by changing only the base URL and the credential.

POST /v1/{phone_number_id}/messages
Authorization: Bearer hello_live_xxxxxxxxxxxxxxxx
Content-Type: application/json

phone_number_id is the sending number’s numeric WhatsApp phone number ID, shown on the Accounts page of the dashboard.

Parameter Type Default Description
sync boolean false Wait up to 10 seconds for WhatsApp to accept the message. See queued and synchronous sends.
Header Required Description
Idempotency-Key No A retry with the same key returns the original message instead of sending again.
Field Type Required Description
messaging_product string No Defaults to and must be whatsapp
recipient_type string No Defaults to individual. group is rejected: Hello doesn’t support the Groups API.
to string Yes Recipient’s number, with a leading + and country code, for example +16505551234. See phone numbers.
type string Yes text, template, image, video, audio, document, sticker, location, contacts, interactive or reaction
context object No { "message_id": "<wamid>" } to send as a reply, quoting an earlier message
biz_opaque_callback_data string No Passed through unchanged; returned on delivery webhooks
<type> object or array Yes, matching type The type-specific object. contacts is an array; every other type is an object.

Every field is forwarded to WhatsApp as sent — Hello validates only what it needs to route, window-check and bill the message, so new Cloud API fields work without a Hello release.

Each type has its own page with full examples and fields:

{
"to": "+919999999999",
"type": "text",
"text": { "body": "Your order has shipped." }
}
Terminal window
curl -X POST "$HELLO_API_URL/v1/<your-phone-number-id>/messages" \
-H "Authorization: Bearer $HELLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919999999999",
"type": "text",
"text": { "body": "Your order has shipped." }
}'

Every type, including template, is accepted onto Hello’s send queue and dispatched fairly across your organization’s traffic; every type except template also needs an open 24-hour window at the time it’s accepted.

{
"messaging_product": "whatsapp",
"contacts": [{ "input": "+919999999999", "wa_id": "919999999999" }],
"messages": [{ "id": "b7e1d2c3-4f5a-4b6c-8d9e-0a1b2c3d4e5f", "message_status": "queued" }]
}
Field Type Description
contacts[0].input string to exactly as you sent it
contacts[0].wa_id string Recipient’s number, normalized digits, no +
messages[0].id string (UUID) Hello’s message ID. Use it with GET /v1/messages/{id}. It isn’t WhatsApp’s wamid — the send is still queued when this responds.
messages[0].message_status string queued

Returned when ?sync=true and the send reaches WhatsApp within the 10-second wait. messages[0].message_status is sent and messages[0].wamid carries WhatsApp’s own message ID. Without ?sync=true, or if the queue hasn’t dispatched it within 10 seconds, you get 202 as above — poll GET /v1/messages/{id} either way.

A body of {"messaging_product": "whatsapp", "status": "read", "message_id": "<wamid>"} (no type) is a mark-as-read / typing-indicator request. It’s sent to WhatsApp directly, not queued, and returns 200 {"success": true}. See Mark a message as read.

Status code Cause
400 VALIDATION_ERROR Invalid JSON, missing to/type, an unsupported type, the wrong shape for the type’s object, a group recipient, or to missing the + and country code
400 TEMPLATE_LANGUAGE_REQUIRED (type: template) The name exists in several languages and none was given — available_languages lists them
401 UNAUTHORIZED Missing, wrong or disabled API key
403 OUTSIDE_24H_WINDOW A non-template message to a recipient whose 24-hour window is closed
403 TEMPLATE_NOT_APPROVED The template exists but isn’t APPROVED
403 PLAN_* Your plan doesn’t allow this send. See plan errors.
404 ACCOUNT_NOT_FOUND No number with this phone_number_id in your organization
404 TEMPLATE_NOT_FOUND (type: template) No template with this name on the number’s WABA
404 TEMPLATE_LANGUAGE_NOT_FOUND (type: template) The name exists but not in the requested language
409 ACCOUNT_NOT_REGISTERED The number hasn’t completed phone registration
422 SEND_FAILED WhatsApp rejected the message. Only returned with ?sync=true or for mark-as-read — otherwise a rejection lands on the message as status: "failed".
{
"message": "template \"order_shipped\" has no \"fr\" language; available: en, en_US, hi",
"code": "TEMPLATE_LANGUAGE_NOT_FOUND",
"available_languages": ["en", "en_US", "hi"]
}