API Reference
Send WhatsApp
POST /v1/whatsapp — send a transactional WhatsApp message using a pre-approved template.
Send a transactional WhatsApp message to a phone number in international format.
/v1/whatsapp Request
curl https://api.zevsend.com/v1/whatsapp \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"template_id": "tpl_01HXYZ...",
"variables": {
"first_name": "Ada",
"order_id": "ORD-1234"
}
}'
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
to | string | yes | Recipient phone in E.164. |
template_id | string | yes | Template public id (tpl_…). Must be a whatsapp template. |
variables | object | sometimes | Values for the template’s declared customer.* variables. |
brand_domain_id | string | no | Pin the send to a specific verified domain (dom_…) for brand variables like {{brand.name}}. |
Response
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"id": "wam_01HXYZ...",
"to": "+2348012345678",
"status": "sending",
"sandbox": false,
"created_at": "2025-05-24T12:34:56.789Z"
}
Lifecycle
WhatsApp has one more terminal state than email or SMS — a
read event fires when the recipient opens the conversation.
| State | Meaning |
|---|---|
queued | Persisted but not yet handed off. |
sending | Handed off; awaiting accept. |
sent | Accepted and in transit. |
delivered | Confirmed at the recipient’s device. |
read | Recipient opened the conversation. |
failed | Rejected at submit or permanent delivery failure. |
The read state is best-effort. WhatsApp only fires read
receipts when the recipient has read-receipts enabled in their
own settings, so a message that’s been read may never visibly
transition past delivered. Don’t treat the absence of read
as a signal that the message wasn’t seen.
Templates only
Every WhatsApp send must reference a pre-approved template. Raw text isn’t accepted — the upstream network would reject it anyway, so we reject it at the API.
The variable manifest on the template encodes the positional order of placeholders in the approved template body. You pass named variables; we map them to positions at send time. The upstream network renders the message body from the approved template using your variable values.
Pricing
WhatsApp templates carry a per-send price, set on the template in your team’s billing currency. Templates can also have per-country overrides that take precedence over the default rate when sending to those countries. The price you see on the template is what you pay. We never expose which upstream provider handled the send.
See the Pricing guide for the full charge cascade and how Zev Credit is debited.
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_phone | to isn’t a valid E.164 number. |
400 | template_id_required | template_id was missing. |
404 | template_not_found | No such template on this team. |
409 | wrong_template_channel | The template is for email or SMS. |
422 | missing_variable | A required variable was missing. |
422 | unknown_variable | You passed a variable the template doesn’t declare. |
403 | no_approved_brand_domain | You’re live but no approved domain exists for the team. |
403 | recipient_not_allowed | Sandbox-only: recipient hasn’t been verified on this team. |
503 | no_carrier_available | No enabled WhatsApp provider is currently online. |
Conversation windows
WhatsApp enforces 24-hour conversation windows: once a recipient last messaged you, you have 24 hours to send free-form messages back. Outside that window only pre-approved templates of the right category can be delivered. ZevSend is template-only, so this is enforced by design.
Template categories (set when the template is registered):
| Category | Used for |
|---|---|
AUTHENTICATION | OTPs, login codes, verification messages. |
UTILITY | Account updates, order confirmations, shipping notifications. |
MARKETING | Promotional content. Limited per recipient per day. |
Pick the right category when you author the template — picking wrong is the most common reason templates get rejected.
Updated at, Thursday, October 1, 2026