Conventions
Every endpoint follows the same rules, so a client written once handles them all.
Response envelope
{
"success": true,
"data": { … },
"page": { "size": 20, "number": 0, "total_elements": 134, "total_pages": 7, "has_next": true },
"meta": { "traceId": "4bf92f35…", "timestamp": "2026-10-04T10:00:00Z", "version": "v1" }
}
page appears on list responses only. Quote meta.traceId when you contact support.
Errors
{
"success": false,
"error": {
"code": "validation.failed",
"type": "https://errors.finaiq.ai/validation.failed",
"title": "Bad Request",
"status": 400,
"detail": "Only a VALIDATED (or previously EXPORT_FAILED) document can be exported",
"fields": { "email": "must be a well-formed email address" }
},
"meta": { "traceId": "…" }
}
Branch on error.code (stable); show error.detail to people.
| Status | When |
|---|---|
| 400 | Invalid input or the action isn't allowed in the document's current state. |
| 401 | Missing, invalid or expired access token — refresh and retry once. |
| 403 | Signed in, but not allowed (role or workspace). |
| 404 | Not found, or not visible to you. |
| 409 | Conflict, e.g. a duplicate. |
| 429 | Rate limited — wait until the time in X-RateLimit-Reset. |
Pagination
List endpoints take ?page= (0-based) and ?size= (default 20) and return
page. Keep requesting while page.has_next is true.
Rate limits
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and
X-RateLimit-Reset (Unix time, in seconds, when the window refills). Reads allow 60 requests a minute, writes 30, and
exports 10. On 429, back off until the reset.
Idempotency
Endpoints that require it document an Idempotency-Key header (1–255 characters, e.g. a UUID).
Retrying with the same key and body returns the original result; the same key with a different body is
rejected with 409.
Dates and money
Timestamps are ISO 8601 in UTC (2026-10-04T10:00:00Z); dates are yyyy-MM-dd; amounts are
decimal numbers in the document's currency (currencyCode, ISO 4217).