Billing your AI customers should cost less code than charging a card with Stripe. Stripe made you pass a card token; Tokenality makes you pass a customer_id. Everything else — attribution, rating, overage, reconciliation, invoicing — happens on a meter you own.
This guide shows the three ways to integrate, from lowest-touch to greenfield, plus backfill, rating, and projection.
The one concept: customer_id
For each AI call, answer one question: which of my customers is this for? That's the customer_id — your own id for the customer (a tenant id, a workspace id, a Stripe customer id, an ERP account number). Tokenality handles the rest.
Install
npm install @tokenality/billing
# set a test key first — it routes to a sandbox ledger, nothing real is billed
export TOKENALITY_API_KEY=tk_test_...
Option A — tail your existing usage table (recommended)
Most platforms already write an append-only usage row per AI call. If you do, you don't need to touch your AI-call path at all — point Tokenality at that table and it derives billing events on a cursor.
import { Tokenality } from "@tokenality/billing";
const tk = new Tokenality({ apiKey: process.env.TOKENALITY_API_KEY! });
const adapter = tk.adapters.sqlLedger({
// you supply the read; we page through on the cursor
query: (sinceCursor) => db.query(
`SELECT id, tenant_id, model, provider_input_tokens, provider_output_tokens, created_at
FROM token_ledger
WHERE reason = 'chat' AND created_at > $1
ORDER BY created_at ASC
LIMIT 1000`,
[sinceCursor],
),
map: (row) => ({
customerId: row.tenant_id, // YOUR customer of record
requestId: row.id, // your own row id → exactly-once, no double-bill
model: row.model,
inputTokens: row.provider_input_tokens,
outputTokens: row.provider_output_tokens,
occurredAt: row.created_at,
}),
cursorField: "created_at",
});
// run it as a cron / scheduled job — never in your request path
await adapter.runOnce();
Zero changes to how you call models, and it covers every code path that writes to your table. This is the fastest path for any platform that already meters.
Option B — emit one line after your metering hook
No usage table, or you want events the instant they happen? Emit a usage event right after your existing metering step.
// after your AI call returns usage:
tk.usage.record({
customerId: tenantId,
model: "claude-sonnet-5", // any provider — we rate them all
inputTokens: usage.prompt,
outputTokens: usage.completion,
requestId: chatId, // idempotency key — retries never double-bill
});
record() is buffered and never throws into your request — a metering hiccup can't take down your product. Failures surface through an onDrop hook and a health endpoint, not exceptions.
Option C — route through the gateway (greenfield)
If you're greenfield or also want the governance plane (budgets, PII, SSRF), route AI through the Tokenality gateway and tag the customer on the request — no reporting code at all.
openai.chat.completions.create(
{ model: "gpt-4o", messages },
{ headers: { "X-Acc-Customer-Id": tenantId } },
);
Backfill your history
Already have months of usage? Replay it once and it's rated instantly — no waiting for new traffic.
tokenality backfill --from-table token_ledger --cursor created_at
Rate with your margin
A rate card turns provider cost into your customer price — markup, flat, or tiered — across every provider.
await tk.rateCards.upsert({
// default for all customers, or pass a customerId to override
mode: "markup",
spec: { markupPct: 40 }, // price = provider cost × 1.40
});
Close the period and project the invoice
At period end, draft the invoice (v1 never auto-finalizes — you review first), reconcile events against it, then send it wherever the money moves.
const draft = await tk.periods.close(customerId, "2026-07");
const check = await tk.reconcile(draft.periodId); // events-generated vs invoiced
// project: Stripe today, your ERP tomorrow — a swappable choice, per customer
Because Tokenality computes but never collects, where the invoice lands is a projection you choose per customer. Own your meter; swap the processor without re-instrumenting a single call.
Test safely
Use a tk_test_ key for a sandbox ledger, and dry-run any batch before it touches real billing:
const preview = tk.usage.validate(events); // what WOULD be rated — persists nothing
That's it
If you already meter tokens, you're an adapter config and a rate card away from accurate, reconciled, processor-neutral customer billing — live in days.
Model what accurate billing would recover for your book with the Billing ROI calculator, see the customer-billing overview, or read why the meter should stay yours in own your meter.