Guide
Every failure names itself.
The shape is {"error":"<code>","message":"…"}, and "details":[…] when a validator rejected more than one thing. Match on error, never on message — the prose may improve, the code will not change.
400 your request; 401 the token; 403 your role or your scope; 404 no such row for you (not-found and not-yours are deliberately the same answer, so the API cannot be used to discover whether an id exists); 409 a real-world conflict, and the body says what; 422 the courier refused and the message is theirs; 5xx ours.
One deliberate exception: /v1/documents/* answers {"message":"…"} with no error key on 400/404, and {"code":"…","message":"…"} on 409.