# Billing records

Your organization's ledger by org API token: every charge down to the
node and the price window, daily totals in your calendar, the balance
now with the hourly burn rate and the balance over time, the balance
card with the card on file and the auto reload setting, spend
attributed per workload, and the unit prices in force. Five writes
start the console's billing actions for a person: three hand back a
hosted page URL, one tightens the spend limit, one configures auto
reload.

Contract: [`GET /api/billing/openapi.json`](https://nationalcompute.com/api/billing/openapi.json).

| Route | What it serves |
|---|---|
| `GET /api/billing/transactions` | the itemized ledger: every charge and deposit, newest first, one entry per resource per price window |
| `GET /api/billing/daily` | one row per calendar day: charges, deposits, the resources charged and their unit-hours |
| `GET /api/billing/summary` | balance, lifetime totals, this month's charges, spend limit, hold state, the card on file, the auto reload setting |
| `GET /api/billing/balance` | the balance now, the hourly burn rate the meter last assessed, and what it is made of by kind, cluster and unit price |
| `GET /api/billing/balance/history` | the balance as a series over a window: `start`, `step_s`, one value per bucket |
| `GET /api/billing/usage/workloads` | spend attributed per workload over a preset range |
| `GET /api/billing/rates` | the unit prices in force for your organization |
| `POST /api/billing/checkout` | `{usd}`: a hosted payment page URL for a person to complete; the deposit lands when they pay |
| `POST /api/billing/setup` | a hosted page URL that saves a card and charges nothing |
| `POST /api/billing/portal` | the payment portal URL: receipts and card changes |
| `PUT /api/billing/limit` | `{usd}`: set the monthly spend limit where none exists, or lower it |
| `PUT /api/billing/reload` | `{enabled, threshold_usd, amount_usd}`: the auto reload setting, a standing charge on the saved card whenever the balance runs low; a token may only disable it, lower the amount or raise the threshold |

Any of your organization's tokens reads these routes, cluster-bound or
org-wide. The organization is the token's own and nothing identifies it
on the wire. There is no cluster selector: the ledger is organization
wide, and each charge entry names its cluster. No call on this wire
moves money by itself: the three hosted page routes return a URL that
a person opens in a browser, and no card detail ever rides the token.
Auto reload is the one standing authorization. Once enabled it charges
the saved card whenever the balance falls under the threshold, so a
token may only tighten it: enabling it stays a console action.

```sh
curl -H "Authorization: Bearer $NC_TOKEN" \
  "https://nationalcompute.com/api/billing/daily?limit=31"
```

## Units

Amounts are integers in **microcredits**: fields ending in `_ucredits`,
where 1,000,000 microcredits = 1 credit = $1. Stamps are **RFC 3339
UTC** to the second (`2026-09-10T14:00:00Z`). Where a route aligns to
your calendar it takes `tz_offset_min`, the JavaScript
`getTimezoneOffset()` value (minutes UTC is *ahead* of local; `0` is UTC
days; a value outside ±900 is refused with `422 bad-request`).

## The ledger

`GET /api/billing/transactions` returns one row per ledger transaction,
newest first:

```json
{
  "transactions": [
    {
      "type": "charge",
      "start": "2026-09-10T14:00:00Z",
      "end": "2026-09-10T14:15:00Z",
      "amount_ucredits": …,
      "source": "metered",
      "note": null,
      "balance_after_ucredits": 3998500000,
      "charges": [
        {
          "kind": "burst",
          "cluster": "acme-train",
          "resource_id": "…",
          "resource_type": "GPU_…",
          "units": 8,
          "unit_hourly_price_ucredits": …,
          "start": "2026-09-10T14:00:00Z",
          "end": "2026-09-10T14:15:00Z",
          "amount_ucredits": …
        }
      ]
    }
  ],
  "next": "1757513700000.2"
}
```

| Field | Meaning |
|---|---|
| `type` | `charge` or `deposit` |
| `start`, `end` | the window the row accounts for — metering windows are about 15 minutes |
| `amount_ucredits` | always positive; `type` gives the sign |
| `source` | `metered` (the platform's compute meter), `inference` ([Marshall model usage](https://docs.nationalcompute.com/billing.md#marshall-model-usage) charged to credits), `public_research` (a meter row made only of Public Research pool lease entries), `card` (a card payment), `airdrop` (the credits a [public research organization](https://docs.nationalcompute.com/sign-in.md#public-research-organizations) receives at sign-up), `manual` (platform-authored: a wire deposit, a grant, an adjustment) |
| `note` | a platform-authored note on a manual row, else `null` |
| `charges[]` | the per-resource entries a metered row is made of — empty on deposits and manual rows; they sum to `amount_ucredits` |
| `balance_after_ucredits` | the balance after this row in ledger order: the newest row's is the balance now, and each older row's is the next newer one plus a charge or minus a deposit. Rows a filter hides still move it |
| `next` | the page cursor; pass it back as `before` for the next page. A full page always carries one (keyset paging cannot know whether more exists); a short or empty page ends the walk, `next` then `null` |

### Charge entries

Each entry is one resource over one price window, so a metered row lists
every node you held in that window, once per rate change:

| Field | Meaning |
|---|---|
| `kind` | `burst`: market capacity at the clearing rate. `base_load`: a [base load block](https://docs.nationalcompute.com/market.md#base-load-capacity) at its fixed rate. `fixed`: an attached node at the price posted for your organization. `inference`: one member's [Marshall model usage](https://docs.nationalcompute.com/billing.md#marshall-model-usage) over one metering pass. `public_research`: one whole node held under [Public Research](https://docs.nationalcompute.com/public-research.md#billing), at the flat rate in force when the lease was granted |
| `cluster` | the cluster the resource served; `null` on an `inference` or `public_research` entry |
| `resource_id` | the node's identity, `reservation:<id>` for a base load block, `public_research:<request id>` for a Public Research lease (the node rides `node`), or the shared volume's id |
| `resource_type` | the priced type — a GPU type, a CPU type, or `STORAGE_SHARED_GIB`. GPU and CPU type names carry the hardware model; read them from the payload |
| `units` | GPUs for a GPU type, nodes for a CPU type, GiB of used bytes for storage |
| `unit_hourly_price_ucredits` | the rate per unit-hour assessed for this window |
| `start`, `end` | the entry's own window inside the row's |
| `principal`, `model`, `prompt_tokens`, `completion_tokens` | `inference` entries only: the member (username), the public model id the calls named, and the token counts in the pass. `resource_type` and `resource_id` are `null` there; `units` and `unit_hourly_price_ucredits` are `0` |
| `principal_kind` | `inference` entries only: `member` when a person's credential made the calls (`principal` is then the username). `token` when a credential other than a person's made them (`principal` is then that credential's id) |
| `node`, `site`, `gpus`, `rate_ucr_per_gpu_hour`, `start_ms`, `end_ms` | `public_research` entries only: the node the lease held, the site id it ran on, the node's GPUs (the billed `units`), the flat rate per GPU-hour fixed when the lease was granted in microcredits (the same figure as `unit_hourly_price_ucredits`), and the entry's window as epoch milliseconds beside `start` and `end`. `resource_type` is the node's GPU type |
| `amount_ucredits` | `(end − start) / 1 h × units × unit_hourly_price_ucredits`, rounded to the microcredit |

### Query parameters

| Parameter | Meaning |
|---|---|
| `limit` | rows per page, 1–100; default 25 |
| `before` | the previous page's `next` (opaque) |
| `type` | `charge` or `deposit` |
| `source` | `metered`, `inference`, `public_research`, `card`, `airdrop`, or `manual` |
| `since`, `until` | a half-open window on the row's `end`: RFC 3339 (`2026-09-01T00:00:00Z`; a bare date is midnight UTC) or epoch milliseconds |

A bad value is `422 bad-request`; `detail` names the parameter.

### What the ledger read hides

- **Metered rows totalling $0** never appear — a metering window that
  cleared at $0 leaves an auditable zero row in the books, but the read
  returns neither it nor a day made only of such rows. Manual $0 rows
  stay (an adjustment can carry a `note` worth reading).
- **Who acted** never leaves: a manual row names no platform operator.
  `source` is the class, nothing finer.

## Daily totals

`GET /api/billing/daily` folds the ledger into one row per calendar day
in your zone, newest first:

```json
{
  "days": [
    {
      "date": "2026-09-10",
      "start": "2026-09-10T00:00:00Z",
      "end": "2026-09-11T00:00:00Z",
      "today": true,
      "charges_ucredits": …,
      "deposits_ucredits": 0,
      "transactions": 96,
      "resources": [
        {"kind": "burst", "resource_type": "GPU_…",
         "unit_hourly_price_ucredits": …,
         "units": 16, "unit_hours": 192.0}
      ],
      "clusters": ["acme-train"],
      "balance_end_ucredits": 3904000000
    }
  ],
  "next": "20706"
}
```

| Field | Meaning |
|---|---|
| `date` | `YYYY-MM-DD` in your zone (`tz_offset_min`) |
| `start`, `end` | the day's bounds as UTC stamps |
| `today` | the current local day — its totals are still running |
| `charges_ucredits`, `deposits_ucredits` | the day's totals |
| `transactions` | ledger rows folded into the day |
| `resources[]` | the day's charges per (`kind`, resource type, unit price), most unit-hours first: `kind` is `burst` / `base_load` / `fixed` / `public_research` as on a charge entry (`inference` charges fold into no resource line), `units` the distinct units charged that day, `unit_hours` their metered time |
| `clusters` | cluster names touched that day |
| `balance_end_ucredits` | the balance at the day's end; for today, the balance now |

`limit` counts days (1–100, default 31); `before` takes the previous
page's `next`, with the same full-page rule as the ledger. For the rows
behind a day, read the
[ledger](#the-ledger) with `since` and `until` set to its bounds.

## Balance and burn rate

`GET /api/billing/balance` is the one call a budget script polls: the
balance now, the hourly rate the meter last assessed your organization,
and what that rate is made of.

```json
{
  "balance_ucredits": 4213500000,
  "burn_ucredits_hr": …,
  "as_of": "2026-09-11T17:05:00Z",
  "billing_hold": null,
  "resources": [
    {"kind": "burst", "cluster": "acme-train", "resource_type": "GPU_…",
     "units": 16, "unit_hourly_price_ucredits": …,
     "rate_ucredits_hr": …},
    {"kind": "fixed", "cluster": "acme-train",
     "resource_type": "STORAGE_SHARED_GIB", "units": 800,
     "unit_hourly_price_ucredits": …, "rate_ucredits_hr": …}
  ],
  "clusters": [{"cluster": "acme-train", "rate_ucredits_hr": …}]
}
```

| Field | Meaning |
|---|---|
| `balance_ucredits` | the balance now; negative when charges outran deposits |
| `burn_ucredits_hr` | microcredits per hour: the sum of units × unit price over every resource still held when the newest metering window closed. A node released or a rate replaced mid-window is not part of it; the entry that replaced it is |
| `as_of` | the close of the metering window the rate was read from; `null` before the first charge |
| `billing_hold` | the hold state the [summary](#balance-and-totals) and the capacity reads carry, or `null` |
| `resources[]` | the rate by (`kind`, `cluster`, `resource_type`, `unit_hourly_price_ucredits`), largest first, with the `units` held at that price; `kind` is `burst` / `base_load` / `fixed` / `public_research` (a live [Public Research](https://docs.nationalcompute.com/public-research.md#billing) lease, `cluster` null). Jobs holding nodes at different clearing rates are separate lines, so the fold says what each limit price is costing |
| `clusters[]` | the rate per cluster, largest first |

The rate is read from the meter, not the market, so it reconciles with
the [ledger](#the-ledger) to the microcredit and covers every kind the
meter bills: burst, base load, attached nodes and storage, on VM and
Kubernetes clusters alike. The meter closes one window every 5 minutes,
about 5 minutes after the fact, so the figure is 5 to 10 minutes behind
real time and changes at most every 5 minutes; polling faster buys
nothing. The meter writes no window in which nothing was billed, so a
newest window older than 20 minutes reads as a rate of 0, with `as_of`
still naming it. For one Kubernetes cluster's live market rate, ahead
of the meter, read
[`GET /api/k8s/market/spend`](https://docs.nationalcompute.com/api/k8s-market.md#what-your-nodes-are-assessed).

### Balance over time

`GET /api/billing/balance/history` returns the balance as a series over
the last `hours` (1 to 8760, default 24; a value out of range is
`422 bad-request`). The path is the operation; `hours` rides the query
string.

```json
{"start": "2026-09-10T17:10:00Z", "step_s": 300,
 "balance_ucredits": [4300000000, 4300000000, 4289333333]}
```

`step_s` is the bucket width, `max(300, span / 400)` seconds, so a
window is at most 400 points; bucket `i` covers
`(start + i × step_s, start + (i + 1) × step_s]` and the last bucket
closes now. `balance_ucredits[i]` is the balance at the close of bucket
`i`: a balance is a step function, so buckets with no ledger event carry
the last value forward, seeded from the last event before the window,
and `0` before the first ever event. For the exact stamp of a deposit or
charge read the [ledger](#the-ledger), whose rows carry
`balance_after_ucredits`; for a day's closing balance read the
[daily totals](#daily-totals).

## Balance and totals

`GET /api/billing/summary`:

| Field | Meaning |
|---|---|
| `balance_ucredits` | the current balance; negative when charges outran deposits |
| `deposited_ucredits`, `charged_ucredits` | lifetime totals |
| `month` | `start`, `end`, and `charged_ucredits` for the current calendar month in your zone |
| `spend_limit_ucredits` | the monthly spend limit, or `null`; set on the console, or [tightened by token](#writes) |
| `spend_limit_used_pct` | `month.charged_ucredits` as a percentage of the limit, one decimal; `null` without a limit. The console's limit bar warns at 75 and turns critical at 99. The value can exceed 100 |
| `billing_hold` | the [hold state](#hold-state), or `null` — the same object the [capacity read](https://docs.nationalcompute.com/api/vm-capacity.md#reading-state) carries |
| `spend_limit_enforced` | `true` when reaching the spend limit holds your organization's capacity. `false` when the platform records the limit and does not act on it for your organization's clusters; the cap is then the instruction a person gave the agent and nothing else stops it |
| `billing_hold_enforced` | `true` when a balance at or below zero holds your organization's capacity. `false` when the platform records the balance and does not hold on it for your organization's clusters. `spend_limit_enforced` requires it |
| `base_load` | `true` when your organization is on a [base load](https://docs.nationalcompute.com/market.md#base-load-capacity) arrangement |
| `purchases` | `true` when self serve credit purchases are open to your organization on the console's [Billing page](https://docs.nationalcompute.com/billing.md#buying-credits) |
| `payment_method` | the card on file as `{brand, last4}`, or `null` when none is saved |
| `card_max_usd`, `card_window_days` | the card cap: your organization's card payments may total `card_max_usd` USD inside any rolling `card_window_days` days (currently $50,000 per 7 days). Every card payment counts, checkout and auto reload alike |
| `card_window_remaining_usd` | whole dollars a card payment can still carry right now: the cap minus the card payments inside the window, never negative. It rises as payments age out of the window |
| `card_window_blocked` | `true` while `card_window_remaining_usd` is below the smallest purchase: no card payment goes through right now |
| `min_usd`, `max_usd` | the smallest and the largest single checkout, whole dollars (platform constants; the card cap still applies) |
| `auto_reload` | the auto reload setting: `enabled`, `threshold_ucredits`, `amount_ucredits`, `last` (`status`, `reason`, `at`) for the newest attempt whether or not auto reload is enabled now (`null` before the first), and `cooldown_until`: the stamp until which a failed attempt pauses new ones, or `null` |
| `wire` | the [wire transfer rail](#wire-transfer), or `null` while `purchases` is `false` |
| `recent_deposits[]` | the last 20 deposits: `at`, `amount_ucredits`, `source` (`card`, `airdrop` or `manual`) |
| `inference` | [Marshall model usage](https://docs.nationalcompute.com/billing.md#marshall-model-usage) charged to credits, dollars with cents: `billed` (`true` when model usage is charged to the organization's credits), `free_tier_usd` (the lifetime free allowance per member; `0` when model usage is billed from the first call), `free_tier_remaining_usd` (what the caller has left of it; `null` for an org token, which has no person behind it, or while the allowance is `0`), `charged_month_to_date_usd` and `charged_lifetime_usd` (the organization's charged model usage) |
| `public_research` | [Public Research](https://docs.nationalcompute.com/public-research.md#billing) leases, as `{gpu_hours_month_to_date, charged_month_to_date_usd}`: GPU-hours this calendar month from the lease records (an open lease counts up to now; a free rate still counts) and the charged amount from the ledger's `public_research` entries, dollars with cents. Both `null` while the ledger cannot be read. The MCP `billing_read` summary view carries the same object |

These read what the console's balance card shows. The [writes](#writes)
start three of the console's actions for a person (buying credits,
saving a card, opening the payment portal), tighten the spend limit and
configure auto reload.

### Hold state

`billing_hold` is one object on the summary, the [balance read](#balance-and-burn-rate)
and the capacity reads, or `null` when nothing holds or counts down.

| Field | Meaning |
|---|---|
| `state` | `hold`: an active hold. `grace`: a countdown to a hold |
| `reason` | `balance` (the balance at or below zero), `spend_limit` (the month's charges at `spend_limit_ucredits`), or `balance+spend_limit` (both at once) |
| `stage` | `hold` only. `notice`: growth is blocked and nothing is reclaimed yet. `enforce`: capacity is being reclaimed |
| `enforce_after_ms` | `hold` only: epoch milliseconds when `stage` turns `enforce` |
| `remaining_min` | `grace` only: minutes until the hold opens. The grace is 0 by default, so this state is rare |
| `base_load_min` | present only while your organization holds a live [base load](https://docs.nationalcompute.com/market.md#base-load-capacity) block during a `balance` condition: minutes left before the block is cancelled |

A deposit cures a `balance` condition. Only a raised or cleared limit,
or the month rolling over, cures a `spend_limit` one. `billing_hold_enforced`
and `spend_limit_enforced` say whether either can open for your
organization at all.

### Wire transfer

`wire` carries what the console's Billing page shows under Wire
transfer, for amounts the card cap refuses. It is `null` while
`purchases` is `false`: an organization on a reserved arrangement pays
by invoice.

| Field | Meaning |
|---|---|
| `bank_name`, `aba_routing`, `bank_address` | the receiving bank; `aba_routing` serves domestic wires and ACH |
| `aba_routing_alt` | the routing number to use when the sending bank does not recognize `aba_routing` |
| `swift_bic`, `intermediary_swift_bic` | international wires: the receiving bank's SWIFT code and the intermediary bank the wire must name |
| `beneficiary_name`, `account_number`, `account_kind`, `beneficiary_address` | the beneficiary |
| `reference` | your organization's memo for the current UTC day. It rolls at midnight UTC: read it on the day of the wire. It must ride the wire's reference or memo field, or the deposit is not matched to your organization promptly |
| `notify_email` | the address to email when the wire is sent |

The bank and beneficiary values are platform constants and appear on
this wire and the console only.

## Spend per workload

`GET /api/billing/usage/workloads?range=7d` re attributes charge
entries to the jobs and pods that occupied the charged nodes, from the
platform's occupancy samples. The figures are approximate by design:
idle capacity and spend that cannot be attributed ride as their own
labeled series, never smeared across jobs.

| Parameter | Meaning |
|---|---|
| `range` | one of `1d`, `7d`, `30d`, `90d`, `mtd`, `ytd`, `lastmonth` (default `7d`); anything else is `422 bad-request` |
| `tz_offset_min` | the calendar the buckets use, as on the other reads |

The response carries `starts[]` (one stamp per bucket) and `end`,
`labels[]` (`HH:00` for `1d`, `MM-DD` otherwise), `today_index` (the
bucket holding today, `null` for `lastmonth`), and `groups` with two
groupings, `workload` and `user`. Each grouping has `order[]`, `series`
(per name: `spend[]` in microcredits and `unit_ms[]`, milliseconds of
unit time, per bucket) and `meta` (per name: `label`, `who`, `cluster`,
`unit`, `handover_ucredits`; the user grouping adds `workloads`, how
many workloads the user ran). `who` names the person who started the
workload: the Slurm username, or the creator of the Kubernetes object
as the cluster's audit log recorded it (the console's "Started by").
A workload with no recorded creator carries its Helm release or app
group instead. A workload with neither carries an empty `who`, and the
user grouping lists it under "No user recorded". Series names are
workload ids plus three sentinels: `IDLE` (held capacity no job used),
`UNATTRIBUTED` (spend no sample explains) and `OTHER` (the fold of the
smaller workloads, the way the console's chart folds them). The payload
is served through a cache: `cache_age` is its age in seconds, and
`stale: true` means a refresh is in flight, so read again in a moment
for fresh figures.

A range nobody has read yet is computed on first request. When the
computation takes more than a few seconds, the call answers
`202 Accepted` with `{"pending": true, "retry_after_s": 3}` and a
`Retry-After` header instead of holding the connection. Poll the same
URL after that many seconds. The `200` lands once the figures are
ready, and later reads of the same range come from the cache.

## Prices in force

`GET /api/billing/rates` lists every resource type priced specifically
for your organization right now:

```json
{"rates": [
  {"resource_type": "GPU_…", "label": "…", "unit": "GPU",
   "unit_hourly_price_ucredits": …, "since": "2026-09-01T00:00:00Z"}
]}
```

`unit` is `GPU`, `CPU`, or `node`. Burst capacity does not appear here —
it bills at the [clearing rate](https://docs.nationalcompute.com/billing.md#what-the-meter-measures),
which the [price feed](https://docs.nationalcompute.com/api/market-feed.md) publishes.

A public research organization also sees its
[Public Research](https://docs.nationalcompute.com/public-research.md#billing) rate as one line per
site that offers the program: `resource_type` the node's GPU type,
`label` `Public Research · <GPU model> GPU`, `unit` `GPU`,
`unit_hourly_price_ucredits` the flat rate a lease granted now is
fixed at, and `since` the stamp the rate was last set. A free rate is
listed at `0`; a standard organization never sees the line. The MCP
`billing_read` rates view carries the same rows.

## Writes

The five writes are the console's billing actions an agent starts for
the person it works for. Bodies are JSON; every amount is a whole
number of dollars.

```sh
curl -X POST -H "Authorization: Bearer $NC_TOKEN" \
  -H "Content-Type: application/json" -d '{"usd": 250}' \
  https://nationalcompute.com/api/billing/checkout
```

```json
{"url": "https://checkout.stripe.com/c/pay/…"}
```

| Route | Body | What comes back |
|---|---|---|
| `POST /api/billing/checkout` | `{"usd": 250}` | a hosted payment page URL ($1 = 1 credit). Hand it to a person; the page never offers the organization's saved card, the person enters a card, so the URL alone cannot pay. The deposit lands on the ledger when they pay, so read the [summary](#balance-and-totals) again afterwards. Card payments are capped at `card_max_usd` total per rolling `card_window_days` days per organization. `card_window_remaining_usd` on the summary is what a card can still carry right now. Larger amounts go by wire transfer |
| `POST /api/billing/setup` | none | a hosted page URL that saves a card and charges nothing. The on ramp for auto reload |
| `POST /api/billing/portal` | none | the payment portal URL: receipts and card changes. Before the first purchase there is no customer record to manage |
| `PUT /api/billing/limit` | `{"usd": 500}` | `{"ok": true, "spend_limit_ucredits": 500000000}`. Sets the monthly spend limit where none exists, or lowers the one in force |
| `PUT /api/billing/reload` | `{"enabled": true, "threshold_usd": 50, "amount_usd": 150}` | `{"ok": true, "enabled": true, "threshold_ucredits": 50000000, "amount_ucredits": 150000000}`: the setting as stored. Tightens the auto reload setting: lowers `amount_usd`, raises `threshold_usd`, or disarms it with `{"enabled": false}` (nothing else needed; the stored amounts stay for the next enable). A write that changes nothing is a no op |

The spend limit is the cap a person sets on the month's charges. Read
`spend_limit_enforced` on the summary before relying on it. When it is
`true`, reaching the limit holds capacity until the limit is raised or
the month rolls over. When it is `false`, the platform records the
limit and does not act on it for your organization's clusters. The
flag differs between organizations: read it, never assume it. A token
may only tighten the limit:
raising the limit, or clearing it with `null`, is refused
`403 session-required` and stays a console action. The hosted page URLs
are short lived; request a fresh one if it expired before the person
opened it.

Auto reload is a standing spending authorization: every charge it fires
runs off session with no further approval and counts against the card
cap. A token may only tighten it, the spend limit's posture: disable it,
lower `amount_usd`, or raise `threshold_usd`. Enabling it, raising the
amount, or lowering the threshold is refused `403 session-required` and
stays a console action for a person. An update sends `enabled: true`
with both amounts (`threshold_usd` 1 to 1000000, `amount_usd` within
`min_usd` and `max_usd`; the bounds apply to every write, a disable
included). A failed charge pauses attempts for 24 hours, or until a
change is saved on the console, whichever comes first; the summary's
`auto_reload.cooldown_until` names the stamp, and a no op by token never
moves it. On the [MCP server](https://docs.nationalcompute.com/api/mcp.md#two-phase-confirmation) the
same write runs in both directions, because a person approves the
preview there: `billing_write` carries `human_confirmation` on it.

## Errors

| Status | Code | When |
|---|---|---|
| `401` | `unauthorized` | missing, malformed, expired, or revoked token |
| `403` | `reserved-arrangement` | credits are managed by the platform team for your organization; purchases are not self serve |
| `403` | `session-required` | raising or clearing the spend limit by token, or enabling auto reload, raising its amount or lowering its threshold by token: console actions |
| `409` | `card-window` | the card cap for the rolling window is spent (`card_window_remaining_usd` is `0`); wait for payments to age out of the window, or pay by wire transfer |
| `409` | `no-purchases` | the payment portal before the first purchase: nothing to manage yet |
| `409` | `no-card` | enabling auto reload with no card on file; `POST /api/billing/setup` hands back the page that saves one |
| `422` | `bad-request` | a query value or body field out of range or malformed; `detail` names it |
| `422` | `card-cap` | `usd` above what a card can still carry this window (`card_window_remaining_usd` on the summary); lower the amount, or pay larger amounts by wire transfer |
| `429` | `rate-limited` | past the token's request budget for the minute (300 by default; the vm contract states the figure); wait `retry_after_s` |
| `503` | `purchases-unavailable` | self serve purchases are off |
| `503` | `station-unavailable` | the ledger or the payment provider is unreachable; retry |

Every error is the plain `{"error": code}` body of the
[API conventions](https://docs.nationalcompute.com/api/index.md).
