The fleet billing screen is one component rendered twice. /admin/robotics/billing and /admin/games/billing share the same shell and differ only by a vertical discriminator (robotics | games). Everything on the page — the KPI strip, the metered rows, the cost-by-tag split, the period history — is driven by that one value. That matters for reading the page: the rate plan is per vertical. The metric list on the robotics screen and the metric list on the games screen come from two separate server-side rate plans, and neither is a subset of the other. A vertical whose rate plan is empty renders as “No billable metrics yet — rate plan is empty for <vertical>” rather than as a zero-dollar bill.

How the rate plan is served per vertical

Three procedures back the screen. getUsage returns the rate plan and the metered usage together. There is no separate “list the rate plan” call — the plan is the row set, with units and cost_usd filled in for the current period. A metric that exists in the plan but has not been used this period still comes back as a row, with zero units. The Metrics billed KPI is exactly this distinction: it counts rows where units > 0 against the total number of rows in the plan. The current-period query refetches on a 120-second interval, and the header shows the last successful aggregation time. Aggregation is computed inline on each query rather than precomputed, so the Refresh button re-runs it. History is cached with a 5-minute stale time and is requested for 6 months.

Reading a metered usage row

Each row in This period · metered usage is one metric from the vertical’s rate plan. The fields are: cost_usd is computed server-side and rendered as returned; the console does not multiply rate by units itself. The quantity and the dollar figures are rounded for display only — quantities below 1 are shown to four decimal places, quantities between 1 and 100 to two, and larger quantities are progressively rounded. Do not reconcile a spreadsheet against the rendered strings; reconcile against the units and cost_usd values from getUsage. The panel header shows the response’s currency field, defaulting to USD. All cost fields in the payload are _usd-suffixed.

What each unit label counts and over what window

The unit_label is the meter’s unit, and it is authoritative in two places at once: it is the denominator of the price ($0.02/unit_label) and the unit of the quantity (1,204 unit_label). Because both come from the same field, a row is always internally consistent — you never have to guess whether a rate is per hour while the quantity is in minutes. What the unit counts for a given metric is stated in that metric’s rate_plan_description, which the screen renders verbatim under the row title. TeleQuick treats that description as the definition of record for the meter; this page deliberately does not restate per-metric semantics, because the plan is served from the control plane and can add metrics without a docs change. The window is the billing period, not a rolling window. getUsage returns periodStart and periodEnd, which the screen prints in the page subtitle, and every units value in the response is the total accumulated inside that period for the queried vertical. The KPI strip additionally surfaces projected_end_of_period_usd as Projected end-of-period — a server-side projection for the same window, not a forecast beyond it. Because the period is closed at periodEnd, the current-period row set is a moving target and the history table is not. Once a period closes, its totals land as a row in fleet_usage_period and are served by fleetBilling.history.

Which metrics are resource-attributed and which are unattributable

A chargeback split only works for metrics that carry a per-resource dimension — that is, metrics whose usage the meter can attribute to a specific robot, site, server, or fleet. Metrics without that dimension cannot be divided by any tag, no matter how thoroughly you tag. fleetBilling.costByTag makes the division explicit and returns three buckets: The console renders all three, and the distinction is the point. Unallocated is a tagging gap you can close. Unattributable is a property of the metric — the reason field tells you which, and the screen prints it next to the metric name for any unattributable metric with non-zero cost. Two further details in the group list:
  • Groups arrive ordered by cost, largest first; the bars are scaled against the first group.
  • A group whose value is the empty string is rendered as (no value). This is not the same as unallocated: it means the tag key is present on those resources with an empty value, whereas unallocated means the key is absent entirely.

Reconciling the split against the period total

The panel prints a reconciliation line rather than asking you to trust the bars:
When those agree to within a cent, the screen states that allocated plus unallocated matches the period total. When they do not, it states that some usage is not resource-attributed — which is the signal to read the unattributable entries above the line, since that is where the difference lives. Check this line before you send a chargeback. A split that does not reconcile is not wrong, but it is incomplete: the residual is real spend that the selected tag key structurally cannot carry.

Tagging the objects that make a chargeback split work

The tag key selector is populated by tags.keys({ orgId }), which reads the resource_tag table for the organization, counts occurrences per key, and returns { key, count } sorted by count descending with the key name as tiebreak. The key list is built from up to 20,000 tag rows. The selector defaults to the first entry, so the most-used key in your organization is the default grouping on both billing screens. If the organization has no tags at all, the panel does not render a split. Instead it tells you what to tag, and the advice differs by vertical:
  • Robotics — tag a site or a fleet.
  • Games — tag a region or a server fleet.
In both cases the reason is inheritance: resources inherit the value from the container they belong to. Tagging the container is what makes the split possible without tagging every individual robot or every individual server. That is also the fastest way to shrink Unallocated: an untagged container puts every resource beneath it into the unallocated bucket at once. Pick the key you intend to charge back on — cost centre, team, customer, environment — and apply it at container level across the whole organization. Partial coverage of a key produces a split that looks plausible and under-reports every group in it.

Reading the period history

The Recent months panel lists closed periods from fleet_usage_period, most recent first, with three columns: the period (month and year), the comma-joined list of metric identifiers billed in that period, and the period total. The metric list per row is what makes this panel useful beyond the totals: if a metric appears in one month and not the next, the rate plan for that vertical changed, or nothing metered against it. A freshly onboarded organization sees “No history yet. This month’s row lands once usage is aggregated.”

Current limits of the rate plan

Three constraints are stated on the screen itself and are worth carrying into any integration you build on these procedures:
  • Rates are platform defaults. Per-tenant rate overrides are not implemented; every organization on a given vertical sees the same rate_per_unit.
  • Aggregation is inline. Usage is computed on each query, not served from a precomputed rollup. Re-querying is how you refresh.
  • The period is fixed by the server. The screen holds a period selector in state but does not currently pass it to getUsage, so the current-period view is always the server’s active period.
  • Telemetry — the metric and CDR streams that feed usage
  • Authentication — org-scoped API keys for the control plane