Prepaid credits
Usage credits represent service units, not money. Settlement of a
usage_credits benefit issues a provenance-preserving credit lot.
import { Credits } from "@byfungsi/sapa";
const creditBenefit = yield * Credits.pack({ unit: "ai.credit", quantity: 10_000n, });Reserve before work
Section titled “Reserve before work”import { consumeCredits, releaseCredits, reserveCredits } from "@byfungsi/sapa";import { Effect } from "effect";
const runJob = Effect.gen(function* () { const reservation = yield* reserveCredits({ idempotencyKey: "job-018f-credit-reservation", customerId: "customer-42", unit: "ai.credit", quantity: 60n, });
const workSucceeded = true; // Replace with your external work result.
return yield* workSucceeded ? consumeCredits({ reservationId: reservation.id }) : releaseCredits({ reservationId: reservation.id });});Reserve before starting costly work. Consume only after success. Release after failure or cancellation so the units can be reserved again.
Lifecycle rules
Section titled “Lifecycle rules”- Reservation uses a stable idempotency key.
- Consumption and release are mutually exclusive terminal outcomes.
- An expired reservation cannot be consumed, but can be released.
- Concurrent reservations cannot overspend the same balance.
- Refund handling preserves lot provenance and does not invent negative units.
Use getCreditBalance({ customerId, unit }) for verified aggregates and
getCreditHistory({ customerId, unit }) for sanitized authoritative movements.