Metered billing
Metered billing is a server-to-server workflow. Do not expose usage recording or invoice finalization directly to untrusted clients.
Lifecycle
Section titled “Lifecycle”flowchart LR accTitle: Metered billing lifecycle accDescr: A metered offer progresses through enrollment, usage, corrections, period finalization, invoice checkout, and authenticated payment. Register["Register metered offer"] --> Enroll["Enroll postpaid subscription"] Enroll --> Usage["Record positive usage"] Usage --> Correction["Append signed corrections"] Correction --> Finalize["Finalize exact period"] Finalize --> Checkout["Create invoice checkout"] Checkout --> Payment["Authenticate payment notification"]
Enroll and record
Section titled “Enroll and record”import { enrollPostpaidSubscription, recordMeteredUsage } from "@byfungsi/sapa";import { Effect } from "effect";
const program = Effect.gen(function* () { const enrollment = yield* enrollPostpaidSubscription({ idempotencyKey: "api-enrollment-customer-42", externalCustomerId: "customer-42", offerKey: "api.metered", offerRevision: 1, });
return yield* recordMeteredUsage({ idempotencyKey: "request-batch-2026-09-16T12:00Z", subscriptionId: enrollment.subscriptionId, meter: { key: "api.request", revision: 1 }, quantity: 120n, occurredAt: Date.UTC(2026, 8, 16, 12), });});Original usage must be positive and occur in a valid retained period. Its meter, rate, period, and subscription head are authenticated historical facts.
Corrections
Section titled “Corrections”import { correctMeteredUsage } from "@byfungsi/sapa";
const correction = correctMeteredUsage({ idempotencyKey: "request-batch-2026-09-16T12:00Z-correction-1", originalUsageIdempotencyKey: "request-batch-2026-09-16T12:00Z", quantityDelta: -20n, correctedAt: Date.UTC(2026, 8, 17, 9),});A correction always references the original usage key, never another correction. It appends history and does not rewrite the original record.
Finalize and collect
Section titled “Finalize and collect”finalizeMeteredInvoice({ subscriptionId, periodEndsAt }) closes exactly the
current period. Finalization aggregates first, applies the retained rational
rate once, and records billing watermarks. Exact retries return the retained
invoice.
The result can be payment_due, not_due, or credit_due. Only
payment_due can create an invoice checkout. Read the authoritative lifecycle
with getMeteredInvoiceCollection(invoiceId).
Usage accepted after close and post-close corrections become signed adjustments on the next invoice at the original rate. Finalized invoices never mutate.