Errors
Every Zaraftis v1 API error code, what it means, and what to do about it.
Any route can fail with 401, 403, 429, or 500, besides the responses listed on its reference page. Every error has the same shape: branch on error.code, log error.message, and check error.retryable before you retry.
{
"success": false,
"error": {
"code": "validation_failed",
"message": "validation failed",
"retryable": false,
"field_errors": [
{
"field": "body.prompts.0.text",
"code": "required",
"message": "Invalid input: expected string, received undefined"
}
]
},
"request_id": "d3b0e28f-89a4-4c1c-a68b-11b0d5886f45"
}
Keep the request_id. Quote it when you report a problem.
Errors and fixes
| Status | Code | Why it happens | What to do |
|---|---|---|---|
| 400 | bad_request |
The request cannot be read, for example the body is not valid JSON. | Fix the request and send again. |
| 401 | unauthorized |
The Authorization header is missing, or the key is wrong, revoked, or expired. |
Send Authorization: Bearer <key>. If the key was revoked, create a new one on the developer page. |
| 403 | forbidden |
The key does not have the scope the route needs, such as scans:write to start a scan. |
Create a key with the scope on the developer page. |
| 404 | not_found |
The id is unknown, belongs to another workspace, or is outside the key’s brand allowlist. All three get the same answer. | Check that the id came from this workspace and that the key’s allowlist includes its brand. |
| 400 | validation_failed |
The body or query does not match the route’s schema. field_errors lists each bad field, for example body.prompts.0.text. |
Fix the listed fields and send again. |
| 400 | unsupported_selection |
A platform, country, or language in the request is not offered. | Read GET /v1/capabilities and pick from what it lists. |
| 400 | invalid_page_token |
A page token was reused with a different filter or limit, changed, or expired. | Start the list again without a token. |
| 400 | confirmation_required |
A destructive DELETE was sent without { "confirm": true }. The message says what the delete will do. |
Send the same request with { "confirm": true } in the body. |
| 402 | wallet_not_activated |
The workspace has no credits yet: no free trial and no credit pack. | Start the free trial or buy a credit pack in the dashboard. See Credits and billing. |
| 402 | insufficient_credits |
The wallet cannot cover the most this request can cost. | Add credits in the dashboard, or make the request smaller. |
| 402 | payment_action_required |
Auto-reload needs the card holder to confirm a payment. | Confirm the payment in the dashboard, then retry. |
| 405 | method_not_allowed |
The action needs a person in the dashboard, such as connecting a Google account. | Do it in the Zaraftis dashboard. |
| 409 | conflict |
The resource is in a state that does not allow the request, or a brand with the same name already exists. | Read the resource again and check its state. For a name clash, use the existing brand. |
| 409 | idempotency_conflict |
An Idempotency-Key was reused with a different request body. |
Use a new key for a new request. Reuse a key only to retry the exact same request. |
| 409 | duplicate_url |
A brand with this URL already exists in the workspace. details gives its brand_id and name. |
Use the existing brand_id. |
| 422 | incomplete_evidence |
A report or export needs a source record that is no longer available. details.unavailable_source_ids lists them. |
Leave those sources out, or pick other ones. |
| 429 | rate_limited |
Too many requests in the current window. | Wait for the seconds in Retry-After. Watch X-RateLimit-Remaining to slow down before you hit the limit. |
| 503 | capacity_unavailable |
The workspace has too much work running at once (scans, or queries still owed). details gives the limit and the current count. |
Retry after a running scan finishes. |
| 503 | spending_temporarily_unavailable |
Paid work is paused on the wallet for now. Reads still work. | Retry later. |
| 503 | reconciliation_pending |
The wallet is being checked. Paid work is blocked; reads still work. | Retry later. |
| 500 | internal_error |
Something failed on our side. | Retry with backoff. If it keeps failing, report the request_id. |
| 501 | not_implemented |
The option is not available yet, such as creation_mode: "auto" on a visibility setup. |
Use the available option, such as creation_mode: "manual". |
When to retry
Retry only when error.retryable is true. When you retry a POST, send the same Idempotency-Key as the first attempt: the API then returns the first result and never starts or charges the work twice. Wait longer after each failed attempt, and honor Retry-After when it is present.