Tasks and spend
What a task is, how plan caps and spend caps show up as 429s, why a task can wait for your confirmation, and the /v1/tasks endpoints.
codai bills by task, not by request. A task is one unit of work — a fresh instruction from a user and everything the agent does to finish it. Continuation turns (tool results, follow-up steps) belong to the task that started them and bill nothing extra.
When a task starts
A request starts a task when it is a top-level turn whose last message is a real user message — text a person typed, not a tool result and not an assistant continuation. Rapid follow-ups inside a 10-minute window after a paid task are free: they count as the same task. A follow-up after the window opens a new one.
Spawned sub-agents and agent-run steps are always continuations (is_task_start = 0). A synchronous agent run is exactly one task.
X-Codai-No-Task
Utility calls — a title, a classification, a one-line rewrite — should not consume a task. Send X-Codai-No-Task: 1 on POST /v1/chat/completions or POST /v1/messages with codai-fast or codai-explorer and the request bills as a continuation. On any other alias the header is 400 bad_request:
x-codai-no-task is only valid for utility aliases (codai-fast, codai-explorer); got "codai"
Caps and the two 429s
Before a fresh task runs, the gateway walks your entitlements in this order and stops at the first that pays:
- Unlimited plan → run.
- Wallet balance in EUR (welcome credit, referrals, top-ups) → run, charged after the fact.
- Tasks included in your plan this month (or today, on daily plans) → run.
- Prepaid task credits → run.
- Metered overage, if your billing allows it → run.
- Otherwise
429 quota_exceeded.
{ "error": { "message": "You've used all 5 agent tasks included in the Free plan this month. Your free tasks reset in 7d 3h. Upgrade to Student for 25 tasks every day with the same Claude Opus 4 quality.", "type": "rate_limit_error", "code": "quota_exceeded" } }Retry-After on that response is honest: the number of seconds until the cap resets (next UTC month, or UTC midnight on a daily cap). Continuations are never blocked — a task that already started always finishes.
The other 429, rate_limit_exceeded, is about request rate and euro budgets, not tasks: per-minute and per-hour request limits (X-RateLimit-Limit-Minute, -Remaining-Minute, -Reset-Minute and the -Hour trio on every response) and the daily / monthly EUR spend cap set on a key in the hub. Read Retry-After and back off. See limits and pricing for the tiers.
Both codes carry type: "rate_limit_error", so an OpenAI SDK raises RateLimitError for both. Distinguish them by error.code: quota_exceeded means buy or wait for the reset; rate_limit_exceeded means slow down.
Outcome billing and confirmation
Beyond the cap, a task carries an outcome — did codai actually solve it? On surfaces that can prove it (desktop, CLI) the client reports pass or fail with execution evidence and the task settles by itself. Everything else — IDEs, raw SDKs, curl, anything the gateway classifies as proxy — cannot prove an outcome, so the gateway asks you.
open ──(idle 30 min)──▶ unconfirmed ──(you answer within 24 h)──▶ confirmed | fail
└──(no answer)─────────────▶ window closed, not billed- A
proxy,cliormobiletask with no activity for 30 minutes is closed asunconfirmedand given a 24-hour confirmation window. - It appears under Pending at hub.codai.ro/tasks — "When a task from your IDE or CLI goes idle, it shows up here so you can tell us whether codai solved it." You answer Yes, it worked or No, optionally with a note (kept with the task, never sent to the model).
- Yes closes the task as
confirmedwith evidenceuser_confirmedand applies the flat success charge; on wallet plans the token cost already charged is refunded against it. No closes it asfail— nothing is charged. - Let the window lapse and the task is simply not billed as a success.
The hub also shows every task's surface (IDE / API, Desktop, CLI, Resolve, Sandbox, Mobile, Hub), request count, upstream cost and what was charged, so you can reconcile a bill line by line.
To attribute tasks precisely, send X-Codai-Client: <surface>/<version> (see request headers) and, if you have your own task ids, X-Codai-Task-Id ([A-Za-z0-9._:-], ≤ 128 chars) — the gateway groups requests by it instead of by derived session and echoes it back.
The task object
Every /v1/tasks response uses this shape. Money is in integer micro-units; dates are ISO-8601 or null.
Prop
Type
Endpoints
All /v1/tasks routes need a key tied to a user account (401 invalid_api_key — Tasks require a user-scoped API key — otherwise) and return only your own tasks. They are rate-limited like any other request.
| Method | Path | Returns |
|---|---|---|
GET | /v1/tasks?outcome=&limit=&cursor= | { tasks: Task[], next_cursor: string | null }, newest first. outcome filters by state; limit 1 – 200 (default 50); cursor is the opened_at of the last row you saw. |
GET | /v1/tasks/pending | { tasks: Task[] } — up to 100 tasks waiting for your answer (same list as the hub's Pending tab). |
GET | /v1/tasks/stats?since= | { since, opened, confirmed, pass, unconfirmed, billed } counts since an ISO timestamp (default: 30 days ago). |
GET | /v1/tasks/:id | One task, or 404 not_found. |
POST | /v1/tasks/:id/confirm | Settle an unconfirmed task from your own code. |
# What is waiting for me?
curl https://ai.codai.ro/v1/tasks/pending \
-H "Authorization: Bearer $CODAI_API_KEY"
# Confirm one
curl https://ai.codai.ro/v1/tasks/5c1d…/confirm \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "outcome": "confirmed", "note": "Tests green after the patch." }'POST /v1/tasks/:id/confirm
Body: { "outcome": "confirmed" | "fail", "note"?: string ≤ 500 }. Response:
{ "taskId": "5c1d…", "outcome": "confirmed", "billed": true, "refundedMicroEur": 118000 }| Status | code | When |
|---|---|---|
400 | bad_request | Invalid body. |
403 | forbidden | The task belongs to another user. |
404 | not_found | Unknown id. |
409 | task_not_confirmable | The 24-hour window has closed, or the task is already closed (Task is already closed as "pass".). |
Only open and unconfirmed tasks accept a confirmation. The hub uses the same rules; whichever answers first wins and the other gets 409.