How-to

Billing SDK quickstart: bill your AI customers in a few lines

How to add usage-based customer billing to an AI product with Tokenality — the one concept you learn (customer_id), the three ways to integrate (tail your ledger, emit one line, or route through the gateway), and how to backfill, rate, reconcile, and project the invoice. If you already meter tokens, you can be live in days.

By Chris Therriault8 min read

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.

Tokenality.AI

See how Tokenality handles this.

30-minute demo. Live deployment. Your questions answered directly — no slides, no pitch.