Errors

Every refusal is a JSON body with a single message, and a status code that says what kind of thing went wrong.

{ "message": "This credential may not call from this address." }

401 — the caller is not who it says

MessageWhat it means
The client id and secret do not match.The exchange was given a client id or secret that does not exist
This credential is revoked or has expired.Revoked on the screen, or past its expiry date
The refresh token is no longer valid.Refreshed twice with one token, or it has expired
The account that owns this credential no longer exists.The creator’s account was removed

403 — the caller is known, but not allowed

MessageWhat it means
This token was not issued by an API credential.The bearer token predates the credential exchange, or is not from this API
This credential is revoked or has expired.Same credential, seen on a normal call rather than the exchange
This credential may not call from this address.The call’s address is outside the credential’s allowlist
This credential was not issued for organisation {id}.The organisation on the route is not one of the companies ticked on the credential — a write. A read answers 404 instead, so it never confirms that another company exists
This credential may not write {kind}.The credential holds no {kind}.write scope
This credential does not hold the {scope} scope.A read whose credential holds no {resource}.read scope
This account may no longer administer data for this organisation.The account behind the credential lost its data-admin right on that organisation
This account may no longer report on this organisation.A read, where the account behind the credential lost its reporting right on that organisation
This token may not write.The token was issued without the write ability
This token may not read.The token was issued without the read ability

404 — the path or the record does not exist

A read never confirms that a company it may not read exists: a company that is not one of the credential's answers the same 404 as one that does not exist at all.

{ "message": "No such organisation.", "code": "not_found" }

The schema endpoint answers a kind it does not carry by name, and lists the kinds it does carry:

{ "message": "No such kind.",
  "kinds": ["accounts", "trial_balance", "invoices", "bills", "transactions", "operation_data"] }

422 — the request was refused on its contents

The write endpoints refuse an invalid body in the same shape as the schema's own validation:

{ "message": "The request was refused.",
  "errors": [{ "index": 3, "field": "records.3.amount_base", "code": "invalid_type", "detail": "…" }] }

A read refuses a parameter in the same envelope, with the field that was wrong and no record index, because a read has no records:

{ "message": "The request was refused.",
  "errors": [{ "field": "number_of_months", "code": "invalid_value",
               "detail": "0 is not a number of months. Ask for 1 to 120." }] }

A read uses the codes the write side publishes: invalid_value for a value outside what the endpoint takes, required_field_missing for one it needs - section on a detail read - and unknown_field for a parameter the resource does not take, which is refused rather than ignored so no caller believes a filter applied.

A body that passed its checks and then failed while writing answers with the code that failed, and stays readable at its own URL:

{ "message": "The request failed.",
  "failure": { "code": "journal_does_not_balance", "errors": [ … ] } }

429 — too many requests

Token requests are limited to 10 a minute per caller. Data requests are limited per credential, at the group's own requests-per-minute figure — see Limits.