# Pricing and limits (/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 [#tiers]

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

Prices are in **euro**, the same €7 / €19 / €49 shown on [codai.ro/pricing](https://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.

<Callout type="info">
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.
</Callout>

## The free tier [#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-it-works.mdx).

## How you pay: a wallet hold [#how-you-pay-a-wallet-hold]

<Steps>
<Step>

### Accept places a 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.

</Step>
<Step>

### Verified fix: the hold becomes the charge [#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.

</Step>
<Step>

### Anything else: a full refund [#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.

</Step>
</Steps>

### Not enough balance: `402` and a top-up link [#not-enough-balance-402-and-a-top-up-link]

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

```json
{
  "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](./github-action.mdx) 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](#fix-bundle) is an alternative to topping up.

## Intro price [#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:

```json
{ "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 [#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](https://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 [#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 [#limits]

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

| Limit | Value | What you see when you hit it |
| --- | --- | --- |
| Submissions | **6 per minute** | `429 rate limited: too many resolve submissions, retry in a minute` |
| Jobs in flight (`accepted` / `running`) | **2** | `429 too many resolve jobs in flight, wait for one to finish` |
| Jobs per day | **10** | `429 daily resolve job cap reached, retry tomorrow` |
| Free verified fixes | **3**, 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 EUR | `402 insufficient_funds` with `topup_url`; the job stays `quoted` |
| Issue text | 50 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 [#timeouts]

| Budget | Value | Effect |
| --- | --- | --- |
| Whole job | **30 minutes** | Past it the job ends `failed` (no verified fix within budget) — €0. |
| One sandbox run (setup + tests) | **15 minutes** | A slow suite ends the candidate; the job may try the next one. |
| Agent turns per candidate | **4** | Edits beyond that are not attempted. |
| Fix candidates | **1** (free) / **4** (paid) | First verified pass wins; the rest are skipped. |
| Orphan watchdog | job budget + **15 minutes** | A job whose worker died is marked `error` (*watchdog: worker lost — not billed, safe to resubmit*) and a free slot is returned. |
| GitHub Action polling | **25 minutes** | The 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 [#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`.

<Cards>
  <Card title="GitHub Action" href="./github-action.mdx" description="auto-accept and max-tier control what you pay." />
  <Card title="Gateway limits & pricing" href="../gateway/limits-pricing.mdx" description="How the rest of codai is billed." />
  <Card title="API reference" href="../api/resolve.mdx" description="Every field, including cost_micro_usd." />
</Cards>
