Which Sume Format run receipt fields to store in your database
Store id, format, status, created_at, finished_at, model, usage.debited_usd_micros, output and artifacts. Fetch the rest, because URLs and expires_at change.

Keep the run id, the Format identity, status, the three timestamps, model, the cost in usage.debited_usd_micros, and the output and artifacts you show to users. Do not store the poll URLs or expires_at as facts, because those describe the live run.
A receipt is large, and most of it is useful only while the run is in flight.
Columns worth having
Field names are from the Runs and results page (read 2026-10-03).
| Field | Store? | Why |
|---|---|---|
| id | Yes | Primary key; webhooks carry it as request_id |
| format.id, format.slug, format.version | Yes | Which Format version produced the output |
| status | Yes | completed, failed, canceled or skipped once terminal |
| created_at, started_at, finished_at | Yes | Latency and ordering |
| model | Yes | The orchestrator id that actually ran |
| usage.debited_usd_micros | Yes | The real cost of the run |
| output, output_error, artifacts | Yes | What the user gets; copy media you must keep |
| status_url, result_url, events_url, cancel_url | No | Derivable from id |
| expires_at, next_action, cancelable | No | Only true while the run is live |
Two traps
usage is null when spend could not be read, which is different from zero, so store null as null. Older receipts also leave the wallet fields null. See the cost helper for totalling.
Media URLs on media.sume.com are durable but public to anyone holding the URL. If you show them to your own customers, copy the file or proxy it.
import json, sqlite3
db = sqlite3.connect("runs.db")
db.execute("create table if not exists runs(id text primary key,"
" fmt text, status text, model text, micros integer, body text)")
def save(run):
u = run.get("usage") or {}
db.execute("insert or replace into runs values(?,?,?,?,?,?)",
(run["id"], run["format"]["slug"], run["status"],
run.get("model"), u.get("debited_usd_micros"),
json.dumps({"output": run.get("output"),
"artifacts": run.get("artifacts")})))
db.commit()
save({"id": "run_1", "format": {"slug": "teaser"}, "status": "completed",
"model": "gpt-6-sol", "usage": None, "output": {}, "artifacts": []})Keep the raw receipt too
If storage is cheap, keep the terminal receipt as JSON next to the columns. Fields are added over time, and a raw copy lets you backfill without calling the API for runs that may have aged out.
Sources
Related posts
More in Formats
- Shorts ad copy: 40-character headline, 90-character description
Google recommends 40-character headlines and 90-character descriptions for the Shorts ad CTA card. Draft copy in bulk with Sume and check lengths before upload.
- 250 SKUs for Black Friday: three Format queues of 100, 100 and 50
A Sume bulk queue holds 1 to 100 Format runs. For 250 SKUs, send three queues of 100, 100 and 50 with their own idempotency keys and concurrency window.
- Selling on many channels: queue 100 product videos overnight
Amazon says over 95% of independent sellers sell on several channels. Queue up to 100 Sume Format runs with a concurrency window and wake up to a media set.
- Sume bulk queue has no webhook: poll status_url, read counts.failed
A Sume Format bulk queue gives no queue-level webhook and a completed status that is not all succeeded. Poll status_url and read counts.failed and each item.
Written by Sume