Skip to main content

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.

ColumnWhat it holds
DateWhen the entry was written, to the second
TypeWhat kind of entry it is
DetailFor a charge, the instance it belongs to and how long it covers; for other entries, see below
AmountThe signed change to the balance, positive for credits added
BalanceThe balance immediately after this entry

Types you will see:

TypeConsole labelWhat produced it
topupCredits addedA confirmed Stripe payment
charge_gpuGPU usageRunning time, one row per instance per clock hour
charge_storageStorageStopped disk time, one row per instance per clock hour
adjustmentAdjustmentAny 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. The Type column then shows the reason instead of Adjustment:

meta.reasonConsole labelSignWhat happened
refundRefunddebitA charge was refunded. Only the amount this event moved is debited, so a second partial refund does not take the first one again
dispute_openedDispute openeddebitA chargeback was filed and the funds are gone
dispute_funds_withdrawnDispute — funds withdrawndebitThe funds were withdrawn on a dispute that arrived as an inquiry first, so nothing was debited when it opened
dispute_wonDispute woncreditYou won the dispute and its debit is reversed
dispute_reinstatedDispute — funds partly returnedcreditYou lost the dispute, but the card network returned part of the money. What comes back is credited once, and only as far as it was debited: when an earlier refund had already kept part of the payment from being debited, that part is not credited a second time
dispute_cappedDispute — adjusted to the paymentcreditA refund on the same payment arrived after the dispute was debited, so part of that debit is given back: the payment is never taken back twice
staff_adjustmentSupport adjustmenteitherSuperheat support moved the balance by hand. The Detail cell shows the note they wrote for you

A dispute never takes back more than the payment has left to give: the payment's amount (or the disputed amount, when the card issuer folds several payments into one dispute), less the refunds already recorded on it. So a $10 top-up that was refunded $4 and then disputed and lost costs $10 in total, whatever order the refund and the dispute arrive in — never $14. When a card issuer replaces a dispute with a second one for a corrected amount, each dispute is tracked on its own. If the card network returns part of a lost dispute in another currency, or without saying how much, nothing is credited automatically; Superheat staff are alerted to review it.

A formal 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. Since a stopped instance cannot start meanwhile, its GPUs are released too, and a Stop pressed during the freeze releases them at once, in both cases where the machine supports it — see While a payment dispute is open. 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 formal dispute is still open against the organization. Superheat staff can also lift a freeze after reviewing the dispute. An inquiry, where the issuer is asking a question and no money has moved, neither freezes the account nor posts a ledger row: debiting on a question that often goes nowhere would take credits from someone who has done nothing wrong. If the inquiry escalates into a formal dispute, the freeze and the debit follow then. Each dispute is debited at most once, and a won dispute credits back exactly what it debited.

For a charge, the Detail cell links to that instance's Billing tab and gives the time the row covers, such as 47m 0s. The row for the hour that is still running is marked in progress. Top-ups have no instance and name the payment method instead (via Stripe). A refund or dispute row says Refunded or Payment dispute, a support adjustment shows the note support wrote, and any other adjustment shows a dash.

One row per instance per hour​

Charges are still settled about once a minute, exactly as How billing works describes, but they no longer land in a row each. Each instance has one open charge row for the current clock hour (UTC), and each minute's settlement is added to it: its amount and the time it covers grow, and the balance after it goes down, until the row closes. A row closes, and the next settlement opens a new one, when:

  • the clock hour ends;
  • the instance changes state — a stop, a start, or a destroy writes a closed row of its own for the time up to that moment;
  • the rate changes, or the instance moves to another machine;
  • a top-up or an adjustment is written after it. A payment's balance is never rewritten, so a row never grows past one.

So an instance that runs all day produces about 24 rows, not 1,440, and a stop in the middle of an hour ends that hour's row early. 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 small 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 calendar days in your browser's time zone, so a late-evening run lands on the day you ran it. The console sends that zone to the API, which buckets the charges by it. A charge row counts on the day its hour started: in a time zone whose offset is not a whole hour, such as India's, up to an hour of usage around midnight can land on the neighbouring day.
  • 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"
ParameterRangeDefaultEffect
limit1 to 20050Entries per page
cursoran entry id—Returns entries older than this id
instance_idan 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. An adjustment carries meta.reason from the table above, and reason repeats it at the top level of the entry (null on an entry with none).

A metered charge carries a meta object with period_start, period_end, seconds (the time the row bills), rate_micro_per_hour and settlements (how many minute settlements it adds up; rows written before hourly rows carry no count and are one settlement each). Its created_at is when its first settlement ran, and the row only takes settlements from that same clock hour. accruing is true on the row an instance is still adding to: until it closes, its amount, meta.seconds, meta.period_end and meta.settlements grow, and the balance after it, and after any charge rows that follow it, goes down by the same amount. Every other entry is final as written. If you copy the ledger elsewhere, re-read rows that were accruing until they come back false.

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"
ParameterRangeDefaultEffect
days1 to 9030How far back to aggregate
tzan IANA time zone name, such as Europe/BerlinUTCWhose calendar days the charges are grouped by. A name the API does not know returns 422 INVALID_TIME_ZONE

This is the chart's data source: GPU and storage charges grouped by calendar day in tz, 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.