Error shape
Every error uses a standard HTTP status code and a consistent JSON envelope:error.code (a closed, documented set),
not on the HTTP status alone (several distinct conditions can share a status)
and never on the human-readable error.message (it is advisory and may change).
Error codes
code | HTTP status | Meaning |
|---|---|---|
missing_api_key | 401 | No API key was sent. Add Authorization: Bearer <key>. |
invalid_api_key | 401 | The API key is invalid, revoked, or expired. |
org_unbound | 403 | The key is valid but not bound to an organization. |
auth_unavailable | 503 | Authentication is temporarily unavailable; retry shortly. |
review_not_found | 404 | The review does not exist or belongs to another organization. |
report_not_ready | 404 | The review exists but has not finished; keep polling. |
review_failed | 422 | The review failed (terminal); no report will exist. |
document_required | 400 | No usable document was supplied (or a file was empty). |
document_unsupported | 400 | A document, archive, or archive member has an unsupported type. |
document_too_large | 413 | A single document exceeds the per-file size limit. |
too_many_documents | 422 | More documents than the per-review limit. |
documents_too_large | 422 | The documents exceed the aggregate per-review size limit. |
duplicate_reference_id | 409 | A review with the same reference_id already exists. |
all_documents_duplicate | 409 | Every submitted document duplicates one already on the review. |
invalid_request | 400 / 422 | The request body or a field is malformed (bad JSON, failed validation, unfetchable source URL). |
rate_limited | 429 | Rate limit exceeded; retry with exponential backoff. |
webhook_endpoint_not_found | 404 | The webhook endpoint does not exist under your organization. |
internal_error | 500 | An unexpected server error; retry later. |
Review lifecycle & the failure contract
A review’sstatus moves through queued → processing → completed or failed. Both completed and failed are terminal; stop polling once you see either.
GET /reviews/{review_id}/report uses the status code to tell you what to do, so a failure is never mistaken for “still processing”:
| Report status | Meaning | What to do |
|---|---|---|
200 | Review completed; report body follows. | Use the report. |
404 | Review not found, or not finished yet (queued/processing). | Keep polling. |
422 | Review failed (terminal). No report will ever exist. | Stop polling; inspect the review and resubmit if appropriate. |
GET /reviews/{review_id} (status is completed or failed) rather than inferring it from the report endpoint alone.
Rate limits
Requests are rate-limited per organization (the default ceiling is 60 requests/minute, enforced across our fleet). Exceeding it returns429 Too Many Requests.
Response headers
Every rate-limited response carries the standard rate-limit headers so you can observe your remaining budget without waiting for a429:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your ceiling for the current window. |
X-RateLimit-Remaining | Requests left in the current window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
Retry-After | Seconds to wait before retrying. Sent on a 429. |
429, honor Retry-After if present; otherwise retry with
exponential backoff (e.g. 1s, 2s, 4s, … with jitter) rather than
tight-looping. Watching X-RateLimit-Remaining lets you pace requests and
avoid a 429 entirely. Webhooks let you avoid polling altogether and stay well
under any limit. If your integration needs a higher ceiling, contact your
Flightline representative.
Idempotency
Setreference_id to your own external identifier for the loan package.
Creating a second review with a reference_id that already exists returns
409, so retries won’t create duplicate reviews.