The error envelope
Most error responses look like:| Field | When present | Notes |
|---|---|---|
message | Always | Dotted-path i18n key, always starts with error.. |
error_code | Sometimes | Stable machine code for grouping/logging. |
error_subcode | Sometimes | Further qualification. |
validation_errors[] | On 400 from validating endpoints | Per-field or per-row codes. |
validation_errors array (notably file uploads). Always check both the
status code and the body.
Status codes
| Status | Meaning | Action |
|---|---|---|
200–204 | Success. 204 No Content is common for state-change endpoints — there is no body. | Trust the success. |
400 Validation Error | The request is malformed or fails server-side validation. | Surface validation_errors[] or message. Don’t retry. |
401 | Auth missing/invalid/expired. | Refresh token if proactive failed, else force re-auth. |
403 | Authorised but not allowed (wrong partner, missing permission). | Check X-Preferred-Partner-Id and GET /permissions. |
404 | Resource not found. | For lists: empty state. For detail: redirect. |
409 | Conflict (concurrent edit, duplicate). | Refresh state and try again. |
422 | Semantically invalid (often the CR API on save). | Surface validation_errors. |
429 | Rate limited. | Back off — see Rate limits. |
5xx | Server error. | Retry with backoff. Log x-request-id. |
Mapping error.<code> to user-facing copy
The message field is an i18n key. Map it to your own copy:
error.internal.unspecified to a
user.
A typed error wrapper
Retry with exponential backoff
Logging and support
Always log thex-request-id response header on any failure:
Surfacing validation errors in forms
For 400 withvalidation_errors[], map field-level codes back to inputs:
field isn’t present, surface a form-level error instead. Don’t drop
the response on the floor.
Common mistakes
Retrying on 4xx
Retrying on 4xx
Wastes calls and can violate idempotency for non-GET endpoints. Retry
only on
5xx, 429, and network errors.Showing `error.foo.bar` to the user
Showing `error.foo.bar` to the user
The dotted keys are for machines. Always map to human-readable copy
through your i18n layer.
Catching `AbortError` as a real error
Catching `AbortError` as a real error
Aborted fetches throw
AbortError. They are expected on filter
changes and unmounts — swallow them silently.Treating 204 as failure
Treating 204 as failure
Many state-change endpoints (e.g.
PUT /events/{eid}/setup_frontend_status,
PUT /events/{eid}/init_campaigns_setup) return 204 No Content. Your
client must accept a body-less 2xx as success.