Usage metering
A USAGE-typed product gets its billable quantity from raw events you post as consumption happens. The pipeline is:
Code
Aggregators sit between raw events and the pricing-model tier walk - they decide which events count, how to combine them, and what windowing applies. One aggregator per (product, metric_key) pair.
Usage events
A UsageEvent is one consumption data point. Required fields:
| Field | Notes |
|---|---|
customer_id | Who consumed |
subscription_id | Sets the billing-period context for aggregation and pricing |
product_id | Which USAGE product the event feeds |
metric_key | Stable string (e.g. api_calls, storage_gb) - paired with product_id to pick the right aggregator |
quantity | Decimal - 1 for a count event, 2.5 for 2.5 GB, 3600 for an hour-in-seconds, etc. |
| Field | Required | Description |
|---|---|---|
customer_id | Yes | UUID. Who consumed the resource. |
subscription_id | Yes | UUID. Subscription context (determines billing period). |
product_id | Yes | UUID. The product consumed (must have an aggregator). |
metric_key | Yes | Identifies the type of consumption (e.g., api_calls, storage_gb). |
quantity | Yes | Decimal numeric quantity (e.g., 1 for a single call, 2.5 for 2.5 GB). |
timestamp | Yes | ISO 8601 timestamp - when consumption occurred. |
idempotency_key | Yes | Unique key for deduplication - critical for retry safety. |
price_key | Conditional | Required when the product is keyed (price_key_label is set - see Keyed prices). Selects which sibling price the event maps to. Forbidden for single-price products. |
external_cost_amount / external_cost_currency | No | Pass-through cost from an upstream provider (for cost-plus billing). |
pricing_vars, dimension_vars | No | Variables consumed by formula prices and aggregator filters. |
hold_id | No | UUID of an active wallet hold this event should consume. Required when a hold is active for the same (subscription_id, metric_key). |
wallet_id | No | UUID. Pin which wallet the downstream invoice should settle against. |
metadata | No | Free-form key-value bag for your own use. |
If price_key doesn't match any price under the product, the product's unmatched_price_key_policy decides whether the event is rejected, routed to default_price_key, or silently dropped.
Ingesting
The single-event and batch endpoints share one URL: POST /v1/transaction/usage-event. The handler inspects the body and dispatches to single or batch processing automatically. To send a batch, wrap events in { "events": [ ... ] }.
Code
A 409 HOLD_REQUIRED response is returned when an active hold exists for the (subscription_id, metric_key) pair and the request omits hold_id. Either supply the matching hold_id or release the hold first.
Best practices
Unmatched-key drops
If price_key doesn't match any sibling price, the product's unmatched_price_key_policy decides - reject returns 400, use_default routes to default_price_key and stamps the event price_key_remapped: true, drop silently absorbs. Both reject and drop emit usage_event.dropped so you can reconcile catalog drift.
Aggregators
An Aggregator defines how raw events become a single billable quantity per period. One per (product_id, metric_key).
| Field | Notes |
|---|---|
product_id, metric_key | The pair this aggregator handles |
aggregation_function | SUM, COUNT, or MAX (the three supported) |
window_size | HOURLY, DAILY, or BILLING_PERIOD - bucket boundary for the aggregation. BILLING_PERIOD is the most common and aligns to the subscription's cycle |
feeds | quantity (default) - the result becomes the billable quantity walked through the tiers. cost - the result is injected as the cost variable available to tier rate-expressions (for cost-plus pricing on resold metered services) |
deduplication_enabled | When true, events sharing the same idempotency_key within the window are counted once. Required for at-least-once ingestion to be billed exactly once |
filters | Optional list of {field, operator, value} triples - narrows which events the aggregator picks up |
Aggregation functions
| Function | Use for |
|---|---|
SUM | Total consumption (GB transferred, API calls, minutes used) |
COUNT | Number of events regardless of quantity value |
MAX | Peak in the window (max concurrent connections, max active devices) |
Windowing
Code
Filters
Filters narrow which events feed the aggregator. Same operator grammar across the platform:
| Operator | Meaning |
|---|---|
eq, neq | Equals / not equals |
gt, gte, lt, lte | Numeric / timestamp comparison |
in, not_in | Set membership (value is comma-separated) |
contains | Case-insensitive substring on string fields |
Filter field resolves against the event's metadata (and a few well-known top-level fields like price_key). Multiple filters compose with AND.
Code
This counts only premium-tier API calls in the two US regions, deduplicated by idempotency_key, bucketed to the subscription's billing period.
Deduplication semantics
When deduplication_enabled: true, the aggregator treats two events with the same idempotency_key as one - even if posted seconds apart through different request paths. Dedup is per window, so the same key can legitimately appear once per billing period. Pair this with stable per-event keys on your side (request ID, log line UUID) and at-least-once retry becomes safe.
Querying usage
Query usage events through the top-level /v1/usage endpoint. Filter by customer, subscription, product, metric, or time range using the standard list query parameters (see Pagination).
Code
Response is the standard list envelope:
Code
Use cases for usage queries:
- Build real-time usage dashboards for customers
- Set up alerts when consumption approaches plan limits
- Preview upcoming invoice amounts before billing cycle ends
End-to-end example
Build an API-calls metered product priced with a graduated tier walk.
1. Define the USAGE product:
Code
2. Wire up the aggregator:
Code
3. Publish the staircase price:
Code
4. Ingest usage events
Code
5. At period close, the invoice line:
The aggregator sums the period's events into a billable quantity; the STAIRCASE walk applies; the line lands on the invoice as (used quantity) × tier rates → total. Audit detail (per-tier breakdown) is on the line's atoms field when fetched with ?depth=full - see Invoices.
- Pricing Models - understand tiered and formula pricing
- Subscriptions - how subscriptions drive billing
- API Reference - Aggregators - aggregator configuration
- Committed use plans - volume commitments at discounted rates