Errors and limits
Error shape
{
"success": false,
"request_id": "req_e097717d1d433831",
"error": {
"code": "recipient_not_opted_in",
"message": "The recipient has not opted in to your messages yet.",
"type": "invalid_request",
"param": "to",
"upstream": { "provider": "meta", "code": 131047 },
"doc_url": "https://www.fiitsa.com/api-docs/erreurs"
}
}
Branch your code on code, never on message. The code is stable and documented; the message may be reworded or translated.
When WhatsApp refuses, upstream.code carries Meta's own error code, which you can look up in their documentation.
Codes
| Code | HTTP | Meaning |
|---|---|---|
missing_api_key | 401 | No key provided |
invalid_api_key | 401 | Unknown or revoked key |
insufficient_scope | 403 | The key lacks the required scope |
plan_required | 403 | The plan does not include API access |
whatsapp_not_connected | 403 | No number linked to the store |
unsupported_field | 400 | Unrecognised or forbidden field |
missing_parameter | 400 | Required parameter missing |
unknown_route | 404 | Route does not exist |
method_not_allowed | 405 | Known route, wrong verb |
meta_rejected | 422 | WhatsApp refused |
rate_limited | 429 | Rate exceeded |
upstream_unavailable | 503 | Temporary unavailability |
gateway_timeout | 504 | No response within the budget |
Rate limiting
60 requests per 10 seconds in burst, 600 per minute sustained.
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. On a 429, honour the Retry-After header.
These limits protect against an accidental loop. The number of messages itself is not capped.
Support
Every response carries X-Fiitsa-Request-Id, also present in the body as request_id. Quote it: it is what lets us trace the exact call, with its internal context.