Credit expiry and breakage
Every credit grant can have an expires_at. Unused credits at that time are removed with a grant_expired ledger entry and a credit_grant.expired event. This guide covers where expiry comes from, how customers are warned and how the lapsed credits are reported.
Before you start
- Credits delivered as credit grants, in pools.
Credits bought in a top-up expire after one year unless you set a different length. Setting an expiry on paid credits that is lawful where your customers are is your responsibility, whether you keep the one-year default or change it, and Runledger doesn’t check one against any law. Nothing here is legal advice, and legal review of credit expiry is part of your pre-launch checklist.
Where an expiry comes from
Purchased credits expire after one year by default: leave credits_expire_in out and a top-up’s credits last for one year (P1Y) from when payment completes. Set credits_expire_in on the plan’s top_up, or on one checkout session, to use a different length. The default is the same through the API, the customer view and the dashboard. Grants you create yourself with POST /v1/credit_grants have only the expires_at you send.
A checkout session can override the plan’s setting for one purchase:
The duration runs from when payment completes, and goes from P1D to P10Y. The grant’s expires_at is set then, one year on when no duration is given.
Spent first
The burn order uses soonest expiry as its second rule, so credits closest to lapsing are spent first inside a priority. Customers lose the least when the oldest credits go before the newest. Promotional credits before paid is the third rule, so free credits are used before ones the customer paid for.
Warn customers
credit_grant.expiring is sent once before a grant with credits left expires. The lead time is the account setting grant_expiry_notice, an ISO 8601 duration, P7D by default, counted on the customer’s clock, so a test clock sends it when it is advanced past that point. A grant that expires sooner than the notice is told at once. P0D turns the notice off, and a change to the setting moves the notice of every grant that hasn’t expired. An allowance’s refill doesn’t send it: its credits lapse when its window resets. Set it in the configuration document:
The event names the grant, and its context carries the customer. Fetch the grant for the amount remaining and tell the customer in your own product, or let them see the credits that expire next in the customer view. The usage summary lists the next expiry for each pool.
Report breakage
GET /v1/reports/breakage shows credits that lapsed in a range, grouped by day, week or month, in three classes:
For paid credits sold, used, lapsed and still outstanding over time, use the credits report.
Credits that were paid for count as deferred revenue until they are used or lapse. The revenue exports release lapsed paid credits at their cost basis in the period they expire, so the report and the export agree. Lapsed included and promotional credits never had revenue and release nothing.
Rules
- A voided grant isn’t expiry. Voiding keeps its reason and doesn’t appear in the breakage report.
- Expiry is checked against the ledger at the second. A hold that depends on credits that lapse before capture draws from what is left, as in Checks and holds.
- A test clock moves expiry, so you can test the warning and the lapse without waiting.
API reference
Breakage report, and create a credit grant.
Next steps
- Auto top-up and alerts buys credits before a balance runs out.
- Usage meters show customers the next expiry.
- Sandbox and test clocks shows how to test an expiry without waiting.