Format run spend: wait for usage.final before you quote a cost

On a Sume Format receipt, debited is the cost, held and refunded are not spend, and final turns true only once no hold is open.

4 min readSume
All posts

Quote a Format run's cost from usage.debited_usd_micros, and only trust it once usage.final is true. debited is what the wallet actually deducted for the run and its thread. held_usd_micros and refunded_usd_micros are not spend, and final flips to true when no hold is open. billable_amount_usd_micros is a different figure: the generation spend counted against the run's cap, which excludes the agent's own LLM turn.

What does each usage field mean?

The run receipt's usage object carries more than the cap accounting. The same rows and the same fold sit behind GET /v1/usage?run_id=, so the receipt, the ledger and an agent's answer should not disagree.

Format run usage fields, from docs.sume.com/formats/runs and docs.sume.com/dashboard/usage (read 2026-10-03)
FieldMeaningIs it spend?
debited_usd_microsCaptured ledger rows of every operation type for the run and its thread, the turn's own LLM row includedYes: the cost
held_usd_microsHolds still open, parked pending_* rows includedNo
refunded_usd_microsHolds given back after a failure, cancellation or queue_fullNo
finaltrue once no hold is openTells you the totals have settled
billable_amount_usd_microsReserved plus captured generation counted against the cap; excludes the LLM turnNo: not a total cost
caplimit_usd_micros, counted_usd_micros, remaining_usd_microsNever a cost

Why wait for final?

While a run is in flight, billable_amount_usd_micros climbs and holds are open. A hold is a reservation, not a charge, so a total taken mid-run mixes reserved and captured amounts. Once final is true, nothing is parked open and the debited figure no longer moves for holds.

Two edge cases are documented. usage is null when spend could not be read at all, which is different from 0. And the three wallet fields (debited, held, refunded) are null on receipts written before the ledger answered.

How do I poll until it settles?

Read the receipt until final is true, with a bound so a null usage cannot loop forever.

for i in $(seq 1 12); do
  USAGE=$(curl -sS "https://api.sume.com/v1/format-runs/$RUN_ID" \
    -H "Authorization: Bearer $SUME_API_KEY" | jq -c '.data.usage')
  [ "$(echo "$USAGE" | jq '.final')" = "true" ] && break
  sleep 5
done
echo "$USAGE" | jq '{debited: .debited_usd_micros, held: .held_usd_micros, final: .final}'

Which source is the invoice?

The receipt is a receipt figure, not an invoice. GET /v1/usage and GET /v1/balance are the billing records. Generation that finished before a cancel or a failure is billed, and a later step failing does not refund it. A 4xx at create, an idempotent 200 replay and a skipped run cost nothing.

If you total a thread by hand, do not sum rows yourself: use the summary the usage endpoint returns for thread_id, run_id or job_id, since a refunded row keeps its hold amount in its own billable field.

What should a dashboard show while a run is live?

Show the cap and the counted figure, not a cost. usage.cap gives limit_usd_micros, counted_usd_micros and remaining_usd_micros, which answers how close the run is to its ceiling without pretending to be an invoice. When the run is terminal and final is true, replace it with the debited figure. If a run ends failed because it wanted to spend past its cap, the error code is the generic format_run_failed, so comparing the counted and cap figures is how you tell that apart from other failures before raising the brief.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume