Handle API errors, limits, and retries
Interpret Waaru Developer API error codes, rate-limit headers, admission failures, and uncertain send outcomes without duplicating customer messages.
Reviewed
Send HTTPS requests with a Bearer key. For message writes use Content-Type: application/json and the supported message payload. Check the HTTP status before interpreting the body: message acceptance is 202, reads are 200, and successful media reads contain bytes.
Read the error envelope
Errors include a stable code and a human-readable message. Some include additional safe details. Branch on the status and code rather than matching the message text.
{
"code": "service_window_closed",
"message": "The customer-service window is closed. Use an approved template."
}| Status and code | Next action |
|---|---|
400 invalid_message | Correct the payload shape, recipient, or field limit. Unknown message fields are rejected. |
400 invalid_pagination or invalid_cursor | Use only the supported list parameters and the returned opaque cursor. |
401 unauthorized or invalid_api_key | Check the token, rotation, and revocation state. |
403 insufficient_scope | Add only the scope required for this operation. |
403 api_key_not_allowed | Choose a supported external endpoint. Dashboard routes are not generally API-key routes. |
404 message_not_found, conversation_not_found, or media_not_found | Check the identifier and the key's number. The resource may be unavailable within that scope. |
404 template_not_found | Create or sync the exact template name and language for the sending account in the dashboard. |
409 instance_not_api_managed | Choose Developer API handling for the bound number. |
409 contact_unavailable | The contact was deleted or is unavailable. Review the contact record before sending. |
409 message_not_allowed | Another admission policy blocks this message. Review the error and current conversation state. |
503 temporarily_unavailable | Delay and respect retryAfterSeconds when returned. Do not bypass an unavailable safety check. |
409 instance_unavailable | Restore the sender connection. |
409 conversation_owned_by_human | Coordinate with the Inbox owner. Use Release to API when a person decides the server should resume. |
409 service_window_closed | Use an eligible approved template or wait for a new customer message. |
409 template_not_approved or quality_blocked | Check template approval and current sender/template quality. Do not bypass admission with another key. |
409 contact_opted_out, contact_instance_restricted, or contact_marketing_restricted | Respect the restriction. Review its exact scope in the dashboard. |
429 | Reduce request volume and wait for Retry-After when supplied. For media_concurrency_exceeded, also close active downloads. |
503 backlog_full | Admission is temporarily at capacity. Delay further sends. |
Other 5xx or network timeout | Reconcile the outcome before retrying a write. |
An accepted message can fail later. Check its message status or message.failed callback as well as the initial HTTP response.
Respect rate limits
Read RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset when returned. Reset is a wait in seconds, not an absolute timestamp. A request-rate 429 includes Retry-After. Safety services can return 503 instead of allowing unbounded traffic.
Current Developer API request budgets are:
| Request class | Burst | Sustained |
|---|---|---|
| Sends | 20 per second per key and per number | 600 per minute per key and per number; 1,200 per minute across the workspace. |
| Reads | 60 per second per key | 3,000 per minute per key. |
Additional network-level limits and media-download concurrency checks apply. These request budgets do not promise WhatsApp delivery throughput or override Meta quality and capacity limits. Follow the returned limiting headers when several controls apply.
Retry without duplicate messages
Retry safe reads with bounded exponential backoff and jitter. Respect Retry-After instead of retrying immediately.
POST /v1/messages has no public client idempotency contract. A timeout does not prove rejection. Store your send attempt and any returned messageId; query that message, inspect the conversation, and reconcile callbacks before deciding to send again. Do not attach an invented Idempotency-Key header and assume it prevents duplicates.
For help, record the request time, route, status, stable error code, and returned X-Waaru-Request-Id if present. Share sensitive contact or conversation details only through the appropriate support flow, and never include the API key or signing secret.
Receive and verify Developer API webhooks
Configure a Waaru callback URL, select message events, verify signed raw requests in Node.js, deduplicate delivery, and recover paused or failing webhooks.
Developer API endpoint reference
Find the supported Waaru external API routes, required scopes, success responses, and links to message, conversation, pagination, and media examples.