Resources

Error codes

All the API's error codes, what they mean, and whether retrying is worthwhile.

Updated November 12, 2026View as Markdown

Every error has the shape { "error": { "code", "message", "details?", "requestId" } }. Base your code on code, never on the text of message. Keep the requestId: it is what lets us find the call.

Language of message: French by default. Send Accept-Language: en to receive it in English; the code never changes. Texts returned by Meta (for example the reason a template was rejected) are not translated.

Code table#

CodeHTTPMeaningRetry?
validation_error400A field is invalid or missing; details lists it.No, fix it first.
unauthorized401Key or session missing, invalid, expired or revoked.No, check the token.
quota_exceeded402Your plan's monthly quota is reached (production).No, change plan or wait for next month.
plan_limit_reached402The plan does not allow more WhatsApp numbers (tier cap), nor, on the default plan, more connected businesses.No: change plan.
forbidden403The key lacks the required scope; the message names it.No, create a key with the right scope.
sandbox_only403Action restricted to sandbox keys.No.
not_found404The resource does not exist, or belongs to another account or environment.No.
conflict409Duplicate (name, external reference, WhatsApp account already connected…) or action not possible in the current state (delivery in progress, endpoint disabled).Depends on the case.
invalid_message422Invalid message: malformed number, empty text, ambiguous media, non-https link…No.
outside_service_window422Free-form message outside the 24-hour window.No: send a template, or wait for the customer to write.
template_not_approved422The template is not approved by Meta.No, wait for approval.
idempotency_conflict422Same Idempotency-Key for a different request.No: use another key.
meta_error502Meta rejected the request or is not responding; the message reflects its response.Yes if it is an outage, no if it is a rejection.
internal_error500Error on our side.Yes, then contact support with the requestId.

Real examples#

400 — validation_error#

Real error · HTTP 400
{
  "error": {
    "code": "validation_error",
    "message": "Invalid request: see details",
    "details": [
      "accountName must be longer than or equal to 2 characters",
      "email must be an email"
    ],
    "requestId": "req_9333db71-3a23-4154-8cd4-3dfc99f1001a"
  }
}

details indicates the field and the rule that was violated.

401 — unauthorized#

Real error · HTTP 401
{
  "error": {
    "code": "unauthorized",
    "message": "API key required (Authorization: Bearer pk_…)",
    "requestId": "req_1c1b9300-f2d4-4387-88b8-7d66222d7497"
  }
}

403 — forbidden#

Real error · HTTP 403
{
  "error": {
    "code": "forbidden",
    "message": "Missing scope on this key: connections:read",
    "requestId": "req_5acc8bd1-f598-4030-8ccd-6eb02dcfadc8"
  }
}

404 — not_found#

Real error · HTTP 404
{
  "error": {
    "code": "not_found",
    "message": "Message not found",
    "requestId": "req_9b02107e-cfba-4b78-81c0-a0fd32068164"
  }
}

409 — conflict#

Real error · HTTP 409
{
  "error": {
    "code": "conflict",
    "message": "An app with this name already exists",
    "requestId": "req_f4a2e5dd-8d8c-4e03-881a-024492da7967"
  }
}

422 — outside_service_window#

Real error · HTTP 422
{
  "error": {
    "code": "outside_service_window",
    "message": "This contact has not written to you in the last 24 hours: only an approved template can be sent to them",
    "requestId": "req_4fdba006-98d4-4164-893d-98389fb54c62"
  }
}

422 — invalid_message#

Real error · HTTP 422
{
  "error": {
    "code": "invalid_message",
    "message": "Invalid recipient number: use the international format with a \"+\", e.g. +237600000000",
    "requestId": "req_72871759-6841-4dcf-8065-3425c4a44633"
  }
}

422 — idempotency_conflict#

Real error · HTTP 422
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This idempotency key has already been used for a different request",
    "requestId": "req_af73c415-1365-41e6-801f-5fc18b1ee2ec"
  }
}

Message delivery errors#

Once the message has been accepted (202), failures no longer appear in an HTTP response: the message moves to failed, with an error that carries Meta's code and message, and a message.failed event is sent to your webhooks.

SituationAutomatic retry?
Meta outage, timeout, rate limitYes, up to 6 attempts with increasing backoff
Permanent rejection (template not found, recipient unreachable, content rejected)No: failed immediately

The numeric codes that appear in error.code are Meta's: see its documentation for their exact meaning.