Reports and charts
A list shows records, and adding up list pages gives wrong numbers. The reports return totals and time series for a range, so a chart, a finance export and a report to your board agree. This guide shows which report answers which question, and what they have in common.
Before you start
- Revenue, cost and margin recorded with
track()andtrace(), for the revenue, margin and cost reports. - Credit grants and usage windows, for the credits and allowance reports.
Which report answers what
Margin by customer, model or outcome definition, outcome counts, breakage and top-up chargebacks are the older reports. They work the same way, each with its own parameters: margin groups by customer, model or outcome_definition and has no time buckets, so group_by=day is a 400; outcomes buckets by granularity and groups by status, outcome_definition, reason_code or source; top-up chargebacks group by provider or for the whole account.
What the reports have in common
- Buckets are UTC.
group_bytakesday,weekormonth, and a week starts on Monday. A bucket with nothing in it is returned with zeros, so a chart has no gaps, except in the cost report, which has a row only for a bucket with costs, and the breakage report, which has rows only for credit types with an expiry in the range. - One currency. Amounts are never converted or added across currencies. Send
currency, or leave it out when everything in the range is in one. If the range has more than one, the call returns400withparameter_invalidoncurrency. - Credits and money are kept apart. Credits are decimal strings per credit type. Money is in minor units of the report’s
currency. A report never adds one to the other. - A run without a margin isn’t zero. A run has a margin only with a confirmed charge and a traced cost. A row with no such run says
has_margin: falseand leavesmarginandmargin_percentasnull, so you can show “no margin data” instead of a flat line. - The latest bucket isn’t final. Outcome charges confirm after their window, so revenue for the last few days still moves. Shade the newest bucket in a chart rather than reading a margin from it.
- Reports read recorded data. Recorded usage is never repriced, and a new rate card version applies only from its start date. A chart doesn’t change when prices do.
Margin over time and by customer
GET /v1/runs/summary totals every run that matches the filters. Add group_by and the same totals come back as rows:
The totals at the top cover every matching run. group_by=customer returns one row per customer instead, the highest margin first (confirmed revenue less cost), up to limit (50 by default, 500 at most), and sets has_more when there are more. A customer whose runs have no margin has has_margin: false and sorts last, so a ranking leaves it out. The filters are the ones on list runs.
Revenue by invoice line type
The revenue report splits recognized revenue by the type of invoice line that billed it, in by_line_type:
All four are always listed, and they add up to recognized. A plan’s recurring fee is not in the report: the policy recognizes what usage and outcomes earn, and the fee stays a line on the invoice. Runledger has no separate “committed” or “pay as you go” revenue: what a customer commits to appears as the shortfall line, and everything past the included quantity as overage. Add group_by=month (or week or day) to get periods, with the same figures for each bucket.
The report applies one set of rules, named by policy_version, so two reports with the same version were counted the same way:
- Usage and outcome charges are recognized when they are posted, so a backdated event lands in the period it was recorded and a closed period’s figures do not move. A confirmed charge counts at confirmation, and a reversal of a confirmed charge comes off
outcomeand goes intoreversedin the period it happened. A minimum commitment’s shortfall is recognized when the period’s invoice finalizes. - Prepaid credits are valued at the cost basis of the grants they were drawn from. Promotional and included credits bring no revenue. A prepaid charge still pending has drawn nothing, so it is not in
pending: its credits are still indeferred. pending,reversibleanddeferredare standings at the period’s end;reversedandbreakageare the period’s movements. Buckets are UTC and clipped to the range.
These are operating figures. Confirm the treatment with your adviser before a finance team relies on them.
Credits sold, used, lapsed and outstanding
The credits report covers paid credits, the ones with a cost basis, per credit type. sold is what became usable in the bucket, consumed is what was drawn less what reversals gave back, expired is what lapsed unused, and outstanding is what is left at the end of the bucket, valued at the cost basis of its grants in outstanding_value. That value is what the revenue report calls deferred. Promotional and included credits that lapse are in the breakage report.
Payments and failures
The payments report counts payments by status over time: succeeded, failed and disputed, each with a count and an amount. A payment is counted once, in the bucket it was created in, under its current status, and a payment still pending isn’t in the report. Send split_by=provider for a row per provider in each bucket, and kind to look at top-ups or invoice payments alone.
A card chargeback on a top-up is a payment in disputed. It isn’t an outcome dispute. Top-up chargebacks has the rate and the credits at risk.
Who is close to the end of an allowance
The allowance use report lists each customer’s allowance, in the window the customer is in now, ranked by percent_used. Send percent_used_over to keep only customers past a share, such as 0.8 for more than 80%. It has no money in it. For one customer’s windows, with holds and the one that binds first, use the usage summary.
For an allowance with scope: per_member, a row is the member who has used the most, named in member_id. The report returns up to limit rows (50 by default, 500 at most) and sets has_more when there are more.
Cost by model over time
The cost report is cost alone: one row per bucket, provider and model, from every cost recorded with trace() or POST /v1/costs, whether or not a run has revenue. Use it to see which model is driving spend, and the margin report when you want cost beside revenue. A provider price change shows up in it as a step from the date the new price applies.
Export the rows
POST /v1/exports writes a file of the API’s own rows for your warehouse or your finance team: balances as of a moment (as_of), ledger_entries or outcome_charges created in a period, or the revenue report by day, as csv or jsonl. The export starts pending while the worker writes the file, and export.ready or export.failed says when it is done. GET /v1/exports/{export_id} then signs a download_url that lasts an hour; read it again for a fresh one, since GET /v1/exports lists exports without their links. The file is kept for seven days, after which the export is expired.
API reference
Run summary, revenue report, credits report, payments report, allowance use report and cost report.
Next steps
- Cost attribution and margin explains how a run gets its revenue, cost and margin.
- Credit expiry and breakage covers the credits that lapse.
- Usage meters show one customer’s windows.