---
title: "Quotes and credits"
description: "How roasOS calculates a price and charges your credits."
url: https://docs.roasos.com/concepts/quotes-and-credits
---

# Quotes and credits

Every paid action in roasOS obeys one rule. You see the price before roasOS charges you.

## The quote and confirm flow

A paid request has two steps.

1. **Quote.** Set `quote_only` to `true`. roasOS calculates the price, and it charges nothing.
2. **Confirm.** Send the same body again with the `quote_id` of that quote. Only this step uses credits.

A quote returns `200`:

```json
{
  "data": {
    "kind": "quote",
    "quote_id": "8c4a1f77-...",
    "credits": 9,
    "expires_at": "2026-08-19T10:14:11Z",
    "pricing_version": "customer-pricing@2026-10-30.v14",
    "authorization": { "status": "auto_approved" },
    "confirmation": {
      "model": "kling-4",
      "mode": "image-to-video",
      "duration_seconds": 5,
      "generate_audio": true
    }
  }
}
```

Read `confirmation`. It states what roasOS understood from your request. The price is for that reading, not for the words that you sent.

A confirm returns `201` with a job, an analysis or an agent run. Refer to [Jobs](/concepts/jobs).

Send `quote_only` and `quote_id` in one body, and roasOS answers `400`. The message is `quote_id must be omitted when quote_only is true`.

<Callout>
  An agent or a script cannot use your credits alone. roasOS refuses a paid request that carries no `quote_id` and no `quote_only` value. The code is `quote_required`, and the refusal comes before the action starts.
</Callout>

## Idempotency keys

A paid request needs an `Idempotency-Key` header. The value is 16 to 255 characters. Use letters, numbers, and the characters `.`, `_`, `:` and `-`. A value in another shape is a `400 invalid_idempotency_key`, and an absent header is a `400 idempotency_key_required`.

The key makes a second attempt safe. Send the same request twice with the same key, and roasOS gives you the same result. roasOS charges you one time only.

Use the same key for a quote and for its confirm.

```bash
-H "Idempotency-Key: order-12345-image-1"
```

## Quotes have a time limit

A quote is valid for **10 minutes**. After 10 minutes a confirm answers `410 quote_expired`. Quote the request again, with a new `Idempotency-Key`.

A quote applies only to the request that you priced. You cannot change the prompt or the model between the quote and the confirm. A changed body gets a new quote.

## The endpoints that never quote

A free endpoint costs 0 credits. It needs no `Idempotency-Key`, it refuses `quote_only`, and it never answers with a quote. Do not write a quote step for one of these calls.

Every read and every delete is free. These writes are free as well:

* `POST /v1/products` and `POST /v1/uploads`
* `POST /v1/memory/pins`
* `PUT /v1/brand-kit` and `PUT /v1/memory/brief`
* `POST /v1/intelligence/refresh`
* `POST /v1/agent-runs/{id}/gates/{gate_id}/decline`
* `POST /v1/agent-runs/{id}/cancel` and `POST /v1/jobs/{id}/cancel`

Two endpoints are exceptions to the rule above.

* `POST /v1/agent-runs/{id}/gates/{gate_id}/approve` costs 0 credits and &#x2A;*needs an `Idempotency-Key`**. The approval starts the follow-up run that renders the approved card, and that run spends on your key. It answers with no quote. Refer to [Agent runs](/agent/runs).
* `POST /v1/generation-batches/quotes` answers with a quote and nothing else, and it never charges. Refer to [Overview](/api-reference/overview).

## Reserved credits and charged credits

* At the **confirm** step, roasOS reserves the credits. The job envelope reports them in `credits_reserved`.
* If the action is successful, roasOS captures the reservation and charges you.
* If the action fails, roasOS releases the reservation and charges you nothing. A provider error and a timeout are two causes of a failure.

To read your balance, send `GET /v1/account`:

```json
{
  "credits": {
    "available": 1570,
    "reserved": 0,
    "pending_refunds": 0
  }
}
```

## What an agent run costs

An agent run has a second price axis. A run spends on two things: its own thinking, and the renders that it starts.

Two caps govern one run, and neither replaces the other.

| Cap                            | Bounds                                                        | Default |
| ------------------------------ | ------------------------------------------------------------- | ------- |
| `max_credits`                  | what the tokens of the run may cost                           | 25      |
| `generation_spend_cap_credits` | what the run may spend on paid generations with no human gate | 0       |

Each cap stops at 100,000 credits. The key spend policy can lower either default. The generation cap defaults to 0 on purpose. A run that you say nothing about never charges you for a render. It opens a gate, and it waits for your approval.

`quote_only: true` on an agent run answers with the **ceiling**, which is `max_credits` plus `generation_spend_cap_credits`. roasOS does not predict what the run will do. The confirm reserves that whole ceiling.

roasOS then meters the run on the tokens that it used. A run prices at `agent-token-pricing@2026-10-04.v3`, and a model endpoint prices at `customer-pricing@2026-10-30.v14`. At the end of the run roasOS charges the metered cost, and it refunds the rest of the ceiling. A run on a model with no published rate charges 0 credits and refunds in full.

Refer to [Agent runs](/agent/runs).

## The approval threshold

Each API key carries a spend policy. It holds the price of one call, two budgets, an approval threshold, and the models the key may call. Send `GET /v1/limits` to read it. Refer to [Cost controls](/authentication#cost-controls) for the whole response.

A quote reports the decision of that policy in `authorization.status`.

* `auto_approved` — the price is at or under `policy.approval_threshold_credits`. Confirm the quote.
* `approval_required` — the price is above it. The quote carries an `authorize_url`. Approve the request at that URL, then confirm the quote.

roasOS refuses a request that is over a bound, and the code names the bound: `403 request_credit_limit_exceeded`, `403 daily_credit_budget_exceeded`, `403 monthly_credit_budget_exceeded` or `403 spend_approval_required`. Refer to [Errors](/concepts/errors).

The same threshold governs an unattended agent run. The agent pages name it `auto_approve_up_to_credits`, and it is the same policy value.

## Read your usage

* `GET /v1/account/usage` gives a list of the charges.
* `GET /v1/account/ledger` gives the full billing record with reservations and refunds.
* `GET /v1/account/usage/summary` gives the totals for each day.

The list of charges and the ledger are paged. The summary is not. Refer to [Pagination](/concepts/pagination).
