Revenue: total is not realized revenue
sum(total) over invoices overstates what you collected — typically by low double digits — because total is the gross, pre-refund amount and includes discounts, proration, and partially-paid invoices. Realized revenue is what cleared, net of refunds:
- Amounts are in the currency’s major unit (dollars), not Stripe cents. Sanity-check one known invoice before scaling — porting Stripe intuition will put you off by 100×.
voidinvoices carry real-looking totals and must be excluded. Filterstatus = 'paid', not merely non-null.paidincludes negative totals (credit notes / refunds modeled as negative invoices) — expected, and correctly handled by theamount_paid − refunded_amountform.
Paying customers: status = 'active' is not “paying”
Active subscriptions include your entire free base and everyone mid-trial. A naive count() of active rows can overstate paying customers by orders of magnitude. A paying customer has an active, priced, non-trial base subscription — and you must dedup, because one customer holds several subscription rows (versions + add-ons).
Row count ≠ customer count: subscription rows exceed distinct customers because of plan versions, duplicate base rows, and add-ons. Anything customer-level needs
uniqExact(internal_customer_id), never count().Cross-reference: which overage?
Overage (on the balances side) has the same “two figures” hazard as revenue here — billable (what you invoice) vs displayed (what the dashboard header shows) diverge by undrawn grants. If a report combines revenue and overage, make sure both pages agree on which overage you mean. See Working with balances → Overage.All queries here use
env = 'live' (not production) and the `<catalog>`.`<namespace>.v2_3_…` addressing form. Join on internal_* ids — external ids are mutable and versioned. See Querying for the addressing and IS NULL footguns.