Conventions & errors
Field conventions
snake_casenames; dates are ISO-8601 (YYYY-MM-DD), timestamps RFC 3339.- A field with no value is an explicit
null— keys are never omitted. - Transaction ids are random strings (other resources use integer ids). Always use ids exactly as the API returned them.
- Amounts are flat numeric fields next to a
currencyfield, like a bank statement.*_eurfields (amount_eur,gross_eur,net_eur) carry the EUR equivalent — use those when summing across currencies.
Split transactions
Only top-level transactions (real bank movements) appear in lists. A split
transaction (is_split: true) carries its allocation pieces in children,
each with its own amount and category. The children always sum exactly to the
parent amount, so:
For cashflow totals, sum parent amounts only. Never add children on top — that double-counts.
Requesting a child's id on GET /transactions/{id} returns its parent
(with the child nested) — payment records on expenses reference child pieces,
and following that reference lands on the real bank movement.
Pagination
List endpoints return:
Code
To get the next page, pass next_cursor back as ?cursor=… exactly as
received. null means you have everything. Check next_cursor, not the page
size: a page can contain fewer than limit rows while more pages exist.
Rate limits
60 requests/minute per token, refilled continuously at 1/second. Every
response carries X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset; a 429 adds Retry-After (seconds). Honor it and retry.
Errors
Every non-2xx response has the same body:
Code
Branch on code (stable contract); message text may change. Quote
request_id (also on the X-Request-Id header) when contacting support.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token | No/malformed Authorization header |
| 401 | invalid_token | Token unknown |
| 401 | token_revoked | Token was revoked in Settings |
| 401 | token_expired | Token past its expiry date |
| 400 | invalid_company_key | X-Company-Key missing or not the token's company |
| 400 | invalid_request | Invalid parameter — the message names the field |
| 404 | not_found | No such resource in your company |
| 429 | rate_limited | Bucket empty — wait Retry-After seconds |
| 500 | internal_error | Our fault — retry later, quote request_id to support |
Invalid parameters always fail with 400 — filters are never silently
ignored, so you can't accidentally act on wrong data.