Errors
Handle the WhatsApp API error envelope and its numeric codes safely.
Every failure uses the same envelope:
{ "success": false, "error": { "status": 422, "code": 1001, "message": "Field required.", "doc_url": "https://docs.linqapp.com/channel/whatsapp/error/codes/1xxx/1001/", "param": "/parts/0/body", "details": [ { "code": "field_required", "param": "/parts/0/body", "message": "Body is required." } ] }, "request_id": "req_…"}error.status is the HTTP status the response was written with, error.code
is a number, and error.message is for a human reading a log — it may be
reworded at any time, so never match on it. Every code has a page in the
error code reference, which also carries the
complete tables.
Branch on the code, fall back to the range
Section titled “Branch on the code, fall back to the range”Codes are allocated into five ranges. The range says what kind of failure it
is; together with error.status it says whether the request or the service
was at fault.
| Range | Category | Retry? |
|---|---|---|
| 1xxx | Request errors | No — correct the request first |
| 2xxx | Resource, permission and state errors | No — inspect or reconcile that state |
| 3xxx | Service errors | Yes — bounded backoff, unless the code’s page says otherwise |
| 4xxx | Operational and send refusals | Sometimes — follow the code’s remedy |
| 5xxx | Attachment and file errors | Sometimes — follow the code’s remedy |
The set of codes grows additively. Branch on the integers you know, and treat
an integer you do not know by its range plus the HTTP status that came with
it — a 3xxx with a 503 is retryable, a 1xxx with a 422 is not, and an
integer outside every range is a generic failure. Do not model the codes as a
closed enum. A number is never removed and never reassigned to a different
failure: once it means something, it means that forever, so a branch you
write today stays correct.
Honor retry_after
Section titled “Honor retry_after”When error.retry_after is present, it overrides the range guidance above:
wait that many seconds, then retry. It appears exactly when the response also
carries a Retry-After header, with the same value, so either source is
safe to read. A 3xxx with no retry_after is retried with bounded
exponential backoff, starting at 1 second and capping at 30. Sends are
idempotent on Idempotency-Key, so retrying a send that actually succeeded
returns the original result rather than sending twice.
Read the optional members
Section titled “Read the optional members”paramis present only when the response is about exactly one field and is that field’s JSON Pointer. A fault about the whole body points at the root, which is the empty string, soparammay be present and empty.detailslists every field-level finding on a validation failure. Each entry has a stringcode, a JSON Pointerparamnaming the most specific request location the API could identify, and amessage. Validation rejects and points; it never coerces a value into something acceptable.contextcarries typed facts about the failure you may need to branch on, such as whether a customer window ever opened. Its members are documented on the codes that set them.doc_urlis the absolute address of the code’s page in the error code reference, present on every response whose code has a published page and absent for a code reachable only on a surface that has not been released yet; it is derived from the code alone, so link to it rather than building the address yourself.trace_idappears when a valid trace context exists.
None of these is guaranteed on every error.
Log what support needs
Section titled “Log what support needs”Log the HTTP status, request_id, and error.code. Never log credentials or
message content. Include request_id when contacting Linq support; it
identifies the exact request in our logs.
These codes belong to WhatsApp. Every channel numbers its own errors, so the
same integer means something different on another channel — branch on the
code only in code paths you know are talking to this API, and follow the
doc_url it gives you rather than one from another channel.