Errors
Every error code the API returns, with what it means and what your integration should do.
Errors use application/problem+json (RFC 9457):
{
"type": "https://docs.kapiworks.com/errors/invalid_token",
"title": "Invalid API key",
"status": 401,
"detail": "The API key is invalid or no longer active.",
"code": "invalid_token"
}Act on code, or on type, which is the address of the code's page here. They are stable. title and detail are for people and can change, so do not parse them.
Every code
| Status | Code | What to do |
|---|---|---|
| 400 | invalid_request | Fix the query and send it again. Unknown or repeated parameters, malformed dates and IDs, and periods longer than 400 days are refused. |
| 400 | invalid_cursor | Start the pull again without a cursor. A cursor works only on the endpoint and filters that produced it, for the same company. |
| 400 | key_in_url | A URL can be stored in logs and browser history. Ask the company administrator for a new key, and send it only in the Authorization header. |
| 401 | invalid_token | Stop polling and ask the company administrator for a new key. Unknown, expired and revoked keys get the same answer. |
| 403 | insufficient_scope | Ask the company administrator for a key with the scope named in the WWW-Authenticate header. Retrying the same key does not help. |
| 403 | ip_not_allowed | Call from one of the key's networks, or ask the company administrator for a key that allows yours. |
| 404 | not_found | Use a documented path on the API host. |
| 405 | method_not_allowed | The API is read-only. The Allow header lists the methods it accepts. |
| 422 | before_history | Use history_complete_from as the earliest moment you ask for. Earlier records are unavailable, not empty. |
| 422 | record_too_large | Use a records pack for this register, and report the request ID to Kapi support. |
| 429 | rate_limited | Wait at least Retry-After seconds, then add a small random delay. Refused calls do not count against the limit. |
| 429 | concurrent_limit | Wait for the running calls to finish, then retry after Retry-After seconds. Run one pull at a time per company. |
| 502 | read_failed | Keep your last complete pull. Retry with exponential backoff, and report the request ID if it keeps failing. |
| 503 | unavailable | Contact Kapi support with the request ID. Retrying does not help until the configuration is fixed. |
| 503 | upstream_unavailable | Retry after Retry-After seconds with exponential backoff and jitter. Keep your last complete pull meanwhile. |
| 503 | writer_unavailable | Retry after Retry-After seconds. A pull already in progress can still finish from its cursor. |
| 504 | read_timeout | Retry with a smaller limit, then return to your usual page size. |
Headers that come with errors
X-Request-Idon every error. Quote it when you contact support, without the key.WWW-Authenticateon 401 and 403 scope refusals. A scope refusal names the scope it needs.Retry-Afteron 429 and 503: wait at least that many seconds.Allowon 405, listingGET, HEAD.
Retrying
Retry connection failures and statuses 429, 502, 503 and 504, at most five times, with delays such as 1, 2, 4, 8 and 16 seconds plus a little random jitter. Retry-After takes precedence over your own delay. Use a timeout on every request, and when retries stop, report a failed sync instead of an empty result.
Do not retry 400, 401, 403, 404, 405 or 422 without changing something: the same request gets the same answer. Retrying a GET is safe, because reading creates no records.