Types Reference
Every canonical type the SDK exports, with the full interface definition and a plain-English description of what it represents and when you’d see it. Money rule: every dollar amount isnumber, not string. Values come back at full precision. Round at render time, never before.
Null rule: a null means “not applicable here,” not zero. Most commonly: marginPercent: null means “revenue was zero, so margin cannot be computed.” Render as a dash, not 0%.
RevenueMetrics
The canonical revenue shape. Returned byanalytics.revenue, embedded inside the revenue block of customers.getWithRevenue and subscriptions.getWithRevenue.
revenue total and tell you WHERE it came from (usage events, recurring fees, seats, or one-time charges). eventCount is every event; eventCountWithNullCost is the subset whose cost couldn’t be calculated yet.
When you’d see it: any canonical analytics call, plus the revenue blocks on customer and subscription detail pages.
SubscriptionRevenue
One row per active subscription insideRevenueMetrics.bySubscription.
RevenueMetrics.bySubscription, returned by analytics.revenue and the two getWithRevenue overlays.
StrategyRevenue
One row per pricing strategy insideRevenueMetrics.byStrategy.
chargeType tells you whether this was a usage rate, a recurring fee, a seat fee, or a one-time charge. pricingModel tells you the shape of usage rates (flat/graduated/volume/credit_pool). For usage strategies, quantity is the total quantity that ran through the rate in the window; for non-usage strategies it’s 0.
When you’d see it: inside RevenueMetrics.byStrategy.
CostMetrics
The canonical cost shape. Returned byanalytics.costBreakdown.
prior is a same-length window immediately before startDate, populated only when you asked for it with includePriorWindow: true.
When you’d see it: analytics.costBreakdown.
CostByAgentRow
CostByCustomerRow
CostBySignalRow
signalId: null covers orphan events that aren’t tied to a signal.
CostByDayRow
date is an ISO string (JSON doesn’t carry Date objects); convert on your side if you need a Date.
CostByPlanRow
planId: null covers events whose subscription isn’t on a plan.
CostByModelRow
model: null covers non-LLM events and events whose model wasn’t recognized yet.
AgentEarned
The canonical shape returned byanalytics.agentEarned.
analytics.agentEarned.
AgentEarnedSubscriptionRow
AgentEarnedStrategyRow
quantity is the total quantity that ran through the rate.
InvoiceTotals
The canonical invoice-totals shape. Returned byanalytics.invoiceTotals.
billed is what you invoiced in the window (status issued or overdue), counted by invoice date. collected is the cash that landed in the window — payments that went through with a payment date in the window, net of refunds, counted by the day the money arrived (not the invoice date). draft is inventory of invoices not yet sent. outstanding is what’s open right now — issued or overdue and not yet paid, a current-state number with no date window that’s always zero or more. overdueAmount and overdueCount are current-state right now (not filtered by the date window). invoicesSentCount is how many non-draft invoices were dated in the window.
When you’d see it: analytics.invoiceTotals.
Mrr
Returned byanalytics.mrr.
mrr × 12). Stable across the current month (doesn’t move until the next month rolls over).
When you’d see it: analytics.mrr.
RunRateMrrResult
Returned byanalytics.runRateMrr.
analytics.runRateMrr.
RunRateMrrBreakdownRow
recurring is the monthly-normalized recurring fees. seatBased is seat fees based on max(booked, minimum) seats. usage is actual last-30-days events run through the pricing rate. The three add up to total, and all the totals add up to the top-level mrr.
CommittedMrrResult
Returned byanalytics.committedMrr.
RunRateMrrResult.mrr.
When you’d see it: analytics.committedMrr.
CommittedMrrBreakdownRow
usage column is minimum_commitment × rate (or 0 when the pricing strategy has no minimum set), not actual usage.
SubscriptionDetailWithRevenue
Returned bysubscriptions.getWithRevenue.
subscriptions.getWithRevenue.
CustomerDetailWithRevenue
Returned bycustomers.getWithRevenue.
customers.getWithRevenue.
UsageRecordResponse
Returned byusage.record and usage.recordBatch. Since SDK 0.19.0 it tells the delivery truth: a record appears in results.success (and counts toward successful) only when the server confirmed it was stored, and the top-level fields say whether the server was reached at all.
delivered is true only when a server response was actually received; false means the SDK swallowed a transport failure or a validation drop (fire-and-forget mode). buffered is how many records the local retry buffer actually accepted after a transport failure: 0 on validation drops (invalid records are never buffered), absent on delivered responses. If buffered > 0 the SDK will retry those records itself; re-sending them from your code would double-bill.
When you’d see it: every usage.record / usage.recordBatch call.
UsageRecordFailure
One row ofUsageRecordResponse.results.failed.
code is the server failure code (NEEDS_COST_BACKFILL, MISSING_VOLUME_DATA, INTERNAL_ERROR); it’s absent when the record never reached the server. stored is the retry oracle: false means not persisted (including transport failures) and safe to retry; true means persisted server-side despite the failure, so do NOT re-send it; absent means the server didn’t say, and the retry policy treats it as persisted. eventId and rawEventId are the correlation handles to quote when you contact support; rawEventId is null when the raw row itself couldn’t be created.
created says which records this event brought into being. Rows in results.success carry the same field. A stored failure still creates its customer, agent, and signal, so it reports them here. The field is absent (not false) on an idempotent replay, on a transport failure, and on older servers, so branch with created?.customer rather than comparing against false. Reviving a record you deleted in the dashboard reports false, because the record already existed. Full rules in Knowing what an event created.
When you’d see it: any partial failure from usage.record / usage.recordBatch.
CreditPoolInput
The three-number credit-pool shortcut. Accepted bypricingStrategies.createCreditPool (and inside CreatePricingStrategyData / UpdatePricingStrategyData as the creditPool field), and returned derived on every PricingStrategy as creditPool.
poolSize is how many units the pool covers each billing cycle, poolPrice is the flat fee charged every cycle whether or not it gets used, and overageRate is the per-unit price beyond the pool. Send this instead of hand-building tiers and the server compiles the canonical two-tier shape. Worth knowing: in the raw tiers array a credit pool’s FIRST tier rate is a flat fee for the whole pool while the second is per-unit. That asymmetry is easy to get backwards, which is why this type exists.
On reads, PricingStrategy.creditPool is this same shape derived server-side: null on every non-pool strategy, and on any pool whose stored tiers aren’t a clean two-tier shape (read tiers directly in that case).
When you’d see it: pricingStrategies.createCreditPool, strategy create/update params, and every returned PricingStrategy.
creditRates
Which signals draw from a credit pool, and how many credits one unit of each costs. Accepted onCreatePricingStrategyData and UpdatePricingStrategyData, and returned on PricingStrategy.
{ reportSignalId: 4, noteSignalId: 0.5 }. Without it a pool counts only its own signal, one credit per unit, which is what every existing pool does. With it, several signals share one balance at different costs. The pool’s own signal is a member whether or not you name it, at rate 1.
Every value must be above zero: to make a metric free, leave it out of the map. Send {} to clear every rate; omit the field and the stored rates stay put. null or absent on reads means a single-metric pool, and on every non-pool strategy.
When you’d see it: strategy create/update params on a credit_pool strategy, and every returned PricingStrategy.
CreditBalance
One credit-pool subscription’s countdown. Returned bycreditBalances.get, and in the balances array of creditBalances.list.
remainingUnits counts manual top-ups; overageInProgressUnits is the invoice’s own math (usage past the pool size, top-up blind). A topped-up pool can show units left AND overage at the same time; the invoice always matches overageInProgressUnits. remainingUnits goes negative once the pool is overdrawn, and nothing stops at zero: the pool is a meter, not a breaker. noActivePeriod: true (with periodStart/periodEnd nulled) means there’s no period counting down right now: the subscription never got a billing period, or the clock sits outside the stored window, like the gap between one period ending and the next day’s re-mint. implicitFullPool: true means no drawable grant exists for the current period slot (it can accompany noActivePeriod); the pool reads FULL, because a gap between two mint points is an untouched pool, not a drained one.
When you’d see it: creditBalances.list and creditBalances.get. See the Credit Balances API reference for the endpoint-level detail.
CreditBalanceListParams / CreditBalanceListResponse
belowPercent keeps only pools with that percent of the pool (or less) still left; 20 is the near-empty cut the dashboard uses, 0 narrows to pools at or past zero. asOf timestamps the computation, and the list comes back emptiest first.
Parameter types (for completeness)
All canonical analytics methods accept typed parameter objects. These are exported so you can use them in your own function signatures.RevenueMetricsParams
CostMetricsParams
InvoiceTotalsParams
customerId). That’s intentional: invoice math runs at the customer scope, not per-subscription or per-signal.
MrrParams
analytics.mrr uses a fixed “last complete calendar month” window. analytics.runRateMrr uses a fixed “last 30 days” window. analytics.committedMrr is not window-dependent at all.
AgentEarnedParams
Rounding and precision
Every money field in every type above is a rawnumber. No pre-rounding, no string wrapping.
Round exactly once, at the UI boundary:
Next steps
- Analytics: the seven methods that return these shapes
- Subscriptions: where
SubscriptionDetailWithRevenuecomes from - Customers: where
CustomerDetailWithRevenuecomes from

