Conventions
The rules every endpoint follows — read once, rely on everywhere.
The rules that hold everywhere. Learn them once; every surface obeys them.
Identity and shape
- UUIDs only. Every resource id is a UUID, stable for the resource's lifetime.
General-ledger lines use a stable text id (
lineId) that survives re-imports. - JSON in, JSON out (
application/json); errors are RFC 9457application/problem+json(see the error catalog). - Money is decimal. Amounts are JSON numbers with explicit decimals, signed where the domain is signed (bank debits negative). Parse as decimal, never float.
- Dates are ISO 8601 (
2026-03-01); timestamps are UTC ISO 8601 instants.
Reach
Your token defines your reach: a system client sees its organization subtree; a per-user or interactive principal sees what that human can access. Anything outside your reach is a 404 — never a 403 — whether it exists or not. Resource ids are not guessable capabilities.
Pagination
Lists return:
{ "data": [ ... ], "pagination": { "next_cursor": "…", "has_more": true } }
Follow next_cursor until null. Cursors are opaque, signed, and bound to your
client and the exact filters — reuse with changed filters is invalid_cursor
(restart from page one). page_size accepts 1–200, default 50. Small bounded lists
(chart of accounts, VAT terms, users) return everything with a null cursor.
Idempotency
Every mutation requires an Idempotency-Key header (any unique string; use a UUID
per logical action). Retrying with the same key returns the stored response,
byte-identical, flagged with Idempotency-Replayed: true. The same key with a
different body is idempotency_key_reused (422). This makes timeouts safe: when in
doubt, retry with the same key.
Async work
Heavy operations return 202 with a job; poll GET /v1/jobs/{id} until the status
is terminal, or subscribe to job.succeeded/job.failed webhooks.
One job per (type, scope) is in flight at a time — a duplicate submission is a 409
whose jobId extension names the blocking job.
Rate limits
Per-client quotas by plan: standard 300 requests/min (burst 60), premium
1200/min (burst 240). 429 responses carry Retry-After. Design for backoff; for
bulk reads prefer larger page_size over more requests.
Versioning and deprecation
The API is /v1 and — from GA — additive-only: new fields, endpoints, and event types may
appear at any time — build tolerant readers. Anything scheduled for removal is
announced in the changelog, marked with Deprecation and
Sunset response headers, and kept working for at least 12 months after the
sunset announcement (the full policy: deprecation-policy).
Request tracing
Every response carries a requestId. Include it in support requests — it links
directly to our logs and audit trail.