{
  "$schema": "https://www.tryklaros.com/schemas/wa-error-corpus-v1.json",
  "version": 1,
  "source": "https://www.tryklaros.com/whatsapp-api-errors",
  "graphApiVersion": "v25.0",
  "license": "CC-BY-4.0",
  "attribution": "Klaros (tryklaros.com)",
  "description": "Meta WhatsApp Cloud API error codes with production retry semantics: whether to retry, suppress the contact, switch to a template, or raise an account-level alert. Handling flags are generated from the same classifier used on the live send path.",
  "note": "Codes 130429, 131048 and 131056 are throughput caps returned as HTTP 4xx rather than 429. Applying a generic \"4xx is not retryable\" rule fails these permanently.",
  "count": 16,
  "codes": [
    {
      "code": 100,
      "title": "Invalid parameter",
      "httpStatus": 400,
      "cause": "A catch-all Meta returns for dozens of distinct problems. The generic message is never the real explanation: read error_data.details or error_user_msg, which carry the actual cause.",
      "fix": "Parse error_data.details rather than error.message. Fix the named field and resend; the same request will fail identically until it changes.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 190,
      "title": "Access token expired or invalid",
      "httpStatus": 401,
      "cause": "The token itself is no longer valid. Every send on this phone number will fail identically until the WABA is reconnected.",
      "fix": "Reconnect the WhatsApp Business Account and store a new token. Do not retry and do not suppress the contact: nothing about this message is wrong, so failing contacts one at a time hides an account-level outage behind thousands of per-contact errors.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": true,
        "rateLimited": false
      }
    },
    {
      "code": 130429,
      "title": "Rate limit hit",
      "httpStatus": 400,
      "cause": "A throughput cap on the account, not a problem with the message. Meta returns it as 4xx rather than 429.",
      "fix": "Back off and resume. Treating this as a client error permanently fails messages that a rolling window would have released within the hour.",
      "handling": {
        "retryable": true,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": true
      }
    },
    {
      "code": 131008,
      "title": "Required parameter missing",
      "httpStatus": 400,
      "cause": "The request omitted a field the endpoint requires for this message type.",
      "fix": "Add the field. Never retry unchanged.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131009,
      "title": "Parameter value not valid",
      "httpStatus": 400,
      "cause": "A field is present but its value is rejected, commonly a malformed number or an out-of-range enum.",
      "fix": "Correct the value. Retrying the identical payload cannot succeed.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131026,
      "title": "Message undeliverable",
      "httpStatus": 400,
      "cause": "The recipient cannot receive the message: not a WhatsApp user, or a number that cannot be reached on this platform.",
      "fix": "Suppress the contact rather than retrying. Repeated sends to an unreachable number cost quota and push the number quality rating down.",
      "handling": {
        "retryable": false,
        "suppressContact": true,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131030,
      "title": "Recipient not in allowed list",
      "httpStatus": 400,
      "cause": "The account is still limited to its test recipient list, which happens on unverified or freshly created numbers.",
      "fix": "Complete business verification, or add the recipient to the allowed list while testing.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131047,
      "title": "Re-engagement message required",
      "httpStatus": 400,
      "cause": "More than 24 hours have passed since the contact last messaged you, so a free-form message is refused. The customer service window has closed.",
      "fix": "Send an approved template instead. Retrying the same free-form message will never succeed, and waiting does not help: only a new inbound message from the contact reopens the window.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": true,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131048,
      "title": "Spam rate limit hit",
      "httpStatus": 400,
      "cause": "Meta is throttling the account because of recipient signals such as blocks and reports. Transient at the account level.",
      "fix": "Back off and resume. Also treat it as a quality warning: the cap is a symptom, and continuing to send at volume through it risks the number rating.",
      "handling": {
        "retryable": true,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": true
      }
    },
    {
      "code": 131051,
      "title": "Unsupported message type",
      "httpStatus": 400,
      "cause": "The message type is not permitted here, often a free-form type sent outside the service window.",
      "fix": "Switch to an approved template.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": true,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 131056,
      "title": "Pair rate limit hit",
      "httpStatus": 400,
      "cause": "Too many messages between this business and this specific contact in a short period. Scoped to the pair, not the account.",
      "fix": "Back off for that contact. Returned as 4xx, so a naive client-error rule fails it permanently.",
      "handling": {
        "retryable": true,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": true
      }
    },
    {
      "code": 132000,
      "title": "Template parameter count mismatch",
      "httpStatus": 400,
      "cause": "The number of variables supplied does not match the number the approved template declares.",
      "fix": "Align the parameter count with the template definition. This is a build-time bug, not a transient failure.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 132001,
      "title": "Template does not exist",
      "httpStatus": 400,
      "cause": "No approved template with that name and language. Frequently a language mismatch rather than a missing template: en and en_US are different templates.",
      "fix": "Check the exact name and language code against the approved list before assuming the template is missing.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 132005,
      "title": "Template hydrated text too long",
      "httpStatus": 400,
      "cause": "The template rendered with its variables exceeds the length limit, even though the template itself was approved.",
      "fix": "Truncate variable values at send time. An approved template is not a guarantee that every substitution fits.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 132007,
      "title": "Template format or character policy violation",
      "httpStatus": 400,
      "cause": "The rendered content breaks a formatting or character policy, commonly through a variable carrying newlines or control characters.",
      "fix": "Sanitise variable values. The template passed review; the substituted content did not.",
      "handling": {
        "retryable": false,
        "suppressContact": false,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    },
    {
      "code": 133010,
      "title": "Phone number not registered",
      "httpStatus": 400,
      "cause": "The sending number has not completed registration on the Cloud API.",
      "fix": "Register the phone number. No message will send until it is.",
      "handling": {
        "retryable": false,
        "suppressContact": true,
        "switchToTemplate": false,
        "accountLevel": false,
        "rateLimited": false
      }
    }
  ]
}