Sending WhatsApp messages
Send WhatsApp with the same POST /messages as SMS, with channel set to WHATSAPP. What you can send depends on when the customer last wrote to you.
You need
- An API key with the Messaging API turned on. See API keys and base URLs.
- A WhatsApp number: your sandbox WhatsApp number, or one you connected in live. Its number is the
sender. - An approved template, to start a conversation. See Templates.
The 24-hour window
The 24-hour customer service window opens each time a customer writes to you, and closes 24 hours after their last message.
- Inside the window, reply with text, a file, buttons or a list. Each reply is charged at the reply price.
- Outside the window, or to start a conversation, send an approved template. It's charged at its category's price.
Send a template
curl -X POST $PONITOR_BASE_URL/messaging/v1/messages \
-H "apiKey: $PONITOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender": "+231770000000",
"channel": "WHATSAPP",
"recipient": "+231770123456",
"externalId": "remind-0412",
"template": {
"id": "6f1c2a3e-8b4d-4e5f-9a1b-2c3d4e5f6a7b",
"body": ["Blama", "LRD 4,500", "18 June"]
}
}'
For a template approved as Hi {{1}}, your repayment of {{2}} is due {{3}}., the customer gets Hi Blama, your repayment of LRD 4,500 is due 18 June.
| Field | What to send |
|---|---|
template.id | The Template ID from the template's page in WhatsApp › Templates. It must be approved, and belong to the WhatsApp account of the number you send from. |
template.body | The values for {{1}}, {{2}} and so on, in order, as text. Up to 20 values of up to 1,024 characters each. Leave it out if the template has no placeholders. |
template.buttons | The values for the buttons' placeholders, in order, such as the end of a link button's address. Leave it out if the buttons have none. |
The header is sent as it was approved. The older templateId with templateParameters still works. To send an SMS when the number isn't on WhatsApp, add smsFallback. See If they're not on WhatsApp.
Read the response
201 Created means the message is charged and queued for WhatsApp:
{
"message": {
"id": "8d0f3c2a-5b7e-4a91-b6c4-1e2f3a4b5c6d",
"channel": "WHATSAPP",
"direction": "OUTBOUND",
"sender": "+231770000000",
"recipient": "+231770123456",
"status": "QUEUED",
"messageType": "TEMPLATE",
"inServiceWindow": false,
"cost": 0.03,
"externalId": "remind-0412",
"createdAt": "2026-10-05T09:30:00.112Z"
}
}
inServiceWindow says whether it went inside the customer's 24-hour window. 200 OK instead means you already used this externalId, and the body is the original message.
Reply inside the window
Put your reply in text:
{
"sender": "+231770000000",
"channel": "WHATSAPP",
"recipient": "+231770123456",
"externalId": "support-reply-77",
"text": "Thanks Blama, your refund is on its way."
}
WhatsApp formatting works in text: *bold*, _italic_, ~strikethrough~ and ```monospace```. To send an image, a video, an audio file, a document, a location or contact cards, or to add buttons or a list, see WhatsApp replies.
Customers' messages come to your webhook as message.received events. See Receiving webhooks.
When a send is refused
| Status | Why | Fix |
|---|---|---|
400 | E10006: Free-form WhatsApp messages are only allowed during an active customer service window | The window is closed. Send a template. |
400 | The template isn't approved, isn't in this number's WhatsApp account (E10005), or has too few or too many values. The message says which. | Check the Template ID and the number of values. |
400 | A field is missing or invalid (E01001), or your balance is too low (E03001). | Fix the request, or add funds. |
401 | The key is wrong, or it doesn't match the base URL. | See API keys and base URLs. |
403 | The Messaging API isn't turned on, or your team isn't approved (E02010). | Check Subscribed APIs and Settings › Compliance. |
422 | E10008: the person unsubscribed from your marketing. | Don't send them marketing templates. See below. |
429 | Too many requests in a minute (E01011). | Wait, then try again. See Rate limits. |
Nothing is charged for a refused send.
When someone unsubscribes
When someone sends your WhatsApp number UNSUBSCRIBE (or STOP), or taps Stop promotions on a marketing template, they're unsubscribed from your team's marketing. The message must be only that word: "How do I unsubscribe?" doesn't count.
- Marketing templates aren't sent to them. The API answers
422with the codeE10008, and nothing is charged. - Authentication and utility templates still go, so they keep getting their codes and order updates.
- Your replies inside the 24-hour window still go.
- If they send SUBSCRIBE, they're subscribed again.
Either way, they get a short reply saying so, charged as a reply. See who unsubscribed, and subscribe someone again when they ask, under WhatsApp › Settings.
{
"error": {
"type": "unprocessable_error",
"code": "E10008",
"name": "RecipientOptedOut",
"message": "This number unsubscribed, so marketing templates aren't sent to it. Utility and authentication templates still go.",
"doc_url": "https://docs.ponitor.com/api/errors#E10008",
"request_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
}
}
Before codes, this answer had "code": "RECIPIENT_OPTED_OUT" at its top level. It's deprecated and still sent.
WhatsApp statuses
WhatsApp messages move through the same statuses as SMS (see Delivery statuses), with these differences:
- There's no
EXPIRED. A message that can't reach the phone ends asUNDELIVERED. - Read isn't a status. A read message stays
DELIVERED. - An
UNDELIVEREDmessage is charged the lower not-delivered price, and the difference goes back to your balance.
| Status | Meaning on WhatsApp | Charged? |
|---|---|---|
| Queued | Ponitor accepted it and is handing it to WhatsApp. | Held |
| Sent | WhatsApp accepted it. | Yes |
| Delivered | WhatsApp confirmed it reached the phone. | Yes |
| Undelivered | WhatsApp couldn't deliver it, for example to a number that isn't on WhatsApp. | The not-delivered price |
| Unknown | No delivery update arrived within 48 hours. It may have arrived. | Yes |
| Failed | It never reached WhatsApp. | No, refunded |
An undelivered WhatsApp message says why in failureCode and failedReason. WhatsApp's own reasons are the E111 codes in Errors: E11101 not on WhatsApp, E11103 outside the 24-hour window, E11109 a marketing message held back, and more.