Skip to content

Send a Message

WhatsApp only allows free-form text within 24 hours of the customer’s last message. Outside that window (or to start a fresh conversation), you must send an approved template.

Whatbot checks this before calling WhatsApp. If the window is shut and you asked for text or media, you get a 409 naming the problem rather than a message that quietly never arrives:

409
{
"detail": {
"error": "window_closed",
"require_template": true,
"last_inbound_at": "2026-08-01T09:00:00Z",
"message": "Outside the 24-hour window — a free-form message will not be delivered. Send an approved template, or pass 'fallback_template_name' to fall back to one automatically."
}
}

Call Messaging Window first if you want to know before you send.

Pass fallback_template_name alongside a text or media send. Inside the window your actual message goes out; outside it, the template goes instead — one call either way. The response’s delivered_as tells you which actually happened.

cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "9876543210",
"type": "text",
"body": "Here is the brochure we discussed.",
"fallback_template_name": "voice_call_followup",
"fallback_components": [
{ "type": "body", "parameters": [ { "type": "text", "text": "Ravi" } ] }
]
}'

This matters when you cannot know the window state in advance — an automated caller messaging someone who may or may not have written to you before.

The fallback fires on either of two signals, so a caller gets one call either way even when we cannot look the window up:

  • the window is known to be shut, in which case the free-form send is never attempted; or
  • the free-form send was attempted and WhatsApp refused it as out-of-window (error 131047) — which is what happens when the window lookup itself fails, or when the window closed between the lookup and the send.

Only 131047 triggers it. A template is exempt from the 24h rule, so substituting one for any other failure would send unrelated billable marketing in place of your message, and report it as a window problem.

Read delivered_as and template_name in the response to say something truthful about what the customer actually received.

Use this to start a conversation or message outside the 24h window.

cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "9876543210",
"type": "template",
"template_name": "order_update",
"language_code": "en",
"components": [
{ "type": "body", "parameters": [ { "type": "text", "text": "Ravi" } ] }
]
}'
cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "to": "9876543210", "type": "text", "body": "Thanks! Your order ships today." }'

Set type to image, document, video, or audio and pass a public media_url — Whatbot hands the link to WhatsApp, which fetches it (no upload step). The file must be reachable over public HTTPS and within WhatsApp’s media size limits for that type. Like text, media is a service message — it delivers inside the 24-hour window; outside it, send a template with a media header instead.

Send a PDF (document):

cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "9876543210",
"type": "document",
"media_url": "https://example.com/invoices/INV-1042.pdf",
"filename": "Invoice INV-1042.pdf",
"caption": "Your invoice"
}'

Send an image:

cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "9876543210",
"type": "image",
"media_url": "https://example.com/photos/site.jpg",
"caption": "Site photo"
}'

type: "video" and type: "audio" work the same way (video takes an optional caption; audio takes neither caption nor filename).

A workspace can hold several WhatsApp numbers. Pass from to choose which one sends — either the number itself in any format, or its WhatsApp phone number ID (the phone_number_id that arrives on message.received). Leave it out to send from the workspace’s default number.

cURL
curl -X POST https://api.whatbot.in/public/v1/messages \
-H "X-API-Key: wa_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "to": "9876543210", "from": "+91 78100 83015", "type": "text", "body": "Hi from our Chennai store" }'

A from that isn’t one of this workspace’s numbers — or one that has been disconnected — returns 400. It never silently falls back to the default number.

FieldTypeRequiredDescription
tostringYesDestination phone, any format (normalized to E.164). An invalid number returns 422.
fromstringWhich of your numbers sends: the number in any format, or its phone number ID. Defaults to the workspace’s default number.
typestringtext, template, image, document, video, or audio. Defaults to text. Any other value is treated as text.
bodystringThe message text. Required when type is text.
media_urlstringPublic HTTPS URL of the file. Required when type is image/document/video/audio.
captionstringOptional caption for image/video/document.
filenamestringOptional display filename for document (e.g. Invoice.pdf).
template_namestringThe approved template’s name. Required when type is template.
language_codestringTemplate language, e.g. en, en_US. Defaults to en.
componentsarrayTemplate variable values (header/body/button parameters), per the WhatsApp template component format.
namestringOptional contact name for the conversation. If given, it also replaces the name shown on an existing conversation.
fallback_template_namestringTemplate to send instead when a free-form message will not be delivered — either because the window is known to be shut, or because WhatsApp rejects the send as out-of-window (error 131047). Not consulted for an explicit type: "template" send.
fallback_language_codestringLanguage for the fallback template. Defaults to en.
fallback_componentsarrayVariable values for the fallback template.
FieldTypeRequiredDescription
message_idstringWhatbot’s id for the message row.
wa_message_idstringWhatsApp’s wamid, once accepted.
statusstringsent once WhatsApp accepted it. Later delivery states arrive on the message.status webhook.
conversation_idstringThe conversation it was recorded on.
delivered_asstringWhat actually went out — your type, or template if the fallback fired.
window_openbooleanWhether the 24h window was open. null for an explicit template send, where it is not checked, and when the window lookup failed and the message was sent anyway.
template_namestringPresent only when the fallback fired.
200
{
"message_id": "b1f0...",
"wa_message_id": "wamid.HBg...",
"status": "sent",
"conversation_id": "a5230fa7-...",
"delivered_as": "text",
"window_open": true
}

Read delivered_as on a success to tell what actually went out: text means your message was sent, template means the window was shut and the fallback template went instead. The two are identical on status alone.

StatusCause
400from is not one of this workspace’s numbers, or is disconnected
401Missing/invalid API key
402Not enough prepaid wallet balance (error: "insufficient_balance", with balance, required, currency), or the workspace’s plan is not active. Nothing was sent.
409window_closed — a text/media send outside the 24-hour window with no fallback_template_name
422Invalid to; body missing for text; template_name missing for a template; media_url missing for media. Checked before the window, so a malformed request fails the same way whatever the window state.
424The workspace has no WhatsApp number connected
502WhatsApp rejected the message (send_failed / fallback_template_failed)