Skip to main content
The Reinx API uses conventional HTTP status codes and a flat, machine-readable error body.

Error format

Every API error returns a stable code plus a human-readable message:
Some errors add route-specific scalar fields (like retry_after_seconds above) — there is never a nested details object. Branch on the error code, not the message text; messages can change. Casing convention: routes authenticated with a session or organization key return UPPER_SNAKE codes (UNAUTHORIZED, PLAN_LIMIT_REACHED). Routes authenticated with an agent API key return lower_snake codes (unauthorized, agent_disabled, scope_denied).
Request validation failures are the one exception to the shape above — they return Fastify’s standard 400 body, where the machine code lives in code:

Common error codes

Organization API key codes

Codes involved in the organization-key lifecycle. Most are returned on routes authenticated with a reinx_org_live_… key; the mint-time codes are returned to a human dashboard session creating or rotating a key, since key management has no machine scope at all. See Authentication for scopes and bindings.

Rate limits

Limits are applied per organization and scale with plan tier: Several tighter limits are enforced on top of that org-wide ceiling: Authenticated responses carry usage headers:
On a REST success these headers describe the general per-organization bucket. On a 429 they describe the tripped bucket only when the per-organization limiter is what rejected you (the org bucket or a scoped per-route cap). For CREATE_QUOTA_EXHAUSTED and the upstream-provider relay, a different mechanism rejected the request while the org limiter passed — so its pre-handler already stamped healthy general-bucket headers that say nothing about the actual rejection. A healthy-looking x-ratelimit-remaining therefore does not rule out a per-route cap or the creation quota, and for those two cases you must not read a delay from the headers at all. Branch on the error code to tell them apart: RATE_LIMITED is a rate limit; CREATE_QUOTA_EXHAUSTED is not.MCP differs: a gateway-level 429 carries no retry_after_seconds (use the retry-after header), its x-ratelimit-reset is a Unix epoch, and a pre-auth IP-level 429 carries no x-ratelimit-* at all. A management tool proxying a REST cap does return retry_after_seconds. Full comparison on Authentication.

Daily creation quota (organization keys)

Distinct from rate limiting: a per-key counter covering agent, project, and team creation (default 100/day) that resets at 00:00 UTC, not on a rolling window. Exhaustion returns 429 CREATE_QUOTA_EXHAUSTED with no retry-after header and no retry_after_seconds — there is no short window to wait out, and backing off will not clear it before the reset. Retrying the same idempotency key + payload within the same UTC day is never re-charged (complete or not); only a cross-midnight resume of an incomplete create costs another unit. Rotating the key does not reset the count.

Handling 429s

  • First check the code — two 429s are daily lockouts, not throttles. CREATE_QUOTA_EXHAUSTED (organization keys, resource creation) and COPILOT_BUDGET_EXCEEDED (POST /v1/copilotkit, the org’s daily assistant-usage ceiling) both carry no delay and clear only at 00:00 UTC — retrying either on a backoff loop just burns the day. Every other 429 (incl. COPILOT_BUSY, a genuine short throttle) is a short throttle.
  • For a throttle, wait at least retry_after_seconds (or the retry-after header) before retrying — don’t hammer. When neither is present — the upstream-provider relay case in the 429 matrix carries no delay field at all — fall back to client-side exponential backoff (start ~1s, grow it). Retrying immediately just re-hits the upstream limit.
  • For write requests, retry with the same idempotency key: the retry is deduplicated and will never double-spend. See the payment workflow for how idempotency keys behave.
  • If you’re consistently hitting limits at legitimate volume, contact support about your plan’s limits.