codai docs
Agents

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:

  1. Unlimited plan → run.
  2. Wallet balance in EUR (welcome credit, referrals, top-ups) → run, charged after the fact.
  3. Tasks included in your plan this month (or today, on daily plans) → run.
  4. Prepaid task credits → run.
  5. Metered overage, if your billing allows it → run.
  6. 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, cli or mobile task with no activity for 30 minutes is closed as unconfirmed and 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 confirmed with evidence user_confirmed and applies the flat success charge; on wallet plans the token cost already charged is refunded against it. No closes it as fail — 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.

MethodPathReturns
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/:idOne task, or 404 not_found.
POST/v1/tasks/:id/confirmSettle 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 }
StatuscodeWhen
400bad_requestInvalid body.
403forbiddenThe task belongs to another user.
404not_foundUnknown id.
409task_not_confirmableThe 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.

On this page