Skip to content

Errors and limits

Every failure returns the same envelope. Branch on code, never on message.

{
  "error": {
    "code": "validation_failed",
    "message": "Check the highlighted fields",
    "fields": {
      "registrationNumber": "invalid",
      "amount.currency": "unsupported"
    }
  }
}

fields appears only on validation_failed. Keys are dotted paths into the request body, so they map directly onto form fields.

Error codes

Status Code Meaning
400 bad_request Malformed JSON, or a body over 256 KB
401 unauthenticated Missing, expired, or invalid token
403 forbidden Token lacks the scope for this call
403 consent_required No consent on record for this client. Send the consent object or onboard the client first
404 not_found No such client or invoice
409 client_exists Registration number already registered. Existing clientId is in the body
409 already_submitted Onboarding was already submitted
409 not_financeable Invoice is settled, credited, or already financed
409 terms_not_available Insufficient history, or client not yet active. reason says which
422 validation_failed Field-level problems, enumerated in fields
429 rate_limited Too many requests. Honour Retry-After
500 internal_error Our fault. Safe to retry with the same idempotency key
503 signing_unavailable Signing provider unreachable. Retry — nothing was consumed

Which errors are worth retrying

Behaviour Codes
Retry with backoff rate_limited, internal_error, signing_unavailable
Fix, then resend validation_failed, bad_request
Treat as success client_exists, already_submitted — the work is already done
Do not retry forbidden, not_found, not_financeable

consent_required is worth calling out separately: the request itself is well-formed and will succeed unchanged once consent exists. Hold the invoice and resend after the client signs, rather than discarding it.

Always retry with the same Idempotency-Key. A new key on a retry is how duplicates get created.

Validation field codes

The values inside fields are stable machine codes, suitable for mapping to your own messages.

Code Meaning
required Field is missing or empty
invalid Present but malformed — wrong shape, pattern, or length
unsupported A valid value we do not accept here, e.g. a non-EUR currency
out_of_range Numeric or date value outside the permitted bounds
before_issue_date A dueDate or paymentDate earlier than issueDate
inconsistent Valid alone, but contradicts another field

Rate limits

Surface Limit
Reads 600 per minute
Writes 120 per minute
Bulk invoice jobs 10 concurrent per partner
Token requests 60 per minute

Every response carries:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1789412460

X-RateLimit-Reset is a Unix timestamp. On 429, honour Retry-After rather than reading the reset header.

Bulk loads bypass the write limit

Sending twelve months of history through a loop over POST /invoices will hit the write limit within seconds. Use POST /invoices/batch instead — it is exempt, and it is an order of magnitude faster.

Request size

Bodies are capped at 256 KB. In practice this only matters for POST /invoices/batch; if a batch exceeds it, split into two calls rather than trimming fields.

Timeouts

Operation Typical Timeout
Reads under 200 ms 10 s
Writes under 500 ms 10 s
Onboarding submit 1–3 s (calls the signing provider) 30 s
Batch accept under 1 s (processing is async) 30 s

Set your client timeout to at least 30 seconds on onboarding submit. A client-side timeout there can leave a signing contract created without you knowing — replay the same idempotency key to recover the result rather than submitting again.