// Endpoints

Messages

Send text, media and approved templates to any WhatsApp number.

POST/api/v1/messages

Send a message

Scope: messages:send

Pass a phone number, not an internal id. If we have never seen the number, a contact and a conversation are created for it, and the message appears in your team's inbox like any other.

Body

FieldTypeNotes
tostring, requiredE.164, e.g. +919876543210
typestringtext (default), template, image, video, document or audio
textstringThe message, or the caption for media
media_urlstringRequired for media. A public https URL
filenamestringOptional, shown for documents
templateobjectRequired when type is template — see below
reply_to_message_idstringQuote an earlier message in the same conversation
namestringNames the contact if this send creates one

Text

curl -X POST https://whatsapp.esendblue.com/api/v1/messages \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "+919876543210", "text": "Your order has shipped." }'

Media

curl -X POST https://whatsapp.esendblue.com/api/v1/messages \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+919876543210",
  "type": "document",
  "media_url": "https://files.example.com/invoice-1042.pdf",
  "filename": "Invoice 1042.pdf",
  "text": "Your invoice"
}'

Templates#

Outside the 24-hour window — for example, the first message to a customer — only an approved template goes out. Build one under Settings → Templates, wait for Meta to approve it, then send it by name and language.

curl -X POST https://whatsapp.esendblue.com/api/v1/messages \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+919876543210",
  "type": "template",
  "template": {
    "name": "order_update",
    "language": "en_US",
    "params": ["A1042", "Thursday"]
  }
}'
params as an objectFills
bodyBody variables {{1}}, {{2}}… in order
headerTextA text header's {{1}}
headerMediaUrlAn image, video or document header for this send
buttonParamsKeyed by button position: the {{1}} of a URL button, or a copy-code value
otpCodeThe code, for authentication templates — see below

An array, as in the first example, is shorthand for { "body": [...] }.

Authentication (OTP) templates

Meta writes the text of an authentication template; you supply only the code. Pass it as otpCode and it fills both the message and the copy-code or autofill button. If you want the code generated, stored safely and checked for you, use one-time passwords instead.

curl -X POST https://whatsapp.esendblue.com/api/v1/messages \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+919876543210",
  "type": "template",
  "template": { "name": "login_code", "language": "en", "params": { "otpCode": "483920" } }
}'

Response#

{
  "data": {
    "message_id": "…",
    "whatsapp_message_id": "wamid.HBgM…",
    "conversation_id": "…",
    "contact_id": "…",
    "contact_created": false
  }
}

A 201 means Meta accepted the message, not that it was delivered. To track delivery, subscribe to message.status_updated or read the conversation's messages.

Errors#

If the send was refused before reaching Meta, nothing was delivered and nothing was charged. These codes come on top of the common ones:

codeStatusWhat to do
window_closed409Outside the 24-hour window. Send an approved template
template_not_approved422The template is pending, rejected or paused at Meta
template_not_synced422The template is not in your list yet. Run "Sync from Meta" under Settings → Templates
contact_opted_out403Marketing template to a contact who replied STOP. Never sent, by design
frequency_capped429Marketing template to someone who has reached your project's frequency cap
daily_budget_exceeded429Today's marketing allowance for your number is used up. Try tomorrow
policy_lookup_failed503We could not check the rules above, so nothing was sent. Safe to retry
whatsapp_not_configured400No WhatsApp number is connected to this project yet
plan_locked402The project's plan has ended. Sending resumes once it is renewed
per_user_marketing_cap429Meta limits how many marketing messages one person gets. Try another day
undeliverable_recipient422The number is not on WhatsApp, or cannot receive this message
marketing_excluded422Meta does not deliver marketing to this recipient
spam_rate_limit429Meta has slowed this number down. Stop and send less
rate_limited429Temporary. Retry with backoff — the only retryable Meta error
meta_error502Meta rejected the send for another reason; the message says which

Marketing templates follow campaign rules

A request through the API has no person reviewing it. So a template whose category is Marketing (or cannot be read) goes through the same checks as a campaign: opt-out, your frequency cap, and your number's daily allowance. Utility and authentication templates, and replies inside the 24-hour window, are not affected.

Don't retry everything

Only rate_limited, policy_lookup_failed and internal are worth retrying automatically. Retrying a quality or policy refusal can get your number restricted by Meta.