Export a run
This guide writes one run's result into a directory, once as JSON documents and once as a CSV table. What every file and every member holds is in the export formats reference.
Before you start
- The engine database reachable from the machine you run the CLI on, and its connection string.
- The id of the run to export, completed or finalized.
- One empty directory per export, or a path that does not exist yet.
jqon the machine, for the two reads below.tally-engineat the version the deployment runs, with its subcommands on the engine CLI page.- The engine settings page, which names
TALLY_ENGINE_DB_URL, the one variable this guide sets.
Write the documents
Point the engine at its own database. An export without
--rollupreads that database alone:shexport TALLY_ENGINE_DB_URL='postgres://tally:password@db.internal:5432/tally_engine?sslmode=require'Export the run as JSON. The first line names the run, the period and the directory, and one line per kind of file follows it:
shtally-engine export --run <run id> --format json --out ./2026-07/jsontextrun <run id> exported for 2026-07 as json into ./2026-07/json wrote run.json and <n> statements wrote kickbacks.json with 0 kickbacksRead the run's stats off the index.
run.jsonis written last, after every file it names is on stable storage:shjq .stats ./2026-07/json/run.jsonjson{ "snapshot_at": "2026-08-03T00:00:00Z", "candidates": 812, "usage_records": 1204, "rated_records": 3612, "statements": 6, "unpriced": [ {"platform": "openstack", "resource_type": "image", "count": 4}, {"platform": "openstack", "resource_type": "loadbalancer", "count": 2} ] }The four counts are always there, and a list stands beside them for each class of finding the run recorded. A clean run reads as the counts alone.
Read the costs one project carries for another. A project that a relation attributes another project's resources to carries them under
related_costsrather than in its own line items, with the type of the relation that claimed them. A Gardener project whose shoots run on an infrastructure tenant has no usage of its own and gets a statement of just that shape:shjq '.related_costs[] | {relation_type, project_id, total}' \ ./2026-07/json/statement-garden-prod-eu1%2Falpha.jsonjson{ "relation_type": "infrastructure_tenant", "project_id": "9c4a1b2d3e4f5061728394a5b6c7d8e9", "total": 184.20 }
Write the table
Export the same run as CSV, into a directory of its own:
shtally-engine export --run <run id> --format csv --out ./2026-07/csvtextrun <run id> exported for 2026-07 as csv into ./2026-07/csv wrote rated.csv with <n> rated records wrote kickbacks.csv with 0 kickbacksAn
--outthat already holds files is refused, and nothing is written:shtally-engine export --run <run id> --format csv --out ./2026-07/csvtext--out: ./2026-07/csv is not empty, and an export does not remove what an earlier one left there
Check the result
The JSON directory holds the index, one statement per project the run billed, and the settlement:
shls ./2026-07/jsontextkickbacks.json run.json statement-garden-prod-eu1%2Falpha.json statement-os-prod-eu1%2F9c4a1b2d3e4f5061728394a5b6c7d8e9.jsonA file is named after the statement key, the cloud and the project id joined by a slash, escaped twice, which is where the
%2Fcomes from.The CSV directory holds the rated records and the settlement:
shls ./2026-07/csvtextkickbacks.csv rated.csvExporting one finalized run twice into two clean directories yields the same files: a finalized run's records no longer change. An export with
--rollupis the exception: it reads the membership from the registry when the export runs.