Analytics schema reference
This page documents all 20 views available to the
Analytics API. You can also fetch this live for your account from
GET /schema.
Conventions:
- Monetary columns are cents. Divide by 100 for display. Most are integers;
fact_cost.cost_amountis fractional cents (Decimal). - Credits are not money and not whole numbers. The
fact_credit_transactionamount and balance columns areDecimal(30, 6): a spend can be a fraction of a credit. Do not divide them by 100. UInt64/Int64values are returned as JSON strings (precision-safe); parse client-side.Decimalvalues are returned as JSON numbers, not strings; the 64-bit quoting above does not apply to them. ADecimalbeyond 2^53 is therefore already rounded in the response; selecttoString(col)to get its exact digits as a string.Nullable(...)columns can benull.- Facts join to dimensions on the
*_idcolumns called out below.
How the tables join
dim_customer is the hub: every fact table carries a customer_id. Usage
facts (fact_signal, fact_cost, fact_delivered_value) also carry a
product_id and link to each other through trace_id and signal_id.
Revenue facts chain from fact_order through fact_order_line to
fact_invoice_line, and fact_payment and fact_usage_summary tie back to
invoices and order lines. fact_credit_transaction joins to
dim_credits_currency for credit reporting.
Customer units (the teams, departments, or users inside a customer) are in
dim_customer_unit; fact_signal and fact_credit_transaction carry the unit
each row was attributed to. Spend caps on units are in dim_customer_unit_cap
(every version) and dim_customer_unit_cap_current (the cap in force now, with
its current period), and every change to a unit is in fact_customer_unit_event.
Common join keys:
Dimensions
dim_customer
One row per customer. Join id to any fact’s customer_id. Supports hierarchy
rollups (parent_customer_id), segmentation (annual_revenue, status,
churn_date), and geography (country/state/city).
dim_customer_unit
One row per customer unit: a team, department, tenant, or user inside a customer, arranged as a tree. Deleted and draft units stay in the table so their history keeps a name.
Join from fact_credit_transaction on customer_id and
external_customer_unit_id = external_id. Join from fact_signal on
customer_id and customer_unit_id = id; when customer_unit_id is null (the
signal named a unit that did not exist yet), match external_customer_unit_id = external_id within the same customer instead. Use LEFT JOIN so unassigned
usage stays in your totals: an unmatched row returns an empty name, not
null. See usage by customer unit for
complete queries.
dim_customer_unit_ancestor
One row per unit and each unit above it, including the unit itself at depth 0.
Use it to total a unit together with everything below it: join the unit a fact
belongs to on unit_id and group by ancestor_id. A row counts once for every
ancestor, so report one level of the tree at a time.
dim_customer_unit_cap
One row per version of a spend cap on a customer unit, including versions that
were replaced or ended. A cap limits how many credits of one credits currency a
unit and every unit below it can spend per period. Changing a cap adds a
version; nothing is rewritten. For the cap that applies now, use
dim_customer_unit_cap_current.
Caps are spend policy: this table and dim_customer_unit_cap_current return
rows only to a caller that may read billing data, an API key with
read:billing or a dashboard user, and in a custom view only when the view has
capsEnabled: true. Anyone else gets no rows, not an error.
The cap in force at a time t is the highest version of the unit and credits
currency whose window covers t: effective_from <= t and effective_until
is null or after t.
dim_customer_unit_cap_current
The cap in force now on each unit and credits currency, with its current
period, resolved the same way as the customer unit cap endpoints. It has every column of
dim_customer_unit_cap plus:
Usage counted against a cap is the net SPEND of its credits currency by the
cap’s unit and every unit below it, from period_start up to the earlier of
period_end and effective_until. See
cap usage so far for the query; it gives the
same figure as the cap API’s currentPeriod.used. Caps on deleted units are
included, because they still apply to the units below until they are ended;
join dim_customer_unit and keep status = 'ACTIVE' to list only the caps the
API lists.
dim_product
One row per product (also called an agent). Products are AI agents/services
or prepaid credit bundles. Join id to any fact’s product_id.
dim_plan
A plan is a pricing configuration within a plan group. Plans form an
upgrade/downgrade chain via has_next_plan/has_prev_plan. Join from
fact_order.plan_id.
dim_credits_currency
Defines the types of credits your organization uses. Join from
fact_credit_transaction.credits_currency_id or dim_product.credits_currency_id.
dim_organization
Your organization. Typically one row.
Usage
fact_signal
One row per signal (event). This is the core usage metric. COUNT over time, group by
signal_name, customer_id, or product_id. Join trace_id to fact_cost
for cost-per-signal, or signal_id to fact_delivered_value for value
delivered.
Cost
fact_cost
One row per AI/LLM call. SUM(cost_amount) for total spend; group by
vendor, model, customer_id, or product_id. Includes token usage and
latency.
cost_amount is exact fractional cents: a single AI call usually costs a
fraction of a cent, so values look like 3.71 (= $0.0371). Sums match the
Cost Explorer in the Paid UI.
Delivered value
fact_delivered_value
The business value delivered per signal, by value type. Join signal_id to
fact_signal, or customer_id/product_id to dimensions.
Rows can be denominated in different currencies: organizations using
value models can configure non-USD currencies, and delivered_value_cents
is always in that row’s delivered_value_currency. Never sum
delivered_value_cents across currencies; group by
delivered_value_currency (or filter to one currency) first.
Revenue and billing
fact_order
Contracts and subscriptions. One row per order. Join customer_id to
dim_customer, plan_id to dim_plan. Compare total_billed_amount to
total_amount for billing progress.
fact_order_line
Product-level order line items link products to revenue. SUM(total_amount)
by product_id for revenue-by-product. Filter effective_until IS NULL for
current lines only.
fact_invoice
One row per invoice. SUM(total_incl_tax) for revenue; filter status = 'posted'
for finalized invoices. Use due_date + payment_status for aging analysis.
fact_invoice_line
One row per line on an invoice. Join order_line_id → fact_order_line.id →
dim_product.id to break invoiced revenue down by product.
fact_payment
One row per payment received. SUM(amount) for cash collected; filter
payment_status = 'posted' for confirmed payments. Join invoice_id to
fact_invoice.id for reconciliation.
fact_usage_summary
Usage per signal per billing window: one row per priced attribute of an order
line per billing window, built from the same billing record as invoicing.
Filter status = 'billed' and SUM(subtotal) by signal_name to see which
signals drive revenue. Compare signals_quantity to included_quantity to see
overage.
A few rows from older billing share one invoice line between two windows; the
line’s amount is split between them, so SUM(subtotal) still matches the
invoice.
Consistency. Billing windows and invoices are copied into the analytics
store independently. Just after a window closes, it can briefly read billed
with invoice_id, invoice_line_id and subtotal still NULL, until its
invoice arrives. A billed window whose invoice line was later deleted keeps
invoice_id, invoice_line_id and subtotal NULL.
Credits
fact_credit_transaction
One row per credit grant, spend, or pending grant. Filter type = 'SPEND' for
consumption; group by signal_name or product_id for usage breakdowns. Join
credits_currency_id to dim_credits_currency.
Customer unit history
fact_customer_unit_event
One row per change to a customer unit: created, renamed, retyped, made billable
or non-billable, moved, or deleted. Rows are never changed or removed. A unit’s
state at a time t is its last event at or before t; order by version to
replay changes in order. Who made a change and unit metadata values are not
included.
Changelog
October 2026: fact_usage_summary is built from the billing record. It now
reads the same record invoicing uses. Before, windows closed after September 17,
2026 stayed collecting with no invoice, so billed revenue was undercounted.
What changes:
idis the billing window id the usage cells API returns. It is stable, but it is not the id this table returned before.subtotalisNullable(Int64): NULL while a window is collecting, since the amount is decided when the window closes. Before, it showed a running estimate.SUM(subtotal)skips NULLs; usecoalesce(subtotal, 0)where a number is required.statusaddsskipped, for a window closed with nothing to bill.voidednow means the window was billed onto an invoice that was later voided or canceled.- There is one row per billing window. Closed windows with no usage now
appear, usually as
skipped. - New columns, appended after the existing ones:
order_line_attribute_id,invoice_line_id, andnext_billing_date.
Related
- Analytics API overview for endpoints, limits, and errors.
- Query cookbook for ready-to-use SQL recipes.