Errors
Every error is an RFC 9457 problem details body, with the media type
application/problem+json:
{
"type": "https://oscaraccountant.com/errors/period_locked",
"title": "The period is locked",
"status": 409,
"code": "period_locked",
"detail": "Period 2026-03 is locked. Reopen it or post in an open period."
}
| Field | Meaning |
|---|---|
code | Stable and machine-readable. Branch on it in your code |
status | The HTTP status, the same as the status of the response |
title | A short text for a person. It can change |
detail | What went wrong in this request, for a person. It can change |
type | A URL that names the problem: the code after /errors/ |
errors | Only for a validation error: one entry for each invalid field |
A validation error names each field:
{
"type": "https://oscaraccountant.com/errors/validation_error",
"title": "The request is not valid",
"status": 400,
"code": "validation_error",
"detail": "The request has invalid fields.",
"errors": [
{ "field": "lines.0.amount", "code": "required", "detail": "This field is required." },
{
"field": "cursor",
"code": "invalid_cursor",
"detail": "The cursor is not a cursor of this list. Start again from the first page."
}
]
}
field is a dotted path into the request (lines.0.amount), or null for
an error of the whole request.
Status codes
| Status | When | Retry? |
|---|---|---|
| 400 | The request is malformed or not valid | No. Fix the request |
| 401 | No credential, or a credential that is not valid (not_authenticated, authentication_failed) | No. Fix the credential |
| 403 | The caller may not do this (permission_denied, insufficient_scope, human_actor_required) | No |
| 404 | The object does not exist, or the caller cannot see it (not_found) | No |
| 409 | The books are in a state that refuses the change | Not as it is. Read the code |
| 413, 415 | A body or a file is too large (request_too_large, file_too_large) or of a type we do not take (unsupported_file_type) | No |
| 429 | Too many requests (throttled) | Yes, after Retry-After |
| 500 | An error on our side. The detail never repeats internal text | Yes, with the same Idempotency-Key |
| 503 | The books of the company are not available now (books_unavailable) | Yes, later |
Common codes
| Code | Status | Meaning |
|---|---|---|
validation_error | 400 | See errors for each field |
invalid_payload | 400 | The books refused the content of the request |
unbalanced_entry | 400 | The amounts do not balance |
not_authenticated | 401 | No credential |
authentication_failed | 401 | An unknown, revoked or expired API key or token |
permission_denied | 403 | The role, or the client capabilities of the company, do not allow it |
insufficient_scope | 403 | The role allows it, the scopes of the credential do not |
human_actor_required | 403 | A person must make this decision. See Human judgements |
not_found | 404 | No such object, or not visible to the caller |
invalid_state | 409 | The object is not in a state that allows this, for example issuing an invoice twice |
period_locked | 409 | The date is in a closed period. Your accountant decides |
no_open_period | 409 | No open period covers the date |
company_not_ready | 409 | The books of the company are still being set up |
idempotency_conflict | 409 | The Idempotency-Key was used with another request body |
idempotency_in_progress | 409 | The first request with this Idempotency-Key is still running |
request_too_large | 413 | A JSON body is larger than 1 MB |
throttled | 429 | Too many requests |
books_unavailable | 503 | The books cannot be read now |
The API reference lists the codes of each operation. A new code can appear at any time. Handle an unknown code by its status.