Errors
Error envelope and codes.
Envelope
All error responses share the same JSON shape:
Codes
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_json | Request body was not valid JSON. |
| 400 | invalid_request | JSON parsed but failed schema validation. Carries issues. |
| 400 | (none) | No upstream status to report: the URL did not resolve to a supported provider, or the call never reached the bank. Read error. |
| 401 | missing_key | No x-api-key header. |
| 401 | invalid_key | Key not recognised. |
| 401 | revoked_key | Key was revoked. |
| 429 | rate_limited | Hit the per-minute request rate. Includes Retry-After. |
| 429 | quota_exceeded | Hit the plan's uncached verification cap. Carries used and cap, plus Retry-After to the window rollover. |
| 429 | image_cap_reached | Hit the plan's monthly screenshot read allowance. POST /api/verify-image only. |
| 429 | ocr_daily_cap_reached | The service-wide daily screenshot budget is spent. Rare, and not about your account. |
| 503 | provider_down | The bank is failing every health check we run, so we did not queue a lookup that had no answer in it. Includes Retry-After. Never counts against your cap. |
| 502 | (none) | The bank answered but the receipt failed validation. See error and the partial receipt. |
There is no 504. A call that times out or gives up waiting for a busy bank
has no upstream status code to report, so it comes back as a 400 with the
reason in error. See
what a busy bank does.
POST /api/verify-image can also return ai_not_configured (503, the
extractor is not set up on this deployment) and detection_failed (502, the
extractor ran but could not read a receipt reference out of the image).
When a bank is down
We probe every upstream about every five minutes, from each of our egress
addresses. When the most recent round has a bank failing on all of them,
a verification for that bank returns 503 provider_down straight away
instead of queueing a call that would spend two 20 s attempts to reach the
same conclusion.
Retry-After holds 300 s, the probe interval — there is no point asking again
before we have looked again. A single address failing is not enough to trigger
this, because that looks like our IP being blocked rather than the bank being
down. GET /api/status shows the same picture per component.
Retry guidance
rate_limitedwait theRetry-Afterinterval, then try again.provider_downwait theRetry-Afterinterval. Retrying sooner cannot work: nothing is asked of the bank until our next probe round says it is answering again. It costs you nothing, so a queued job can simply try later.quota_exceededandimage_cap_reacheddo not retry on a timer.Retry-Aftercan be days away, because the window is your plan's, not a minute. Upgrade, enable overage, or wait for the rollover.- 502 and the busy-bank 400 are usually the bank rather than you. Retry after 1 to 2 s with jitter and treat three consecutive failures as a real outage. Do not retry a Siinqee reference at all: those links allow about five views in total, so a retry loop destroys the receipt. See providers.
- 401 never retry. Mint a new key from the dashboard.
- 400
invalid_requestnever retry. The body is wrong.