> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Credits

> Real-USD balance, metered ledger, and top-ups.

Billing is **real US dollars** held as a credit balance on the [organization](https://vetta.sh/docs/api/organizations.md). Every metered action debits the balance; top-ups credit it. All money fields are integer **micro-USD** (`*_micro_usd`, where 1 USD = 1,000,000 micro-USD).

## Balance

`GET /v1/credits/balance` returns the current balance.

**object** `string`

Always `credit_balance`.

**balance\_micro\_usd** `integer`

Available balance, in micro-USD.

**mode** `string`

`live` or `test` — the tier this balance is held in.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/credits/balance \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{ "object": "credit_balance", "balance_micro_usd": 48250000, "mode": "live" }
```

## Ledger

`GET /v1/credits/ledger` lists metered entries, newest first. Cursor-paginated — see [Pagination](https://vetta.sh/docs/api/pagination.md). Each inference debit carries a **5-tier token line-item** breakdown of what was metered.

**data** `object[]`

Ledger entries.

**entry**

**id** `string`

Entry id (e.g. `led_01H...`).

**type** `string`

`debit` (usage) or `credit` (top-up/adjustment).

**amount\_micro\_usd** `integer`

Signed amount, in micro-USD.

**balance\_after\_micro\_usd** `integer`

Running balance after this entry.

**actor** `object`

The principal responsible for the entry — the same shape as the [audit log](https://vetta.sh/docs/api/audit-logs.md) actor.

**actor**

**type** `string`

`user`, `key`, or `system`.

**id** `string`

Id of the member (`usr_…`) or API key (`key_…`). A settled top-up is credited by `{ "type": "system", "id": "payment_webhook" }`.

**session\_id** `string | null`

The session that incurred the charge, if any.

**agent\_id** `string | null`

The agent that ran the session, if any.

**deployment\_id** `string | null`

The deployment whose run incurred the charge, if any.

**line\_items** `object[]`

The 5-tier token breakdown of a metered inference debit. Each inference debit records all five classes. Non-model charges carry one line on their own tier.

**line\_item**

**tier** `string`

A token class — `input`, `cache_write`, `cache_read`, `output`, `reasoning` — or a metered capability: `computer` (sandbox time), `search`, `media`, `social`, `browser`, `email` (one outbound email send), `voice` (live voice call minutes), `storage` (a [storage](https://vetta.sh/docs/api/storage.md) bucket-day; no rate is published yet, so no `storage` line is booked).

**amount\_micro\_usd** `integer`

Amount metered for this token class.

**created\_at** `string`

Entry timestamp.

Filter the ledger with any combination of `?type=`, `?session_id=`, `?agent_id=`, `?deployment_id=`, `?from=`, and `?to=` (each `from`/`to` bound is a date like `2026-08-01` or a timestamp like `2026-08-01T00:00:00Z`, an offset's `+` sent as `%2B`; anything else is `400 validation_failed` naming the parameter).

```bash theme={"system"}
curl -fsSL "https://api.vetta.sh/v1/credits/ledger?limit=20&type=debit&agent_id=agt_01H..." \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{
  "data": [
    {
      "id": "led_01H9KK...",
      "type": "debit",
      "amount_micro_usd": -412300,
      "balance_after_micro_usd": 48250000,
      "actor": { "type": "key", "id": "key_01H8XK..." },
      "session_id": "ses_01H9AB...",
      "agent_id": "agt_01H8XK...",
      "deployment_id": null,
      "line_items": [
        { "tier": "input",       "amount_micro_usd": 148000 },
        { "tier": "cache_write", "amount_micro_usd": 32100 },
        { "tier": "cache_read",  "amount_micro_usd": 11400 },
        { "tier": "output",      "amount_micro_usd": 196500 },
        { "tier": "reasoning",   "amount_micro_usd": 24300 }
      ],
      "created_at": "2026-08-20T17:05:04Z"
    }
  ],
  "has_more": true,
  "next_cursor": "led_01H9KK..."
}
```

## Spend

`GET /v1/credits/spend` → `200 OK`. What the matching debits spent, in positive micro-USD, summed over the whole range: the total, the split by tier, and the 50 dearest agents and sessions, dearest first. It takes the ledger's `?session_id=`, `?agent_id=`, `?deployment_id=`, `?from=` and `?to=`. Use it for any total or chart: a sum over one ledger page misses every debit past it.

`?days=` (1 to 90) adds `by_day`: the company's spend per day for that many days, ending today, on `?timezone=` (default the organization's). It is the whole company's spend, so it takes no other filter. With `?days=`, `spent_micro_usd` is those days' total and `by_tier`, `by_agent` and `by_session` are empty: read the splits with `?from=` instead.

```bash theme={"system"}
curl -fsSL "https://api.vetta.sh/v1/credits/spend?from=2026-08-01T00:00:00Z" \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{
  "spent_micro_usd": 41873200,
  "by_tier": { "input": 30112000, "output": 9861200, "computer": 1900000 },
  "by_agent": [{ "agent_id": "agt_01H8XK...", "spent_micro_usd": 41873200 }],
  "by_session": [{ "session_id": "ses_01H9AB...", "spent_micro_usd": 12004100 }]
}
```

## Top up

`POST /v1/credits/topups` opens a hosted charge and returns a **topup object** describing it. It does **not** move the balance: credit is applied when the payment provider's signed callback arrives and is verified, so poll the [balance](#balance) rather than reading the create response as settlement. Requires the `billing:write` scope.

A company [being set up](https://vetta.sh/docs/api/organizations.md#set-up-a-company) cannot top up: it runs on its one-time setup credit, and its next step is the plan. The call is refused with `subscription_required` (402) until the company is activated.

**amount\_micro\_usd** `integer` *required*

Amount to add, in micro-USD.

**return\_to** `string`

Optional app-relative path to preserve through the hosted checkout. Absolute and protocol-relative URLs are refused.

### The topup object

**id** `string`

Top-up id (e.g. `top_01H...`).

**amount\_micro\_usd** `integer`

Amount requested, in micro-USD.

**status** `string`

Lifecycle state. One of:

* `pending` — the charge is being processed.
* `requires_action` — the payment needs additional authentication (e.g. 3-D Secure). Complete it via `action_url`, then poll.
* `succeeded` — the charge cleared and the balance was credited.
* `failed` — the charge was declined; the balance is unchanged.

**action\_url** `string | null`

Present only when `status` is `requires_action`: the URL to complete authentication.

**balance\_after\_micro\_usd** `integer | null`

Always `null` on this response — the balance moves only when the provider callback settles the charge. Read the [balance](#balance) instead.

**created\_at** `string`

Creation timestamp.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/credits/topups \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -H "idempotency-key: $(uuidgen)" \
  -d '{ "amount_micro_usd": 50000000 }'
```

```json Response theme={"system"}
{
  "id": "top_01H9LL...",
  "object": "topup",
  "amount_micro_usd": 50000000,
  "status": "requires_action",
  "action_url": "https://checkout.example/pay/cs_01H9LL...",
  "balance_after_micro_usd": null,
  "created_at": "2026-08-20T17:35:00Z"
}
```

### Confirming a charge that is not yet terminal

A charge can land in `pending` or `requires_action`, so the `status` on the create response is not always the final word.

> [!WARNING]
>
> **There is no `GET /v1/credits/topups/{id}`.** A top-up is not individually addressable — that route is not served and answers `404 not_found`. Confirm a charge by re-reading the balance or the ledger, not by polling a top-up id.

Two ways to reach the terminal state:

* **Poll the balance.** `GET /v1/credits/balance` reflects the credit as soon as the charge clears. This is the simplest check when you know the amount you expect.
* **Read the ledger.** `GET /v1/credits/ledger` lists entries newest-first; the settled top-up appears as a `credit` entry. Match it by `created_at` and `amount_micro_usd`.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/credits/ledger?limit=1 \
  -H "authorization: Bearer sk_live_..."
```

When `status` comes back `requires_action`, send the payer to `action_url` first; the balance does not move until they complete authentication.

> [!NOTE]
>
> When the balance is too low to cover a call, the API returns `insufficient_credits` (402); when a call would breach a configured per-agent or per-session cap it returns `budget_exceeded` (402). Top up (or raise the cap), then retry — see [Errors](https://vetta.sh/docs/api/errors.md) and [Agents](https://vetta.sh/docs/api/agents.md). Successful top-ups are recorded in the [audit log](https://vetta.sh/docs/api/audit-logs.md) as `credits.topped_up`.

## The plan

Credits pay for what an agent *does*. The **plan** is separate: it is the right to use the organization at all. Every organization needs an active plan before any route outside billing will answer. Without one the API returns `subscription_required` (402) with the message telling you exactly what to do.

| Plan | Price | Included credit | Free trial |
| - | - | - | - |
| `standard` | **\$20 per month** | \$20 each paid month | 7 days, with \$10 of credit |
| `pro` | **\$200 per month** | \$200 each paid month | none |

Included credit is added to the prepaid balance and **rolls over**: it never expires. Creating an organization does not start a plan. Buy one, then the organization works.

### Reading the plan

`GET /v1/credits/subscription` returns the current plan. Requires the `billing:read` scope, and is answerable whether or not a plan is active.

**include\_payment\_method** `boolean` (default: false)

`true` adds `payment_method`, the card the plan bills. It is read from the payment provider at call time, so it is left out of the default read. It also needs the `billing:write` scope, and is refused (`403 forbidden`) without it and for any call an agent makes.

**status** `string`

One of:

* `none` — no plan has ever started. Every gated route is refused.
* `active` — the organization can be used.
* `past_due` — the last payment did not go through. The organization keeps working until `grace_until`, then is refused.
* `canceled` — the plan ended. Every gated route is refused.

A free trial reads `active` too: there is no `trialing` status. `trial_end` says the trial is running.

**plan** `string | null`

`standard` or `pro`. `null` until the organization first subscribes.

**price\_micro\_usd** `integer | null`

The plan's monthly list price — `20000000` or `200000000`. `null` while `plan` is.

**included\_credit\_micro\_usd** `integer | null`

The credit each paid month adds to the balance. `null` while `plan` is.

**current\_period\_end** `string | null`

When the paid period ends, or `null` if no plan has ever started.

**grace\_until** `string | null`

While `past_due`, the moment the organization stops working. `null` means no grace remains.

**cancel\_at\_period\_end** `boolean`

`true` once a cancellation has been asked for. The organization stays usable until `current_period_end` and is refused after it.

**trial\_end** `string | null`

While the free trial is running, the moment it converts to a paid Standard month. `null` otherwise.

**pending\_plan** `string | null`

The plan a scheduled downgrade moves to at `current_period_end`, else `null`.

**trial\_eligible** `boolean`

Whether `POST` with `{"plan": "standard"}` from this caller would start the free trial. One trial per organization and one per person: an organization that already had a plan is not offered one, and neither is an organization whose caller, owners or creator — under any `+tag` or Gmail-dot spelling of their address — asked for a trial at another organization that started one (or one opened there in the last 24 hours), or belongs to or created an organization that used one. Leaving an organization does not reset it.

**trial\_days** `integer`

The free trial's length on this deployment, in days. `0` when trials are off.

**trial\_credit\_micro\_usd** `integer`

The credit the free trial starts with. `0` when trials are off.

**plans\_available** `string[]`

The plans this deployment sells: `["standard", "pro"]`, or `["standard"]` where Pro is not offered. `POST` or `PATCH` with a plan not listed answers `501 feature_not_configured`.

**payment\_method** `object | null`

Only with `include_payment_method=true`. The card the plan bills, as `{"last4": "4242"}`, so a screen can name it before an upgrade charges it. `null` when the organization has no plan with a card, or the card could not be read.

**plans** `object[]`

Each plan in `plans_available`, in the same order, as `{ plan, price_micro_usd, included_credit_micro_usd }`: its monthly list price and the credit each paid month adds. Present whether or not a plan is held, so the price can be shown before checkout.

**action\_url** `string | null`

Always `null` on a read; see below.

> [!NOTE]
>
> **An `active` plan whose `current_period_end` is more than three days in the past stops being honoured.** Renewals reach us as signed callbacks, and a callback that never arrives must not become permanent free access. Any later callback moves the deadline forward again, so a plan that is genuinely paid heals itself the moment the next event lands.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/credits/subscription \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{
  "object": "subscription",
  "status": "active",
  "plan": "standard",
  "price_micro_usd": 20000000,
  "included_credit_micro_usd": 20000000,
  "current_period_end": "2026-09-22T17:30:00Z",
  "grace_until": null,
  "cancel_at_period_end": false,
  "trial_end": "2026-09-22T17:30:00Z",
  "pending_plan": null,
  "trial_eligible": false,
  "trial_days": 7,
  "trial_credit_micro_usd": 10000000,
  "plans_available": ["standard", "pro"],
  "plans": [
    { "plan": "standard", "price_micro_usd": 20000000, "included_credit_micro_usd": 20000000 },
    { "plan": "pro", "price_micro_usd": 200000000, "included_credit_micro_usd": 200000000 }
  ],
  "action_url": null
}
```

### Starting or renewing the plan

`POST /v1/credits/subscription` opens a hosted checkout page and returns its URL as `action_url` (`201`). The body is optional: `{"plan": "standard"}` (the default — an empty body is Standard) or `{"plan": "pro"}`. Requires the `billing:write` scope.

**plan** `string` (default: standard)

`standard` or `pro`.

**The free trial.** When the plan is `standard` and `trial_eligible` is `true`, the checkout starts a **7-day free trial**: a card is required on the page, $10 of credit is granted when the trial starts (on an eligible card: see [Pricing](https://vetta.sh/docs/platform/pricing.md)), and unless it is cancelled first the trial converts to $20 per month — with the full \$20 of credit — when it ends. Cancel during the trial (`DELETE` below, or the account page) and nothing is charged. A caller who is not eligible gets the same checkout without the trial, billed on the day. Pro has no trial.

Like a top-up, it changes **nothing** on its own: the organization becomes usable once the payment (or the trial) is confirmed, which is normally a second or two after the customer completes the page. Re-read `GET /v1/credits/subscription` until `status` is `active`.

An organization whose plan is already `active` is refused with `state_conflict` (409): a second checkout would be a second plan billed. Change the tier with `PATCH` below instead. A company [being set up](https://vetta.sh/docs/api/organizations.md#set-up-a-company) starts here: the payment that makes its plan `active` **activates** it (`setting_up` turns `false`), and the crew it held while it was set up is built ([installs](https://vetta.sh/docs/api/apps.md)).

```bash theme={"system"}
curl -fsSL -X POST https://api.vetta.sh/v1/credits/subscription \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"plan": "standard"}'
```

```json Response theme={"system"}
{
  "object": "subscription",
  "status": "none",
  "plan": null,
  "price_micro_usd": null,
  "included_credit_micro_usd": null,
  "current_period_end": null,
  "grace_until": null,
  "cancel_at_period_end": false,
  "trial_end": null,
  "pending_plan": null,
  "trial_eligible": true,
  "action_url": "https://checkout.example/c/pay/..."
}
```

### Changing the plan

`PATCH /v1/credits/subscription` with `{"plan": "pro"}` or `{"plan": "standard"}` changes an `active` plan in place. Requires the `billing:write` scope; audited as `subscription.plan_changed`.

**plan** `string` *required*

`standard` or `pro`.

* **Standard → Pro is immediate.** The difference for the rest of the current month is charged at once on the card already on file, the billing date stays the same, and the answer reads `plan: "pro"`. Upgrading during the free trial ends the trial and charges $200 now. The proration invoice grants what it collected as credit, up to $200.
* **Pro → Standard takes effect at the end of the period already paid for.** Nothing is refunded and the Pro credit already granted is kept. Until then the plan reads `plan: "pro"` with `pending_plan: "standard"`; asking for `pro` again before then takes the downgrade back, free.
* Asking for the plan already held, with nothing pending, changes nothing and answers `200`.
* An organization with no `active` plan is refused with `state_conflict` (409) naming `POST`.
* A plan set to cancel at the end of its period is refused with `state_conflict` (409): there is no plan to change.

Like every route here, the read is the truth: the answer reports what was asked, and the confirmed callback rewrites it a moment later.

```bash theme={"system"}
curl -fsSL -X PATCH https://api.vetta.sh/v1/credits/subscription \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"plan": "pro"}'
```

```json Response theme={"system"}
{
  "object": "subscription",
  "status": "active",
  "plan": "pro",
  "price_micro_usd": 200000000,
  "included_credit_micro_usd": 200000000,
  "current_period_end": "2026-09-22T17:30:00Z",
  "grace_until": null,
  "cancel_at_period_end": false,
  "trial_end": null,
  "pending_plan": null,
  "trial_eligible": false,
  "action_url": null
}
```

### Cancelling the plan

`DELETE /v1/credits/subscription` stops the plan **at the end of the period already paid for** — never immediately, and nothing is refunded or charged again. Requires the `billing:write` scope, and like the two routes above it answers whether or not the organization is currently usable: a recurring charge you can only stop by paying first is not one a customer can stop.

The organization keeps working until `current_period_end`, and is refused after it. Re-read `GET /v1/credits/subscription` to see `cancel_at_period_end` become `true` — the state follows the payment provider's confirmation, so the read is the truth, not this response.

```bash theme={"system"}
curl -fsSL -X DELETE https://api.vetta.sh/v1/credits/subscription \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{
  "object": "subscription",
  "status": "active",
  "plan": "standard",
  "price_micro_usd": 20000000,
  "included_credit_micro_usd": 20000000,
  "current_period_end": "2026-09-22T17:30:00Z",
  "grace_until": null,
  "cancel_at_period_end": true,
  "trial_end": null,
  "pending_plan": null,
  "trial_eligible": false,
  "action_url": null
}
```

Cancelling also drops a scheduled downgrade. Cancelling during the free trial ends it at `trial_end` with nothing charged. To restart after cancelling, `POST /v1/credits/subscription` again — without a second free trial.

### Managing the payment method, invoices and cancellation

`POST /v1/credits/billing_portal` returns a link to the payment provider's own hosted account page, where a customer can change the card on file, download past invoices, and cancel. It takes no body and requires the `billing:write` scope. The link is single-use and expires — follow it, do not store it.

Like every other route on this page it changes **nothing** by itself. Whatever the customer does on that page arrives back as the same confirmed events that already move the plan, so re-read `GET /v1/credits/subscription` afterwards rather than assuming an outcome.

```bash theme={"system"}
curl -fsSL -X POST https://api.vetta.sh/v1/credits/billing_portal \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{
  "object": "billing_portal_session",
  "action_url": "https://billing.example/session/..."
}
```

An organization that has never held a plan has no account to manage, and this answers `404 not_found` rather than an empty page:

```json 404 theme={"system"}
{
  "error": {
    "type": "not_found",
    "code": "not_found",
    "message": "this organization has no billing account to manage; start the plan with `POST /v1/credits/subscription` first",
    "request_id": "req_..."
  }
}
```

### What stays open without a plan

An organization with no plan can still identify itself, read its own billing state, and pay. Everything else is refused with `subscription_required` (402).

| Open without a plan | Refused without a plan |
| - | - |
| `POST /v1/organizations`, `POST /v1/organizations/setup`, `POST /v1/auth/session`, `POST /v1/auth/users` | `/v1/agents/*`, `/v1/sessions/*`, `/v1/computers/*` |
| `GET /v1/me`, `GET /v1/organizations`, `GET /v1/organizations/{id}`, `GET /v1/organizations/{id}/members` | `/v1/deployments/*`, `/v1/files/*`, `/v1/skills/*` |
| `GET`, `POST` and `DELETE /v1/credits/subscription`, `POST /v1/credits/billing_portal` | `/v1/api_keys`, `/v1/webhooks`, `/v1/vaults/*` |
| `GET /v1/credits/balance`, `GET /v1/credits/ledger`, `POST /v1/credits/topups` | `/v1/identities/*`, `/v1/domains/*`, `/v1/connections/*` |

**A company being set up** ([organizations](https://vetta.sh/docs/api/organizations.md#set-up-a-company)) has no plan yet but is not locked: on top of the column above, it may run its CEO chat — reading and making its CEO, the CEO's sessions and their messages, events and plans, recording a template as `held`, renaming the company and setting its logo and brand kit — on the setup model, paid for by its setup credit. Files, transcription, voice and everything else in the right-hand column stay `subscription_required` (402) until it is activated.

> [!NOTE]
>
> **A session already running is never cut off mid-turn.** The plan is checked when work is *admitted* — starting a session, sending it a message, or a schedule firing — not on every turn of a session already under way. A lapse stops new work; it does not kill a task in flight. Plan changes are recorded in the [audit log](https://vetta.sh/docs/api/audit-logs.md) as `subscription.updated`, and a customer's own cancellation as `subscription.canceled`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.