Skip to content
Beta. This surface may change before GA; breaking changes are dated in the changelog.

Errors

Errors are returned as a JSON envelope with a machine-readable kind and a human-readable message:

{ "error": { "kind": "not_found", "message": "Run not found" } }

Branch on kind (stable); show message to humans (may change).

kind HTTP When
validation 400 The request body, params, or query failed validation.
unauthorized 401 Missing or invalid API key.
forbidden 403 Authenticated, but not allowed (e.g. wrong organization).
not_found 404 The resource doesn’t exist (or isn’t in your tenant).
conflict 409 The request conflicts with current state.
rate_limited 429 Too many requests — back off and retry.
internal 500 An unexpected server error.
unavailable 503 A dependency was temporarily unreachable — retry.

rate_limited and unavailable are the two transient kinds; every other kind is a decision about your request and will answer the same way again.

An input we do not understand is refused, never ignored

Section titled “An input we do not understand is refused, never ignored”

A field or query parameter an endpoint does not declare is a 400 naming it, on both halves of a request:

{
"error": {
"kind": "validation",
"message": "filter: unknown query parameter for GET /v1/brains/:handle/search. It accepts: q, mode, limit, consumerId."
}
}

This matters most where a parameter would have NARROWED an answer. Dropping an unrecognized filter= and answering 200 returns a confident, well-formed ranking over the whole Brain to a question that asked for one slice of it — and nothing in the response says so. Refusing it is the only answer you can act on, and it is why a client generated against an older shape of a preview endpoint fails loudly at the parameter it should stop sending rather than quietly reading something else.

Every /v1/* response — success or error — carries an x-request-id header. Log it alongside the request you made; when you report a failed call, include it so the failure can be found on our side without you having to attach a full HTTP transcript. It has no fixed format: usually a UUID, but on some requests it is Cloudflare’s own cf-ray value, echoed as-is rather than minting a second id for the same request.

Terminal window
curl -sD - https://api.nicia.ai/v1/brains \
-H "authorization: Bearer $NICIA_KEY" \
| grep -i x-request-id

429 and 5xx are safe to retry with exponential backoff. When retrying POST /v1/runs, send an Idempotency-Key so a retry never creates a duplicate run. 4xx errors other than 429 indicate a problem with the request itself — fix it rather than retrying.