The short version: Selling prepaid credits on top of a usage meter sounds like a deposit and a counter, but the part that bites is everything between the two: which credit block burns first, what happens to a balance at period end, and how usage past zero turns into a charge. Stripe, Orb, and Lago each ship a credit balance that draws down against metered usage, and each one makes a different default decision about draw-down order, expiry, and overage. This is a tour of those three models and the design choices you have to make explicit before you sell your first credit pack.
Why prepaid credits are harder than a wallet balance
The naive mental model is a gift card. A customer pays you up front, you store a number, and each billable action decrements it. That model survives exactly until the second credit grant lands, the first one is about to expire, and a backdated event arrives after the invoice is cut. At that point you are no longer running a counter, you are running a small ledger with an ordering policy, and the policy decides who gets billed what.
Prepaid credits show up for three distinct business reasons, and Stripe names all three in its announcement of the feature: promotional credits to entice new sign-ups, service credits to compensate for an outage, and a prepaid access model where "AI companies frequently adopt a prepaid credits model, requiring customers to purchase credits to access their products." Stripe also notes that credits "help mitigate fraud risk," because for AI businesses "costs can grow linearly with usage, meaning that even minor fraudulent activities can significantly impact profitability." Those three sources of credit, promotional, compensatory, and paid, often coexist on one account, which is exactly why ordering matters.
The hard questions are always the same four:
- Draw-down order. When a customer holds several credit blocks, which one burns first?
- Expiry. Do credits expire, and what happens to the leftover at period end?
- Overage. When the balance hits zero mid-period, how does the rest of the usage get billed?
- Late events. When a backdated event lands, which block, possibly already expired, should it have drawn from?
Each platform answers these differently. Knowing the defaults is the difference between a credit system your finance team trusts and a stream of "why did my free credits disappear" tickets.
Stripe billing credits: priority, then expiry, applied at finalization
Stripe shipped billing credits (the Credit Grant object) for usage-based billing on February 11, 2025. The constraint to internalize first: a credit grant only applies to metered usage. Stripe is explicit that "you can only apply credit grants to subscription items that use metered prices and report usage through Meters." You cannot point a credit grant at a flat subscription line. If your plan mixes a fixed platform fee with metered usage, the credits touch only the metered part.
The draw-down order is a defined tie-break chain
When several grants could apply to one invoice, Stripe resolves the order with a fixed sequence rather than leaving it to chance:
- Priority. "Credit grants with higher priority (lower number indicates a higher priority) apply first." The field defaults to 50, the highest priority is 0, the lowest is 100. Priority ordering was added to the API in February 2025, so older integrations that never set it inherit the default.
- Expiration. Grants with an earlier
expires_atapply first, so soon-to-expire credit is spent before credit with runway. - Category.
promotionalcredit applies before other categories. - Effective date, then created date, both earliest-first, as final tie-breakers.
The practical takeaway: if you want paid credit preserved and promotional credit spent first, you do not need to hand-sequence anything, the category rule already burns promotional credit ahead of the rest at equal priority. If you want something else, you set priority deliberately on each grant.
Where credits sit in the invoice math
Stripe applies credits "after discounts, but before taxes," and only "at the time of finalization." Two consequences fall out of that. First, a percentage discount is computed on the gross usage charge, then credits reduce what remains, so stacking a coupon and a credit grant does not double-dip. Second, because credits apply at finalization, the running balance a customer sees mid-period is an estimate, and the authoritative deduction happens when the invoice locks. Build your in-app balance display to say "estimated" until the period closes, or you will be explaining the gap.
Expiry is opt-in but has a sharp edge
By default Stripe credits do not expire: "Credits won't expire unless you set this field." You set expires_at to make them lapse. The edge case to handle is reinstatement: "If the credit grant is past the expiration date, the reinstated credits expire immediately." If you void and reinstate a grant as part of a correction flow, doing it after the expiry date silently produces zero usable credit. Reinstate before expiry, or extend the date in the same operation.
Orb: credit blocks with a five-step burn order
Orb models the balance as a set of blocks, each "initialized with an amount and expiry_date," with the total balance being "the sum of the remaining amount in their unexpired credit blocks." Usage that bills in arrears "draws down from the customer's credit balance," and the burn order across blocks is more granular than Stripe's:
- License allocations for subscriptions with a license price are applied to eligible usage first.
- Blocks with item filters apply before general blocks when charging a specific item, so credit earmarked for one product is not spent on another.
- Soonest-expiring blocks burn next, explicitly "to minimize credit expiration" and the wasted balance that creates.
- Lower cost basis first. Blocks with zero or no cost basis precede blocks with a higher cost basis, "preserving revenue recognition." A promotional block worth $0 of recognized revenue is spent before paid credit you booked as deferred revenue.
- Earliest creation time (FIFO) breaks any remaining tie.
That cost-basis rule is the one teams underappreciate. It means your revenue recognition does not get disturbed by free credit floating around, because the free stuff drains first. Orb keeps "an append-only trace of every balance-changing operation, including automatic usage deductions and manual credit operations," so the order is auditable after the fact rather than a black box.
Overage and expiry
Orb's overage answer is the simple one: "when a customer runs out of their credit balance, any excess usage they incur will be charged at the end of the month on the invoice." Credit blocks can be "configured to expire at the end of the cadence, never expire, or expire after a set number of days or months," which lets you express a monthly use-it-or-lose-it allotment and a non-expiring paid top-up on the same account.
Lago wallets: priority-ordered, deducted post-tax
Lago calls the balance a wallet and splits its contents into paid_credits (purchased) and granted_credits (free), with purchased credits added "when payment is confirmed" and free credits added "instantly." Draw-down across wallets is "controlled by wallet priority, from 1 to 50."
The placement in the invoice math differs from Stripe in a way worth noting: Lago credits are "deducted from the post-tax subtotal, after any credit notes have been applied," whereas Stripe applies credits before tax. If you are migrating a model between platforms, that single difference changes the taxable base and therefore the tax line, so it is not a cosmetic detail. Lago also exposes two balances, an invoiced Balance that updates at finalization and an Ongoing Balance that "accounts for current usage, refreshing every 5 minutes," which is the platform's built-in answer to the estimated-versus-authoritative display problem.
The late-event trap nobody specs until it breaks
A backdated event is where credit expiry and metering ingestion collide. Suppose an event happened at 23:00 on the second, a credit block expired at 00:00 on the third, and the event only reaches you ten hours later. Should it draw from the now-expired block, because the usage truly occurred while the block was live, or fall through to the next block, because the block is gone by the time you process it?
Orb resolves this by event time, not processing time: it "will automatically create the deduction ledger entry for the event before the block expiry, properly allowing it to deduct credit balance from an eligible block." That is the correct behavior, and it is also the behavior you have to build deliberately if you roll your own, because the lazy implementation drains the wrong block and bills the customer for usage their prepaid credit should have covered. This is the same event-time-versus-ingestion-time discipline that governs ingestion generally; if you have not nailed that boundary, the credit ledger inherits the bug. The companion piece on idempotent usage metering and late-event handling covers the acceptance window and timestamp rules that make this possible, and the broader usage ingestion pipeline is where those records get created in the first place.
What you actually have to decide before selling credits
Whether you buy a platform or build, these are the decisions that determine whether the credit system is correct. Each one is a place a real product has shipped the wrong default.
| Decision | The safe default | What goes wrong otherwise |
|---|---|---|
| Draw-down order across blocks | Soonest-expiring and free credit first | Customers lose unexpired paid credit while free credit sits idle, generating support tickets |
| Free vs paid sequencing | Promotional/zero-cost-basis credit burns before paid | Recognized revenue is disturbed by promo credit; refunds get messy |
| Expiry policy | State it per block (monthly allotment vs non-expiring top-up) | Silent forfeiture surprises customers; "where did my credits go" churn |
| Position relative to tax | Pick before-tax or post-tax and document it | Tax base shifts; cross-platform migrations produce wrong totals |
| Mid-period balance display | Show it as an estimate until finalization | Displayed balance disagrees with the invoice deduction |
| Overage handling at zero balance | Bill excess usage on the period invoice | Usage past the balance is dropped or hard-blocked without warning |
| Late event against an expired block | Deduct by event time, from the block live at the time | Prepaid usage is billed as overage; ledger does not reconcile |
Buy the ledger, do not reinvent it
The recurring complaint from teams who built credits in-house is that the counter was a weekend and the ledger was a quarter. The block ordering, the expiry edge cases, the tax placement, the estimated-versus-final display, and the late-event reconciliation are each individually small and collectively the whole job. Stripe, Orb, and Lago have all paid that cost already and exposed it as a few fields. If you are deciding where the credit ledger should live, the same logic that applies to building billing infrastructure versus buying it applies here with more force, because a credit bug is not a wrong dashboard number, it is a charge against money a customer already paid you. And when the bill does come out wrong, the recovery path is the one in handling unexpected-bill complaints: an auditable ledger that can show, line by line, which block covered what.
Prepaid credits are a good model. They smooth revenue, reduce fraud exposure, and match how AI buyers want to pay. They are also a ledger with an ordering policy wearing the costume of a wallet balance. Decide the order, decide the expiry, decide the overage, write it down, and most of the disputes never happen.