Errors & limits
Every error has the same shape:
{ "error": "invalid_from_handle", "message": "from_handle is not a number assigned to your account", "request_id": "req_…" }error is a stable machine-readable code (branch on this, not the HTTP status or the human message); message is human-readable and may change. request_id is on every response — success or error — and is mirrored by the X-Request-Id header; quote it when reporting a problem. A 5xx never includes internal detail.
Common codes
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request body, query, or path failed validation — a missing, unknown, or malformed field. |
| 400 | invalid_handle | to / from_handle is not a valid E.164 number or iMessage email. |
| 400 | invalid_from_handle | The from number is not assigned to your account (or has no connected WhatsApp session when channel is forced). |
| 400 | invalid_attachments | An attachment_id is unknown or already used by another message. |
| 400 | invalid_url | A webhook url isn’t a public http(s) endpoint (private, loopback, or link-local addresses are rejected). |
| 401 | unauthorized | Missing or invalid API key. |
| 403 | forbidden | Valid key, inactive account. |
| 404 | not_found | The resource doesn’t exist — or belongs to a different account. |
| 409 | conflict, not_a_group | The action doesn’t apply: editing an SMS, reacting before delivery, group ops on a DM, retrying a non-failed message. |
| 429 | rate_limited | Rate limit exceeded; retry after the window resets. |
| 502 / 503 | *_failed, device_offline, unavailable | A synchronous upstream action failed or the sending number is temporarily offline. Safe to retry. |
Rate limits
- API requests are limited per key (default 120 requests/minute). Exceeding it returns
429. - Outbound message throughput is additionally paced per sending number to protect deliverability — bursts queue rather than fail, so a
202means the message will go out as capacity allows.
Retries
- Always send with an
Idempotency-Keyso network-level retries can’t double-send. 5xxresponses and429are safe to retry with backoff.4xx(other than 429) are not — fix the request.