Errors
Errors use standard HTTP status codes. The body is always JSON (except where noted) and always includes a message. There are eight shapes you can get back.
At a glance
| Status | Meaning | Body |
|---|---|---|
400 |
Bad request — a malformed Idempotency-Key header |
{ "message": "…" } |
401 |
Missing or invalid token | { "message": "Unauthenticated." } |
402 |
The token's book has no active subscription | { "message": "…" } |
403 |
Missing ability or live permission | { "message": "…", "required_ability": "…" } |
404 |
No such resource in this book | { "message": "" } (blank) |
409 |
Conflicts with the resource's current state | { "message": "…" } |
422 |
Validation failed | { "message": "…", "errors": { … } } |
429 |
Rate limit exceeded | { "message": "Too Many Attempts." } |
400 — Bad request
Returned when a create is sent with a malformed Idempotency-Key header — missing, empty, longer than 255 bytes, or not valid UTF-8. Fix the header and retry. See Idempotency.
401 — Unauthenticated
The bearer token is missing, malformed, expired, or revoked. See Authentication.
402 — Payment required
The administration this token belongs to has no active subscription. The integration cannot fix this itself — the book owner needs to resolve billing in the app.
403 — Forbidden
The token lacks the required ability, the account behind it no longer holds the mapped permission, or access to the book was revoked. On an ability failure the body names it:
{
"message": "This token does not have the 'invoices:write' ability.",
"required_ability": "invoices:write"
}
required_ability is present only on ability failures — a live-permission or access failure omits it.
404 — Not found
No such resource in this administration. The body is deliberately blank ({ "message": "" }) so it never leaks whether an id exists in another book. You also get a 404 for a soft-deleted row on a single-resource GET, and for a feature-gated surface (for example projects) on a book with that feature switched off.
409 — Conflict
The request conflicts with the resource's current state: a locked accounting period, an invalid lifecycle transition (for example editing an invoice that is no longer a draft), or deleting a record that is still referenced.
{ "message": "This invoice can no longer be edited because it is not a draft." }
422 — Validation failed
The payload is well-formed but invalid. errors maps each field to a list of messages — render these straight back to your user.
{
"message": "The name field is required.",
"errors": {
"name": ["The name field is required."]
}
}
429 — Too many requests
You exceeded the rate limit. Back off and retry later. See Rate limits.
{ "message": "Too Many Attempts." }