Credits
An integer credit wallet, an append-only ledger, and debit-before-spend that returns 402.
@caisson/credits is the metering layer. Balances are integer units, never floats — there is no
rounding to argue about in a dispute. Every movement is a row in an append-only ledger; the
balance is the ledger's fold, not a mutable column you can overwrite.
The contract
The debit happens before the metered work, not after. If the balance is short, the call returns HTTP 402 and nothing runs — no half-spent tokens, no negative wallet.
import { debit } from "@caisson/credits";
// Debit before the work. No balance → 402 raised, the metered call never starts.
await debit({ accountId, amount: 1, idempotencyKey });Idempotency is enforced by two unique constraints, so a retried webhook or a double-clicked request moves the balance exactly once:
-- Inbound grants: a source event is applied once.
UNIQUE (source_event_id, event_type)
-- Client-initiated debits: one key, one debit.
UNIQUE (account_id, idempotency_key)Related
Kernel
The 402 credit-gate error shape lives in the typed CaissonError hierarchy.
Billing
Credit grants are applied on verified Stripe webhook events.
This page covers the essentials. The full @caisson/credits API reference —
the ledger schema, grant and refund flows, and the balance projection — is
still expanding.