Reference
Errors
Every failure carries a stable code you can branch on. Branch on the code, never on the message: codes are permanent, wording is not.
The shape
{
"error": {
"code": "missing_variables",
"message": "This template expects 1, 2: missing 2",
"param": "template.variables",
"detail": {
"expected": ["1", "2"],
"missing": ["2"],
"unexpected": []
}
}
}code and message are always there. param names the field at fault when there is one, and detail carries extra structure documented per code, as above.
Authentication
| Code | HTTP | What to do |
|---|---|---|
unauthenticated | 401 | No key was sent. Add the Authorization: Bearer header. |
invalid_key | 401 | The key is not one of ours. Check you deployed the right value. |
key_revoked | 401 | Somebody revoked it. Issue a new one in the console. |
key_expired | 401 | It was created with an expiry that has now passed. |
ip_not_allowed | 403 | This key is restricted to certain addresses. Add yours, or clear the list. |
insufficient_scope | 403 | The key lacks a scope. detail names what was needed and what it has. |
workspace_suspended | 403 | The workspace itself is suspended. Talk to us. |
The request
| Code | HTTP | What to do |
|---|---|---|
invalid_request | 400 | Something is malformed. param and detail.issues say what. |
unknown_field | 400 | You sent a field we do not recognise, usually a typo. The message names it. |
not_found | 404 | No such endpoint, or no such object in this workspace. |
Sending
| Code | HTTP | What to do |
|---|---|---|
no_sender | 400 | This workspace has no connected number. Connect one first. |
sender_not_found | 404 | That sender id does not belong to this workspace. |
template_not_found | 404 | No template by that name, or not in that language. |
template_not_approved | 409 | The template cannot be sent. The message says why. Check sendable when you sync. |
missing_variables | 400 | Your variables do not match the template. detail lists expected, missing and unexpected. |
header_media_required | 400 | The template’s header is an image, video or document, so the send needs template.header_media. requires_media_header on the template listing says which need it. |
header_media_unexpected | 400 | You sent header_media to a template that has no media header. It would have been ignored, so we say so instead. |
window_closed | 409 | A file on its own, to somebody who has not messaged in 24 hours. Retrying will not help; send an approved template with a media header instead. |
recipient_opted_out | 409 | They asked to stop hearing from you. Do not retry. |
recipient_undeliverable | 409 | WhatsApp has marked that number unreachable. Do not retry. |
insufficient_credits | 402 | Not enough balance. detail gives required and available. |
plan_allowance_used_up | 402 | The workspace is on the all-inclusive plan and its allowance for the period is spent. Credits are not used on that plan, so topping up will not help: it has to be raised or renew. detail gives the plan, remaining and allowance. |
plan_required | 402 | The workspace has no active plan, so it cannot send at all. Reads keep working. Nothing the caller can do fixes this; somebody has to start a plan. |
Idempotency
| Code | HTTP | What to do |
|---|---|---|
idempotency_key_reused | 409 | That key was used for a different request. Your keys are not unique enough. |
idempotency_in_progress | 409 | The first request with that key is still running. Retry shortly. |
Limits and outages
| Code | HTTP | What to do |
|---|---|---|
rate_limited | 429 | Slow down. Wait the number of seconds in Retry-After. |
upstream_unavailable | 502 | We could not reach Meta. Retry with backoff. |
internal_error | 500 | Our fault. Retry, and quote the Sendrix-Request-Id if it persists. |
Which ones to retry
A rough rule that will not get you in trouble:
| Status | Retry? |
|---|---|
| 400, 401, 403, 404, 409 | No. Nothing about repeating the same request will change the answer. Fix it or drop it. |
| 402 | No. Top up first, then send again. |
| 429 | Yes, after Retry-After. |
| 500, 502, 503 | Yes, with exponential backoff and an Idempotency-Key. |
Every request id is quotable
Every response carries Sendrix-Request-Id. Log it. When something goes wrong we can look that exact request up, and you can see it yourself under Developers, then Activity, which also groups your recent failures by code.