Errors

Every refusal has the same shape:

JSON
{
  "statusCode": 409,
  "error": "Conflict",
  "message": "This number has not messaged you in the last 24 hours.",
  "code": "window_expired",
  "details": {}
}

Telling one error from another

code is the field that names what went wrong, and it is the one to check. The HTTP status is too coarse on its own: several different errors share a single status, so a 409 alone does not tell you whether a name was taken or a template was frozen.

if (error.code === "window_expired") return sendTemplate();

The request id

Every response carries X-Request-Id, and every SDK error exposes it as requestId. It is the key into our request log. A support ticket with it gets answered; one without it starts with us asking for it.

The catalogue

Authentication

CodeStatusMeaning
invalid_api_key401Missing, malformed, expired or revoked.
insufficient_scope403Valid key, wrong permissions for this route.

Limits and quotas

These four look similar and are fixed in four different places.

CodeStatusFix
rate_limited429Slow down. Clears in seconds; carries Retry-After.
key_limit_reached403This key's monthly cap. Raise the key's limit.
quota_exceeded403Your plan's cap. Upgrade, or free something up.
spend_cap_reached403This key's Meta spend ceiling for the cycle.

Only rate_limited is worth retrying — see Rate limits & quotas. The other three do not refill by waiting.

Delivery

CodeStatusMeaning
window_expired409Outside the 24-hour window. Send a template.
template_not_approved409Meta has not approved it, or has paused it.
template_category_not_allowed409Marketing templates cannot be sent through the API.
no_sender409No active sending number on the account.
verify_not_configured409No Verify template or sender set up yet.
whatsapp_not_connected400The WhatsApp connection needs reconnecting.

Bad input

CodeStatusMeaning
invalid_request400Generic. message says what is wrong.
not_found404No such id on this account.
media_too_large400Over WhatsApp's limit for that file type.
unsupported_media_type400WhatsApp will not carry that type.
template_name_taken409That name is in use. Meta reserves a deleted name for ~30 days.
template_not_editable409Approved or in review — Meta freezes content in both.

Idempotency

CodeStatusMeaning
idempotency_conflict409The key was reused for a different body, or the first request is still running.

The message tells you which: a first request still running is worth retrying with the same key, a key already used for a different body never is.

Server errors

CodeStatusMeaning
meta_error502Meta failed the operation. details has metaErrorCode and fbtraceId.
service_unavailable503Briefly unavailable. Retry; carries Retry-After.

Sandbox

CodeStatusMeaning
test_key_not_allowed403That write reaches Meta permanently, so a test key cannot do it.