UsageBox Kata #1: From Token Event to Invoice Line in 30 Minutes

A hands-on kata: take a raw AI usage event - a chunk of Claude tokens, a tool call, a credit burn - and turn it into a stable, auditable invoice line using UsageBox, in about 30 minutes, without building a billing database. Six steps against the real metering API: send your first usage event; make retries safe (idempotent dedupe by event_id, with same-id-different-payload surfaced as a conflict); read a cheap month-to-date total from rollups; pull the immutable audit trail behind a disputed line with /explain; close the period to freeze the invoice while corrections land as net adjustments; and run a raw-vs-rollup /verify so the fast number always equals the true number. Plus production notes, kata variations (per-model cost, live spend caps, vendor-bill reconciliation, ad-hoc SQL), and what you just avoided building.

7 min read

usagebox katausage meteringusage-based billingidempotencyaudit trailrollupsperiod closeAI token billingmetering API2026

This is a hands-on kata, not a think-piece. The goal: take a raw AI usage event - a chunk of Claude tokens, a tool call, a credit burn - and turn it into a stable, auditable invoice line using UsageBox, in about 30 minutes, without standing up a billing database of your own. Every call below hits the real metering API. By the end you will have idempotent ingest, a month-to-date total that is cheap to read, an audit trail behind every charge, and a closed period you can actually invoice against.

If you have read why a plain SQL usage_events table quietly breaks, this is the other half: what it looks like to just use a store that holds the billing invariants for you.

Step 1: send your first usage event

Usage goes in as events. Each one carries a stable event_id (your idempotency key), the account_id you are billing, what was metered (meter_id, optionally model_id and product_id), a quantity, and a timestamp. Batch them - one request can carry many events:

curl -X POST https://api.usagebox.com/v1/usage/batch \
  -H "Authorization: Bearer $USAGEBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "event_id": "evt_2026-06-16_acct42_0001",
      "account_id": "acct_42",
      "meter_id": "claude_tokens",
      "model_id": "claude-opus-4-8",
      "quantity": 18450,
      "unit": "tokens",
      "timestamp": "2026-06-16T10:14:05Z"
    }]
  }'

The response tells you exactly what happened, per event:

{ "accepted": 1, "duplicates": 0, "conflicts": 0, "rejected": 0 }

That event is now durable - written to a write-ahead log and fsynced before you got the ack. A crash one millisecond later does not lose it.

Step 2: make retries safe (the part that actually matters)

Usage collectors retry. Networks drop acks. So the real test is: what happens when the same event arrives twice? Re-send the identical request from Step 1:

{ "accepted": 0, "duplicates": 1, "conflicts": 0, "rejected": 0 }

The second copy is recognized by its event_id and counted as a duplicate, not new usage. Your customer is billed once. This is the single most important property in the stack: idempotency is enforced at write time, not bolted on later.

Now send the same event_id with a different quantity (say 18451). You do not get a silent overwrite - you get a conflict:

{ "accepted": 0, "duplicates": 0, "conflicts": 1, "rejected": 0 }

A conflict almost always means a buggy collector sending the same id for different usage. UsageBox surfaces it instead of swallowing it, so you find the bug in your logs and not in a billing dispute three weeks later.

Step 3: the number that goes on the invoice

Send a few hundred more events through the month, then ask for the account total. This is the figure the invoice shows ("18.4M tokens in June"), so it has to be fast - no SUM() over millions of rows under a lock at billing time:

curl "https://api.usagebox.com/v1/accounts/acct_42/usage?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z&group_by=meter_id" \
  -H "Authorization: Bearer $USAGEBOX_KEY"
{ "source": "rollup", "groups": [{ "meter_id": "claude_tokens", "sum": 18412900, "count": 1043 }] }

Note "source": "rollup". Completed hours are pre-aggregated in the background, so the monthly read is cheap and never contends with live ingestion. The open (current) hour falls back to raw automatically, so the number is still correct, not just fast.

Step 4: the audit trail behind a disputed line

A customer disputes the June charge. "Trust our current total" is not an answer - you need to show what they actually did. The raw events are immutable and queryable:

curl "https://api.usagebox.com/v1/accounts/acct_42/explain?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z" \
  -H "Authorization: Bearer $USAGEBOX_KEY"

/explain returns the breakdown, the segment provenance (which immutable file each number came from), and any corrections applied. That is the evidence behind the invoice line, not a recomputed guess.

Step 5: close the month and freeze the invoice

When June ends, close the period. That captures a frozen snapshot - the quantity, the event count, and a watermark - so the invoice number stops moving:

curl -X POST https://api.usagebox.com/v1/accounts/acct_42/periods/2026-06/close \
  -H "Authorization: Bearer $USAGEBOX_KEY"

From now on, a new Usage event dated in June is rejected at ingest - it cannot silently change a number you already billed. But a genuine correction is still accepted as a first-class event (a negative-quantity Correction that references the original via correction_ref), and it lands as a pending adjustment, never an edit to history. Read the period back:

{
  "status": "closed",
  "frozen": { "quantity": 18412900, "event_count": 1043 },
  "pending_adjustments": [{ "quantity": -5000, "correction_ref": "evt_..._0099" }],
  "adjustments_quantity": -5000,
  "net_total": 18407900
}

The frozen total is what your original invoice showed. net_total is what a corrected invoice would show today. Both are visible and auditable - the original never mutates.

Step 6: prove the fast number equals the true number

The worst bug in billing is a fast total and a true total that disagree. UsageBox ships a built-in check that scans raw events and compares them against the rollups:

curl "https://api.usagebox.com/v1/accounts/acct_42/verify?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z" \
  -H "Authorization: Bearer $USAGEBOX_KEY"

Run it before you generate invoices. If raw and rollup ever drift, you find out here - not from a customer.

Production notes before you ship it

  • Dimensions. Attach up to 16 dimension keys per event (region, feature, agent, plan tier) and group_by any of them later. You do not have to decide your invoice breakdown up front.
  • Rollup vs raw. Account totals default to source=rollup for speed; pass source=raw to force a full scan when you want belt-and-suspenders correctness.
  • Corrections, not edits. Refunds and fixes are Correction / Retraction events that net against the original. History is never rewritten, which is exactly what makes a dispute winnable.
  • No lock-in. Your raw events export back out. The audit trail is yours.

Kata variations to try

  • Per-model cost. Re-run Step 3 with group_by=model_id to split Opus vs Sonnet vs Haiku spend per account.
  • Live spend cap. Query the open hour and alert when an account crosses a threshold mid-month, before the bill lands.
  • Reconcile a vendor bill. Pull your provider's invoice total and compare it against /explain to catch the gap between list price and real cost.
  • Ad-hoc SQL. Hit /v1/query/sql with a SELECT meter_id, SUM(quantity) ... GROUP BY meter_id when you want a one-off slice without a new endpoint.

Kata FAQ

Do I have to run the database myself? No. UsageBox hosts the metering engine; you send events and read totals over HTTP. The append-only store, WAL, rollups, and crash recovery are handled for you.

What happens if my collector sends an event twice? It is deduped by event_id and counted as a duplicate. Same id with a different payload is flagged as a conflict so you can fix the collector.

What about a correction that arrives after I have already invoiced? It is accepted as an adjustment against the closed period and shown in net_total; the frozen snapshot behind your original invoice never changes.

Can I get my data out? Yes. Raw events are exportable - the immutable trail is yours, no lock-in.

What you just avoided building

In six steps you got idempotent ingest, a durable write path, cheap month-to-date totals, an immutable audit trail, period close with frozen snapshots, first-class corrections, and a raw-vs-rollup reconciliation check. Built in-house, that is a write-ahead log, crash recovery, compaction, dedupe-under-retry, and two aggregation paths you have to keep consistent - a real database project, not a weekend usage_events table. That is the same realization driving the 2026 metering acquisition wave: Stripe bought Metronome and Adyen bought Orb because metering is strategic infrastructure that is hard to get right and expensive to get wrong.

Keep reading: idempotency and late events in depth, why metering needs its own database, and the usage-based billing guide.

Key Topics

  • usagebox kata
  • usage metering
  • usage-based billing
  • idempotency
  • audit trail
  • rollups
  • period close
  • AI token billing
  • metering API
  • 2026

Related Articles

Explore more articles on similar topics to deepen your understanding of usage-based billing.

Why Usage Metering Needs Its Own Database (and What a SQL Table Quietly Breaks)

Most usage-pricing writing is about reading the meter your vendor gives you. This is about the layer underneath: the dat...

9 min readRead more

UsageBox Kata #2: Live Spend Caps and Real-Time Usage

Catch and cap AI spend before the bill lands. A hands-on kata against the real metering API: read an account month-to-da...

7 min readRead more

UsageBox Kata #3: Reconcile a Vendor Bill Against Your Meter

Close the gap between what a model vendor like Anthropic or OpenAI bills you and what you metered and charged customers....

8 min readRead more

Explore More Articles

Discover our complete collection of usage-based billing guides and implementation patterns.

View all articles