Gateway FAQ
Quick answers — 401, 402 and 429 errors, which model served a request, spend caps and refunds.
Short answers to the questions support hears most. Each one links to the page with the full detail.
Why do I get 401 invalid_api_key?
The key is missing, malformed, or no longer valid. The message says which:
message | What to do |
|---|---|
Missing or invalid API key | No Authorization: Bearer codai_… header reached the gateway. Check the header name and that your client sends it on every request. |
Invalid API key | The key does not exist. Copy it again from hub.codai.ro → Keys — a key is shown in full only once, when it is created. |
API key is disabled | The key was revoked or disabled in the hub. Create a new one. |
API key was rotated and its grace period has ended | Use the new key the rotation produced. |
API key is paused until … | The key is paused until that time. |
API key has expired / API key expired after a period of inactivity; sign in again | Create a new key, or sign in again in the app that issued it. |
Also check the base URL: it is https://ai.codai.ro/v1. A wrong path returns 404 not_found, not 401. Opening https://ai.codai.ro/ in a browser also answers 404: it is an API, not a web page — the dashboard is hub.codai.ro and these docs are the place to start.
Why do I get 402?
402 means "this needs money or a plan you do not have right now". The code tells you which:
code | Where | What to do |
|---|---|---|
subscription_inactive | Any request, when the key belongs to a subscription that is not active. | Renew or change the plan in the hub under Billing. |
insufficient_balance | Starting a managed environment (the wallet must cover one hour) or using sandboxes with unpaid sandbox usage. | Top up the wallet in the hub; the body carries the amounts. |
insufficient_funds | Accepting a Resolve quote with too little wallet balance. | The body has a topup_url for the exact shortfall. |
Running out of plan tasks is not a 402 — it is 429 quota_exceeded (below).
Why do I get 429?
Two different walls share the status code. Tell them apart by error.code:
rate_limit_exceeded— slow down. Either the per-minute / per-hour request window or the input / output token window is full, or your daily or monthly spend cap is reached.Retry-Afteris the exact number of seconds to wait.quota_exceeded— the plan's task allowance is used up and nothing else (wallet, prepaid credits, overage) pays for a new task. Conversations you already started keep working; only brand-new tasks wait.Retry-Afterpoints at the reset (next UTC month, or 00:00 UTC on a daily cap).
Both carry type: "rate_limit_error", so an OpenAI SDK raises RateLimitError for both. Details in limits & pricing and tasks and spend.
Which model actually served my request?
You always call codai; the gateway picks the upstream model. Every chat, messages and audio response says which one in the response headers:
| Header | Meaning |
|---|---|
x-codai-routed-to | The concrete upstream model that served the request. |
x-codai-provider | The provider adapter, e.g. vertex-anthropic. |
x-codai-generator | codai/<upstream model>, sent with x-codai-ai-generated: true on every response that carries model output. |
curl -sD - -o /dev/null https://ai.codai.ro/v1/chat/completions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"model":"codai","messages":[{"role":"user","content":"Hello"}]}' | grep -i x-codai-routed-toPer-model totals over a time window are in GET /v1/receipt (by_upstream). All headers: request headers.
How do spend caps work?
You can set a daily and a monthly cap in the hub; it is summed across all your keys, and per-key budgets sit under it, never above it. While caps are on, each response carries x-codai-spend-day-usd, x-codai-spend-day-cap-usd, x-codai-spend-month-usd and x-codai-spend-month-cap-usd. When a cap is reached the gateway answers 429 rate_limit_exceeded, with a message naming the window: daily caps reset at midnight UTC, monthly caps on the 1st (UTC). The cap is compared with the upstream cost the gateway measures, which is in USD — hence the -usd names. More in limits & pricing.
Do I get my money back when something fails?
- A failed request costs nothing. It is recorded with
is_task_start = 0, so it never bills a task, and a prepaid credit or metered task reserved before dispatch is refunded automatically when the upstream never served the request. - Tasks are billed on outcome. Tasks from an IDE, the API or the CLI wait for your confirmation in the hub under Tasks: No closes the task as
failand nothing is charged; a task left unconfirmed for 24 hours is not billed as a success. On wallet plans, confirming Yes refunds the token cost already charged against the flat success price. See tasks and spend. - Resolve refunds the whole hold when a job ends without a verified fix (pricing and limits).
- Usage already consumed is not refundable, because the model cost is incurred at the moment of the request. EU consumers may withdraw from a purchase of prepaid credit or a subscription within 14 days: the unused part is refunded, the part already used is not. Use codai.ro/withdrawal. The binding text is in the Terms.
Where do I get help?
Write to [email protected], or reply to any e-mail codai sent you. Live service status is at codai.ro/status. When you report a problem, include the x-codai-trace-id (or your own X-Request-Id) of a failing request — it lets us find the exact request without seeing your prompt.