REFERENCE
Errors & status codes
How the Decision Receipt API reports failures: a consistent JSON error body, standard HTTP status codes, and the headers that let you trace a request or pace your traffic.
Error response shape
Every error returns a JSON body with an error field carrying a human-readable message. Where applicable the body also includes a machine-readable code and a request_id you can use to correlate the failure with server-side logs.
ERROR BODY
{
"error": "Human-readable error message",
"code": "error_code",
"request_id": "req_abc123def456"
}
The code and request_id fields are present where applicable — not every error carries both. Always branch on the HTTP status first, then read error for detail and code when you need to handle a specific failure programmatically.
HTTP status codes
The API uses standard HTTP semantics. Treat 2xx as success, 4xx as a problem with your request, and 5xx as a problem on the server side.
| Status | Meaning |
|---|---|
200 | Success. |
201 | Resource created (signup, webhook registration). |
400 | Bad request — malformed JSON, missing required fields, or schema validation failure. |
401 | Unauthorized — missing or invalid API key / webhook signature. |
404 | Resource not found (receipt, action, disposition). |
429 | Rate limit exceeded (see rate limits below). |
500 | Internal server error. |
503 | Service unavailable (health check failure, Stripe not configured). |
Common error codes
When a 4xx response carries a code, it names the specific failure. The two you will meet most often:
| Code | Endpoint | Description |
|---|---|---|
malformed_json | Any POST | The request body is not valid JSON. Returned with 400. |
| (validation string) | POST /v1/evaluate | Zod schema validation failure. The message names the offending field and constraint, e.g. "sources: Required" when the mandatory sources array is absent. Returned with 400. |
For validation failures on POST /v1/evaluate, the message is the validation string itself rather than a fixed code — read error to learn which field failed and why. Verify your ClaimInput against the field reference before retrying; a fixed body will keep failing until the schema is satisfied.
Debugging with X-Request-Id
Every response includes an X-Request-Id header — a unique trace ID for that request. You can also pass your own X-Request-Id on the request to correlate a client-side log line with the server-side trace. When you report a problem, include this value: it is the fastest way to locate the exact request.
The request_id in an error body and the X-Request-Id response header serve the same purpose: a single identifier to correlate a failure end to end. Capture whichever is present.
Handling 429 and 503
Rate limits are enforced per API key over a 1-hour sliding window. Every response reports your budget so you can pace traffic before you hit the wall:
RESPONSE HEADERS
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Limit is the hourly cap for your tier; X-RateLimit-Remaining is what is left in the current window. Read these on each response and slow down as Remaining approaches zero. When you exceed the limit the API returns 429 Too Many Requests. See rate limits for the per-tier caps.
For both 429 and transient 503 responses, retry with exponential backoff and jitter rather than tight-looping. A practical pattern: wait, then roughly double the delay each attempt (for example 1s, 2s, 4s, 8s) with a small random offset, and give up after a few attempts. For a 429, the sliding window means waiting clears the oldest requests, so a short pause is usually enough to recover headroom. A 503 indicates the service or a dependency is temporarily unavailable; back off and retry the same request unchanged.
Do not retry 400 or 401 responses without changing the request — the input or the API key is the problem, and an identical retry will fail identically.