Errors

Every error response from /api/v1/* carries a stable, machine-readable code field. Partners filter on code, not on HTTP status (statuses are shared across multiple codes) and not on the message (which is localized). The code space is append-only: a code that ships never gets renamed, renumbered, or removed.

Envelope

A typical 400 response:

json
{
"code": "VALIDATION_FAILED",
"message": "Name is required.",
"errors": { "name": ["Name is required."] }
}
  • code: one of the values in the catalog below. Filter on this.
  • message: a human-readable description, localized to the caller’s Accept-Language (defaults to hr). Do not parse this programmatically.
  • errors: present on VALIDATION_FAILED responses. A dictionary keyed by field name with an array of localized messages per field.

Server errors (HTTP 500) omit the errors dictionary and carry a generic message. Retry with back-off; if the error persists, contact support with the request id (visible in the response X-Request-Id header where present).

Catalog

Code HTTP When it fires Partner response
VALIDATION_FAILED 400 Request body failed validation, or a required header is missing. Inspect the errors dictionary. Surface field-level messages to the end user.
EXCHANGE_RATE_UNAVAILABLE 400 The tenant’s FX provider publishes no rate for the currency on the requested date, and no historical rate exists to fall back on. A validation-family failure with a field-keyed errors body. Prompt the user for a manual rate, or pick a currency the provider prices.
UNAUTHORIZED 401 The API key is missing, malformed, or revoked. Check the Authorization header. Re-issue the key if it was revoked.
API_ACCESS_NOT_ENABLED 402 The target company does not have the ApiAccess subscription feature. Upgrade the company to Enterprise, or add the ApiAccess add-on, in the billing settings.
FORBIDDEN 403 Authenticated, but the key’s issuing user lacks permission for the operation in this scope. Check the acting user’s role in the target company. Re-issue the key as an admin.
NOT_FOUND 404 The target resource does not exist or has been deleted. Verify the id. The resource may have been removed by another actor.
CONFLICT 409 Duplicate resource, stale precondition, or an idempotency-key replay with a different body. Reconcile state. Retry may succeed with a different payload, or may require no-op.
RATE_LIMIT_EXCEEDED 429 Per-key rate limit window (per-second, per-hour, or per-day) was exceeded. Back off per the Retry-After header. See Rate limits.
KEY_REVOKED 401 Reserved for a future custom challenge handler. Not currently emitted. Pre-wire your handler. Today the revoked-key flow returns a bare HTTP 401.
INTERNAL_ERROR 500 An unhandled server error. Retry with exponential back-off. If the error persists, contact support.

Append-only guarantee

Once shipped, a code stays in the catalog forever, even if its emitter is deleted. Partners can rely on their code switch statements never silently breaking because locco renamed a value. If behavior around a code changes (different HTTP status, new constraint) it is called out in the changelog; the code string itself remains stable.

Partners are encouraged to handle unknown codes defensively: if a new code ships before your client is updated, treat it as a generic failure (probably INTERNAL_ERROR-shaped) and log the raw response for later review rather than crashing.