Errors
The LedgerBee API uses conventional HTTP status codes and returns every error
as a JSON envelope. The status code tells you whether retrying can help; the
machine-readable code tells you exactly what went wrong.
The error envelope
Every error body carries at least statusCode, timestamp, path, and
message. Most errors also include code and details:
Code
| Field | Always present | Description |
|---|---|---|
statusCode | Yes | The HTTP status, repeated in the body. |
timestamp | Yes | ISO 8601 time the error was produced. |
path | Yes | The request path, relative to the /api mount (e.g. /v1/customers). |
message | Yes | Human-readable summary. For logging only — its wording may change. |
code | No | Stable machine-readable identifier (e.g. UNAUTHORIZED, LICENSE.REQUIRED). Branch on this, never on message. |
details | No | Context for the specific code — failing fields, required scopes, the missing license key. |
Validation errors (400)
Request bodies are validated strictly: unknown fields are rejected, not
ignored. A failed validation returns code: "SHARED_VALIDATION_FAILED" with
one details.validationErrors entry per failing field:
property— dot-separated path to the field, including array indices (e.g.lines.0.quantity)constraints— map of failed rule name to a human-readable explanation
Status codes
| Status | When |
|---|---|
400 Bad Request | Validation failed (above), or the request violates a business rule — the code says which. |
401 Unauthorized | Missing, invalid, expired, or IP-restricted API key / OAuth token. The body has code: "UNAUTHORIZED" and message: "Invalid API key". See Authentication. |
403 Forbidden | The credential is valid but lacks a required scope (code: "INSUFFICIENT_PERMISSIONS", with details.requiredScopes), or your company lacks a required license (code: "LICENSE.REQUIRED", with details.licenseKey). See Authentication for scopes. |
404 Not Found | No such route, or the referenced resource doesn't exist in your company. |
413 Payload Too Large | The request body exceeds the size limit — message states the received size and the limit. |
429 Too Many Requests | You hit a rate limit. Back off and retry — see Rate limits. |
500 Internal Server Error | Something failed on our side. message is a generic Internal Server Error — retry with backoff and contact support if it persists. |
Requests authenticated with an OAuth bearer token also receive a standard
WWW-Authenticate challenge header on 401/403
(error="invalid_token" vs error="insufficient_scope"), so OAuth and MCP
clients know whether to refresh the token or re-consent.
Request IDs
Every response, success or error, carries an X-Request-ID header with a
UUID unique to that request. The same ID is stamped on our server-side traces.
Include it in support requests so we can locate the exact request in our logs.
Code
Error codes are stable
code values such as SHARED_VALIDATION_FAILED, UNAUTHORIZED, and
LICENSE.REQUIRED are stable identifiers, safe to branch on. They are never
renamed or repurposed; message text may be reworded at any time. New codes
may be added over time, so treat any unrecognized code as a generic failure
for its status code.