Skip to main content
The OpenAPI schema for this reference declares one error shape for almost every failure response:
That is the declared contract. No endpoint in the running application actually produces it. Every error this API returns is raised by one of four generic handlers in RequestExceptionHandler, and each of those renders a narrower shape. The declared schema is a leftover; the shapes below are what your parser will meet.

The shapes you’ll actually see

A single error string, no array:
This is what a missing record, a missing or invalid token, or a hand-checked permission failure returns — not the description/errors shape above.
Don’t hard-parse errors[0].field. Nothing returns it. Where an errors key does appear it holds plain strings, so errors[0] is a sentence, not an object. Read error, then message, then errors[0], and treat each as a string. All three shapes above are live responses from this API, captured against a running server.

Status codes in use

401 and 403 don’t split the way you’d expect. Most authorization failures — a valid token whose role just doesn’t permit the action — return 401, not 403, because the app’s generic policy-failure handler renders them as unauthorized regardless of whether the token itself was valid. A literal 403 mostly shows up on a handful of specific endpoints that check administrator by hand and reject with their own message. Don’t treat 401 as proof the token itself is bad — check the message.
404 is also the cross-tenant response. Every account-scoped resource is looked up through that account’s own association, never by a bare global ID. Requesting /api/v1/accounts/1/conversations/999 with a token that belongs to account 2 returns 404, identical to conversation 999 not existing at all. That’s deliberate — it avoids confirming a resource exists somewhere you can’t reach — but it means a 404 here doesn’t always mean “never existed.”
There’s no documented 5xx contract. If you get one, it isn’t validation feedback of any kind — treat it as transient and retry with backoff rather than trying to parse it as either error shape above.

A real example

Next

Idempotency

Which of these are safe to trigger again after a failure.

Rate limits

The one status code with a body shape that’s different again.