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

json
{
  "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

CodeHTTPWhat to do
unauthenticated401No key was sent. Add the Authorization: Bearer header.
invalid_key401The key is not one of ours. Check you deployed the right value.
key_revoked401Somebody revoked it. Issue a new one in the console.
key_expired401It was created with an expiry that has now passed.
ip_not_allowed403This key is restricted to certain addresses. Add yours, or clear the list.
insufficient_scope403The key lacks a scope. detail names what was needed and what it has.
workspace_suspended403The workspace itself is suspended. Talk to us.

The request

CodeHTTPWhat to do
invalid_request400Something is malformed. param and detail.issues say what.
unknown_field400You sent a field we do not recognise, usually a typo. The message names it.
not_found404No such endpoint, or no such object in this workspace.

Sending

CodeHTTPWhat to do
no_sender400This workspace has no connected number. Connect one first.
sender_not_found404That sender id does not belong to this workspace.
template_not_found404No template by that name, or not in that language.
template_not_approved409The template cannot be sent. The message says why. Check sendable when you sync.
missing_variables400Your variables do not match the template. detail lists expected, missing and unexpected.
header_media_required400The 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_unexpected400You sent header_media to a template that has no media header. It would have been ignored, so we say so instead.
window_closed409A 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_out409They asked to stop hearing from you. Do not retry.
recipient_undeliverable409WhatsApp has marked that number unreachable. Do not retry.
insufficient_credits402Not enough balance. detail gives required and available.
plan_allowance_used_up402The 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_required402The 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

CodeHTTPWhat to do
idempotency_key_reused409That key was used for a different request. Your keys are not unique enough.
idempotency_in_progress409The first request with that key is still running. Retry shortly.

Limits and outages

CodeHTTPWhat to do
rate_limited429Slow down. Wait the number of seconds in Retry-After.
upstream_unavailable502We could not reach Meta. Retry with backoff.
internal_error500Our 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:

StatusRetry?
400, 401, 403, 404, 409No. Nothing about repeating the same request will change the answer. Fix it or drop it.
402No. Top up first, then send again.
429Yes, after Retry-After.
500, 502, 503Yes, 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.