Sume /v1/usage summary.final is false: a hold is open, not spent

Read GET /v1/usage?job_id= and book cost only when summary.final is true. held_usd_micros and refunded_usd_micros are not spend. Code to poll it.

5 min readSume
All posts

Book a Sume job's cost only when summary.final is true. GET /v1/usage with a job_id, run_id or thread_id adds a summary object, and final becomes true when no hold is open. While it is false, part of the money is still a reservation. debited_usd_micros is the amount the wallet really deducted. held_usd_micros and refunded_usd_micros are explicitly not spend.

A cost dashboard that sums every row of the ledger, or reads the figure the moment a job completes, can show a number that later moves. The summary is the supported fold, and the docs say never to sum the rows yourself. Two systems that disagree about one job's cost usually disagree about whether a hold counted, so make that rule explicit in your own reporting: spend is debited_usd_micros, and nothing else in the response is.

The summary fields that matter for billing

Add thread_id, run_id or job_id to the request and the response gives the cost of one thread, one run or one generation job. The limit parameter only caps the listed rows. The summary still folds over every row the scope caused.

FieldMeaningCount it as spend?
debited_usd_micros, debited_usdWhat the wallet deducted: captured rows of every operation type, including the agent's own turnsYes. This is the figure to quote
held_usd_microsHolds that are still open, parked pending_* rows includedNo
refunded_usd_microsHolds Sume gave back after a failure, a cancellation or queue_fullNo
finaltrue when no hold is openGate for booking

Ledger row statuses

Usage rows move through three statuses. reserved means Sume reserved the estimated usage before provider execution. captured means Sume captured the billable usage after a successful completion. refunded means Sume released the reservation after a failure or a cancellation before capture. A refunded row keeps its hold amount in billable_amount_usd_micros, so that field is not a cost either.

Rows carry thread_id, run_id, turn_job_id, script_run_id, script_run_call_index and settle_state. job_id also accepts the id of a turn. Then the sum includes the turn's own row and every job that turn commissioned.

Wait for final, then record

The sample polls the summary every two seconds, up to twenty tries, and returns debited_usd_micros only when final is true. If a hold is still open at the end it throws instead of guessing. It reads the key from the environment and uses the x-api-key header only.

const base = process.env.SUME_API_BASE_URL ?? "https://api.sume.com/v1";
const key = process.env.SUME_API_KEY;
if (!key) throw new Error("SUME_API_KEY is not set");

async function settledCost(jobId) {
  for (let i = 0; i < 20; i++) {
    const res = await fetch(`${base}/usage?job_id=${jobId}&limit=50`, {
      headers: { "x-api-key": key },
    });
    if (!res.ok) throw new Error(`usage ${res.status}`);
    const { summary } = await res.json();
    if (summary.final) return summary.debited_usd_micros;
    await new Promise((r) => setTimeout(r, 2000));
  }
  throw new Error("a hold is still open; do not book this as spend yet");
}

const micros = await settledCost(process.argv[2] ?? "job_123");
console.log(`debited: $${(micros / 1e6).toFixed(6)}`);

What to store

  • Store debited_usd_micros as integer micros, and convert to dollars only for display. The field names end in _usd_micros for that reason.
  • Store the job_id or run_id next to the figure, so a later correction can re-read the same scope.
  • Do not treat usage.billable_amount_usd_micros on a Format run receipt as cost. It counts reserved plus captured generation rows against the run's spend cap and leaves out the agent's own model turn.
  • If final never turns true, keep the row as pending and look at the job. A job that is still queued or processing can legitimately keep a hold open.

The ledger reference is on the Usage dashboard page. Pair it with the job status from Jobs and results when a hold stays open.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume