codai docs
Resolve

Pricing and limits

Flat quotes of €7, €19 or €49 held on your euro wallet and kept only on a verified fix; three free fixes; top-ups on 402; per-user caps and timeouts.

Resolve prices the outcome, not the tokens — no fix, no fee. Triage quotes one of three flat tiers; you pay that amount if — and only if — the job ends resolved with an attestation. Declines, failures and infrastructure errors are always €0, and your first 3 verified fixes are free.

Tiers

TierPriceTypical shape
t7€7Small, well-scoped bug: single file, clear repro.
t19€19Moderate: multi-file, needs investigation.
t49€49Hard but plausible: subtle, cross-cutting.
free€0Your first 3 verified fixes, whatever their difficulty.

Prices are in euro, the same €7 / €19 / €49 shown on codai.ro/pricing. Paid jobs are held on your prepaid codai wallet, which is kept in EUR. No exchange rate is applied anywhere in the pipeline.

Money fields on the wire are integers in micros and keep their historical _micro_usd names for API stability: quote_micro_usd: 19000000 is a 19.00 quote, which holds €19 on your wallet.

The free tier

3 verified fixes per user, across all your keys. It is claimed atomically when a quotable job arrives: the job is created already accepted with tier: "free" and quote_micro_usd: 0, and free_tier_remaining tells you what is left afterwards. A free job that ends failed or error returns its slot. Once the three are used, quotable jobs come back as quoted and wait for your accept.

Free jobs run a single fix candidate (greedy); paid jobs may fan out up to four — see How it works.

How you pay: a wallet hold

Accept places a hold

POST /v1/resolve/jobs/{id}/accept debits the quote from your wallet in the same transaction that moves the job to accepted. If the debit cannot be made, the status change is rolled back too — you never have a running job without the funds held, nor funds held for a job that did not start. Free-tier jobs skip this step entirely.

Verified fix: the hold becomes the charge

When the job resolves, the worker writes the attestation row first and only then records the charge against the hold. The debit simply stays on your wallet — there is no second payment, no card charge and no separate invoice line for Resolve. No attestation, no charge: the order is enforced in code.

Anything else: a full refund

A job that ends failed (no verified fix within budget), error (infrastructure, including a worker lost and swept by the watchdog) returns the whole held amount to your wallet as a refund entry. Refunds are idempotent per job, so a retried settlement never pays you twice or charges you twice.

If your wallet cannot cover the quote, accept answers 402 and the job stays quoted — nothing is debited:

{
  "error": "insufficient_funds",
  "balance": 5000000,
  "required": 19000000,
  "currency": "micro_eur",
  "topup_url": "https://pay.codai.ro/checkout?topup=14"
}

balance and required are EUR micros. topup_url opens the pay.codai.ro checkout for the shortfall rounded up to whole euros, clamped to the checkout bounds of €10 minimum and €1000 maximum. Top up, then call /accept again on the same job id. The GitHub Action posts this link on the issue for you.

For a t7 job the 402 also carries "bundle_url": "https://pay.codai.ro/checkout?bundle=resolve10" — buying the fix bundle is an alternative to topping up.

Intro price

Your first paid fix costs €1. On your first paid accept — any of t7, t19 or t49 — Resolve holds €1 instead of the list price. While you are eligible, the 201 quote says so:

{ "status": "quoted", "tier": "t19", "quote_micro_usd": 19000000,
  "intro_price_micro_eur": 1000000, "list_price_micro_eur": 19000000 }
  • The intro is used up only by a verified fix. If that job ends failed or error, the €1 is refunded like any hold and the intro is offered again on your next paid accept.
  • You are eligible while you have no verified paid job and no other intro job in flight (accepted / running) — so two parallel accepts cannot both get €1.
  • The accept response reports "paid_with": "intro" and "held_micro_eur": 1000000.

Fix bundle

€49 buys 10 fix credits — about €4.90 per t7 fix instead of €7. Buy it at pay.codai.ro/checkout?bundle=resolve10.

  • Credits pay only for t7 jobs. t19 and t49 always use the intro price or your wallet.
  • A credit is taken at accept instead of a wallet hold ("paid_with": "credit", "held_micro_eur": 0), returned on failure or error, and consumed only by a verified fix.
  • GET /v1/resolve/balance returns fix_credits, intro_available and wallet_micro_eur.

Which one pays: precedence

At accept Resolve picks the first that applies: free tier > intro price > fix credit (t7 only) > wallet. paid_with on the accept response (and on GET /v1/resolve/jobs/{id} once accepted) tells you which was used. One exception protects you from a needless 402: if the €1 intro hold does not fit in your wallet but a credit is usable on that t7 job, the credit is used instead.

You can see the upstream model cost per job (not what you pay — what Resolve spent) as cost_micro_usd on GET /v1/resolve/jobs/{id}; it is the sum of gateway usage under the job's session ids.

We never spend more than 2× your quote on model calls for one job (a fixed $2 ceiling for free jobs). That ceiling is ours to absorb — it never changes what you pay.

Limits

All limits are per user, counted across every key you own.

LimitValueWhat you see when you hit it
Submissions6 per minute429 rate limited: too many resolve submissions, retry in a minute
Jobs in flight (accepted / running)2429 too many resolve jobs in flight, wait for one to finish
Jobs per day10429 daily resolve job cap reached, retry tomorrow
Free verified fixes3, lifetime (a failed or errored free job does not use one)Quotes come back quoted instead of accepted
Wallet balance at accept≥ the quote, in EUR402 insufficient_funds with topup_url; the job stays quoted
Issue text50 000 characters (the Action truncates)—

Caps are checked before triage, so a rejected submission costs neither a slot nor a triage call. The day resets at 00:00 UTC; every 429 body is the flat { "error": "<message>" } shown above, with no Retry-After header.

Timeouts

BudgetValueEffect
Whole job30 minutesPast it the job ends failed (no verified fix within budget) — €0.
One sandbox run (setup + tests)15 minutesA slow suite ends the candidate; the job may try the next one.
Agent turns per candidate4Edits beyond that are not attempted.
Fix candidates1 (free) / 4 (paid)First verified pass wins; the rest are skipped.
Orphan watchdogjob budget + 15 minutesA job whose worker died is marked error (watchdog: worker lost — not billed, safe to resubmit) and a free slot is returned.
GitHub Action polling25 minutesThe Action stops waiting and comments the last status; the job itself continues to its own budget.

Triage runs inline in your POST and is bounded by the gateway's request timeout; a slow triage returns declined with triage unavailable, please retry rather than hanging.

Supported repositories

  • Public https://github.com/<owner>/<repo> only. Private repos, other hosts and monorepo sub-paths are rejected at submission with a 400.
  • Python with pytest, or JavaScript/TypeScript with vitest, jest or node:test (detected from package.json).
  • The repo's suite must run from a clean clone plus its committed dependency manifest. Tests that need secrets, running services or system packages beyond the runner image usually end in declined or error.

Last updated:

On this page