Siinqee Bank unavailable (9 days). Known, ongoing.Status →
links.et

Errors

Error envelope and codes.

Envelope

All error responses share the same JSON shape:

{
	"ok": false,
	"error": {
		"code": "rate_limited",
		"message": "Rate limit exceeded. Maximum 60 requests per minute."
	}
}

Codes

HTTPCodeWhen
400invalid_jsonRequest body was not valid JSON.
400invalid_requestJSON 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.
401missing_keyNo x-api-key header.
401invalid_keyKey not recognised.
401revoked_keyKey was revoked.
429rate_limitedHit the per-minute request rate. Includes Retry-After.
429quota_exceededHit the plan's uncached verification cap. Carries used and cap, plus Retry-After to the window rollover.
429image_cap_reachedHit the plan's monthly screenshot read allowance. POST /api/verify-image only.
429ocr_daily_cap_reachedThe service-wide daily screenshot budget is spent. Rare, and not about your account.
503provider_downThe 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.

{
	"ok": false,
	"error": {
		"code": "provider_down",
		"message": "Telebirr is not answering us right now (every check has failed for 25 minutes). We did not spend a lookup on it. Try again in a few minutes."
	}
}

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_limited wait the Retry-After interval, then try again.
  • provider_down wait the Retry-After interval. 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_exceeded and image_cap_reached do not retry on a timer. Retry-After can 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_request never retry. The body is wrong.

On this page