Errors
Every failure comes back as JSON with the same two keys, whatever went wrong:
{
"message": "Falta la lada del país. Manda el número como «+525555555555», o añade «phone_country» (por ejemplo «MX»). Sin lada no podemos enviarle WhatsApp ni llamarle.",
"errors": {
"phone": ["Falta la lada del país. Manda el número como «+525555555555», o añade «phone_country» (por ejemplo «MX»). Sin lada no podemos enviarle WhatsApp ni llamarle."]
}
}
messageis one sentence, safe to log and safe to show a person.errorsmaps each field that failed to the reasons it failed. It is present on every4xxthis API produces, so you can readerrorswithout checking first.- Messages are in Spanish by default, because the people who read them in the
CRM are. Send
Accept-Language: enfor English where a translation exists. Never match on the text: match on the status code and the field name.
The codes
| Code | What happened | Retry? |
|---|---|---|
400 |
A required header is missing — in practice, Idempotency-Key on an endpoint that demands it |
Only after fixing the request |
401 |
No token, an expired token, or one that was revoked | No. Issue a new token |
403 |
The token is valid but lacks the ability for this location, or the role cannot do this | No. See Authenticating requests |
404 |
Nothing here matches — including a record that exists in another location | No. Check the reference |
409 |
Two calls raced on the same Idempotency-Key, or a person here already closed the deal |
No. Read the body before deciding |
422 |
The request was understood and rejected. Read errors |
Only after fixing the payload |
429 |
Over the rate limit | Yes, after Retry-After |
5xx |
Our fault | Yes, with backoff |
Why a 404 and not a 403
A record that belongs to another location answers 404, exactly as if it had
never existed. Answering 403 would confirm that the id is real, which is
information an outsider should not be able to collect one request at a time.
So 404 means "not yours or not there" and there is no way to tell the two
apart. That is deliberate.
What to do with a 429
Every response carries X-RateLimit-Limit and X-RateLimit-Remaining, so you
can slow down before being cut off rather than after. When you do go over,
the 429 carries Retry-After in seconds: wait that long, then continue.
Do not retry a 429 immediately, and do not spread the same work across more
tokens to get around it — the budget is per token precisely so that one runaway
loop cannot starve the rest of your integration.
What is safe to retry
5xxand429are always safe to retry. Nothing was recorded.4xxis never worth retrying unchanged. The same request will fail the same way; something in it has to change first.- Anything that moves money must carry an
Idempotency-Keyso that a retry after a timeout — where you never saw the answer — cannot charge twice. See Idempotency.
A timeout is the one case where you do not know which of the two you had. Retry it with the same key: if the first call landed, you get its answer back instead of doing the work again.