Errors
Every error has the same shape and a stable code to branch on.
The error object
{
"error": {
"code": "validation_error",
"message": "Check these fields: content.text.",
"details": { "fields": ["content.text"] }
}
}
Branch on code, never on message, which may change. provider_code is Meta's own number and details holds what you can act on, such as details.fields for a validation error. Both appear only when there is something to say.
Codes
| Status | Codes |
|---|---|
| 400 | idempotency_key_required, invalid_cursor |
| 401 | unauthorized |
| 403 | insufficient_scope, forbidden |
| 404 | not_found |
| 409 | idempotency_conflict, idempotency_in_progress, channel_disconnected, customer_id_taken, contact_deleted, tag_exists |
| 422 | validation_error, window_closed, contact_blocked, media_invalid, template_invalid, customer_id_invalid |
| 503 | unavailable |
Failed sends
A send that Meta refuses after the 202 does not come back as an HTTP error. The message turns rejected or failed and its error holds the code. Omnio retries only rate limits and Meta outages, up to five tries.
| Meta code | error.code | Retried |
|---|---|---|
| 131047 | window_closed | No, send a template |
| 131026 | undeliverable | No |
| 131056 | recipient_rate_limited | Yes |
| 130429 | throughput_exceeded | Yes |
| 131048 | spam_rate_limited | No |
| 132000, 132001 | template_invalid | No |
| 131053 | media_error | No |
| 190 | channel_disconnected | No, sends wait until the channel reconnects |
Any other Meta code reads meta_ followed by the number.
Rate limits
Omnio does not rate limit API keys today. WhatsApp paces each number, so Omnio queues sends and spaces them out for you.