Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.
Proxy errors use a different body shape from the rest of the API. The proxy returns a top-level {"error": "<code>"} (sometimes with a "detail" string). The rest of the KINDI API returns {"detail": "<message>"}; see Error codes for that shape. Branch your error handling on the route: /api/v1/proxy/* reads error; everything else reads detail.

Body shape

{ "error": "model_not_allowed_on_demo", "detail": "switch to your own key" }
error is a stable machine-readable code. detail is an optional human-readable hint and may be absent.

Catalogue

StatuserrorWhen it firesRetry?
400provider_key_requiredA Bearer caller sent no X-Provider-Key, and the managed key isn't available to it. The most common proxy error — the managed demo is session-only, so programmatic callers must always bring a key.no
400invalid_request_bodyThe body isn't valid JSON, or decodes to something that isn't a JSON object.no
400managed_key_disabledThe managed demo is switched off on this deployment. Check managed_enabled on /proxy/status.no
400model_not_allowed_on_demoA model not in the managed-demo allowlist was requested on the managed path. Bring your own key for unrestricted models.no
402insufficient_tokensYour KINDI masking-fee balance (free quota + paid) is exhausted. detail carries needed_tokens=… available_tokens=…; top up at /dashboard/billing. This is KINDI's fee, not the provider's inference cost.no
402managed_allowance_exhaustedYour personal daily managed-demo token cap is used up. detail: "switch to your own key".no¹
403demo_requires_verificationThe managed demo requires a logged-in, email-verified dashboard session. Unverified/anonymous callers are blocked.no
404unknown_providerThe {provider} path segment isn't openai or anthropic.no
404glossary_not_foundThe glossary_id you referenced doesn't exist or isn't yours.no
413request_too_largeThe request body exceeds the proxy body-size cap. Applies to all callers, not just the demo.no
429rate_limitedYour per-key (or per-user) limit is full; on the managed path a second, tighter demo bucket applies too. Carries a Retry-After header; sleep for it rather than spinning.yes
501endpoint_not_supported_in_v1The requested provider endpoint is outside the v1 scope (chat/completions + messages only).no
502egress_not_allowedThe resolved upstream host isn't on KINDI's egress allow-list. A configuration problem, not a transient one.no
502provider_unreachableKINDI couldn't reach OpenAI/Anthropic at all (DNS, TLS, connection).yes
503demo_unavailableThe global daily managed-demo budget is exhausted across all users. detail: "demo temporarily unavailable, use your own key".yes²
503managed_key_unavailableThe managed demo is enabled but KINDI has no usable managed key for that provider right now.yes
503masker_unavailable / masker_overloadedDetection is down or shedding load, so the prompt can't be masked; KINDI fails closed rather than forwarding your text unmasked. masker_overloaded carries Retry-After: 1.yes
503image_redact_unavailableThe request carries inline images and the image-redaction service is unreachable. Carries Retry-After: 2; fails closed for the same reason.yes
413 / 400image_redact_rejectedAn inline image was rejected as too large (413) or unprocessable (400).no
¹ Resolves at the next Asia/Riyadh midnight reset, or immediately if you switch to your own provider key (X-Provider-Key).
² Resolves at the next Asia/Riyadh midnight reset (the global budget refills). Switching to your own key removes the dependency on the managed budget entirely.

Provider errors pass through

When OpenAI or Anthropic itself returns an error (invalid provider key, provider rate limit, content filter), the proxy relays the provider's status and body as-is, in the provider's native error shape; it does not rewrap them into {"error": code}. So a 401 with an OpenAI-shaped error body means your X-Provider-Key was rejected by OpenAI, not by KINDI.

Shared statuses

Auth (401) and the geo-block (403 with no body at all) are handled before the proxy route runs, so they keep the site-wide shape documented in Error codes401 returns {"detail": …} and the geo-block returns nothing to parse.
Everything the proxy route itself raises, including the ones that also exist elsewhere in the API, like insufficient_tokens, rate_limited, masker_unavailable, and glossary_not_found — is rendered in the proxy's top-level {"error": code} shape and is listed in the catalogue above. So on /api/v1/proxy/* you read error for anything with a JSON body except a 401.