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." }
Something inaccurate or missing? support@billey.nl