Transactions and spend
Every credit that enters or leaves an organization is written to an append-only ledger. The Billing page renders it two ways: a chart for the shape of your spending, and a table for the exact entries behind it.
The transaction list
The table is ordered newest first and shows 50 entries at a time; Load more fetches the next page.
| Column | What it holds |
|---|---|
| Date | When the entry was written, to the second |
| Type | What kind of entry it is |
| Detail | The instance the entry belongs to, and how many seconds it covers |
| Amount | The signed change to the balance, positive for credits added |
| Balance | The balance immediately after this entry |
Types you will see:
| Type | Console label | What produced it |
|---|---|---|
topup | Credits added | A confirmed Stripe payment |
charge_gpu | GPU usage | One settlement of running time |
charge_storage | Storage | One settlement of stopped disk time |
adjustment | Adjustment | Any signed move of the balance that is neither a top-up nor metered usage — mostly Stripe clawbacks, posted automatically. See below |
An adjustment is not only a manual correction. Stripe events post them on their own, and the row's meta.reason says which:
meta.reason | Sign | What happened |
|---|---|---|
refund | debit | A charge was refunded. Only the amount this event moved is debited, so a second partial refund does not take the first one again |
dispute_opened | debit | A chargeback was filed and the funds are gone |
dispute_funds_withdrawn | debit | The funds were withdrawn on a dispute that arrived as an inquiry first, so nothing was debited when it opened |
dispute_won | credit | You won the dispute and the debit is reversed |
A dispute also freezes the organization while it is open: POST /v1/instances and starting a stopped instance both answer 402 PAYMENTS_FROZEN, so a chargeback cannot fund GPU time. Closing the dispute lifts the freeze whichever way it went — a loss keeps its debit, and the balance is what gates you from there — but only once no other dispute is still open against the organization. An inquiry, where the issuer is asking a question and no money has moved, freezes the account but posts no ledger row at all: debiting on a question that often goes nowhere would take credits from someone who has done nothing wrong.
The Detail cell links to that instance's Billing tab. Top-ups and adjustments have no instance and show a dash.
Because settlement runs about once a minute, a single instance produces roughly 60 rows an hour, and around 1,440 a day. The list is meant to be sampled and filtered, not read end to end — for a per-instance total, open the instance and read Settled spend, and for a day-by-day total use the chart. Sub-cent amounts are shown to four decimal places, so a one-minute storage charge reads as something like $0.0007 rather than $0.00.
The spend chart
Spend — last 30 days plots one point per day, with the 30-day total beside the heading. Hovering a point gives that day's figure.
What the chart counts:
- GPU charges and storage charges only. Top-ups and adjustments are excluded — this is spending, not cash flow.
- Days are bucketed in UTC, so a late-evening run west of UTC lands on the following day's point.
- Days with no charges are plotted as zero rather than skipped, which is what gives the chart its flat stretches between bursts of work.
The endpoints
Both endpoints read the current organization's ledger. Send a bearer token — a signed-in session or an shk_ organization API key. With a session, X-Org-Id selects the organization, and without that header the personal organization is used; an API key is pinned to its own organization, and sending an X-Org-Id for any other one is rejected with ORG_MISMATCH. Neither endpoint requires admin, so a member or an API key can read them. See Authentication.
GET /v1/billing/transactions
curl "$SUPERHEAT_API/v1/billing/transactions?limit=100" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
| Parameter | Range | Default | Effect |
|---|---|---|---|
limit | 1 to 200 | 50 | Entries per page |
cursor | an entry id | — | Returns entries older than this id |
instance_id | an instance id | — | Restricts the page to one instance |
Entries come back newest first in items, alongside a next_cursor. Pass that value as cursor to get the next page; when it comes back null, you have reached the end of the ledger.
Each entry carries its id (which doubles as the pagination cursor), its type, the instance_id it belongs to or null for a top-up or an adjustment, a created_at timestamp, its signed amount, and the balance that resulted from it. Metered charges also carry a meta object recording the start and end of the period billed and the number of seconds it covers; an adjustment carries meta.reason from the table above.
To pull the charges for one instance:
curl "$SUPERHEAT_API/v1/billing/transactions?instance_id=$INSTANCE_ID&limit=200" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
GET /v1/billing/spend-daily
curl "$SUPERHEAT_API/v1/billing/spend-daily?days=90" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
| Parameter | Range | Default | Effect |
|---|---|---|---|
days | 1 to 90 | 30 | How far back to aggregate |
This is the chart's data source: GPU and storage charges grouped by UTC day, oldest first, each entry carrying a date and that day's total. Only days that had charges appear, so a quiet week produces no entries rather than a run of zeros — fill the gaps yourself if you are plotting a dense series.