Topups & Grants
Credit packages defined on a plan.
When applied to a customer, a topup creates a grant — a credit balance on that customer. Grants are consumed before meter-overage events fire on soft-limit entitlements, letting you buffer overage against purchased or included credit rather than immediately billing. Grants are also consumed before meter-limit events on hard-limit entitlements, enabling pure credit burndown strategies without a hard stop.
Topup schema
Topups are defined inside a plan's topups map.
plans:
growth:
topups:
<topup-id>:
description: string # Optional.
credit: string # Required. Credit ID granted.
value: float|string # Required. Amount of credit granted.
price: # Optional. What this topup costs.
amount: float
included: bool # Auto-apply to all customers on this plan? Default: false.
included_scopes: [string] # Limit auto-apply to specific customer types. Default: all.
resets: bool # Does the grant reset? Default: false.
reset_inc: duration # Reset interval. Default: 30days.
reset_mode: string # 'hard' | 'add' | 'rollover'. Default: 'hard'.
rollover_min: float|string # Minimum carry-forward (rollover mode).
rollover_max: float|string # Maximum carry-forward (rollover mode).
rollover_pct: float # Carry-forward percentage (rollover mode).
max_balance: float|string # Cap on grant balance after reset.
expires_after: duration # Grant expires N ms after purchase.
reset_catchup_cap: int # Max catch-up resets if periods are missed.
Topup fields
credit
The credit ID this topup grants. Can be abstract or discrete. When abstract, the exchange table governs how the grant is drawn against entitlement overages.
value
How much credit is granted, expressed in the credit's stof_units. Must be > 0.
price
What this topup costs in runes. If null, the topup is free. Used by Cloud billing when a customer purchases a topup.
included
If true, the topup is automatically applied to all new customers on this plan, and to existing customers when ensureCustomerIncludedTopups() is called. The customer gets the grant without purchasing it.
included_scopes
Limits auto-application of an included topup to specific customer types (e.g. only 'user', not 'org'). Leave null to apply to all types.
resets and reset_inc
If resets is true, the grant resets on the reset_inc schedule. Useful for recurring credit allocations — e.g. 100 credits per month included in a plan. Default interval: 30days.
reset_mode
Controls what happens to the grant balance at reset time.
| Mode | Behavior |
|---|---|
hard | Resets balance to starting_value. Unused credit is discarded. Default. |
add | Adds starting_value to the current balance. Credit accumulates over time. |
rollover | Carries the current balance forward (with optional decay and clamping), then adds the new allocation. See rollover_* fields. |
rollover_min, rollover_max, rollover_pct
Used with reset_mode: rollover. Applied in this order at reset time:
- Multiply carried balance by
rollover_pct(e.g.0.5= carry 50% of unused credit) - Clamp result to
[rollover_min, rollover_max] - Add
starting_value - Clamp total to
max_balance
max_balance
A cap on the total grant balance after reset. Prevents accumulation from exceeding a ceiling.
expires_after
The grant created by this topup expires N milliseconds after it was granted. Expired grants are discarded automatically on the next enforcement operation that checks them.
reset_catchup_cap
If a grant is multiple periods behind — for example, the policy hasn't been loaded for 60 days and the reset is monthly — Limitr normally calls reset_period() for each missed period. This field limits how many times it will do so. Set to 1 to never accumulate missed periods.
Grant schema
Grants are created automatically from topups. You don't define them in the policy. The Grant object on a customer has:
{
id: string, // Grant ID ('lgrnt_...')
credit: string, // Credit ID
topup: string, // Topup name that created this grant (if any)
created_on: number, // Timestamp (ms)
granted_on: number, // Timestamp of last reset (ms)
starting_value: number, // Value at grant creation (used for resets)
value: number, // Current balance
resets: boolean,
reset_inc: number, // ms
expires_on: number, // ms, or null
reset_mode: string,
// ... rollover fields
}
How grants cover overage
When a soft-limit entitlement goes into overage:
- Limitr checks the customer's grants for any that can cover the overage credit (directly or via the exchange table).
- It selects the best grant per the exchange
grant_strategy. - It converts the overage amount from the entitlement's credit into the grant's credit and deducts from the grant balance.
- If the grant fully covers the overage,
meter-overagedoes not fire. - If grants are exhausted or insufficient,
meter-overagefires with the remaining uncovered amount inevent.overage.
A grant with resets: false that reaches value: 0 is automatically removed.
Examples
Monthly included credit allocation
topups:
monthly_credits:
description: 100 AI credits included monthly
credit: ai_credit
value: 100
included: true # auto-applied to all customers on this plan
resets: true
reset_inc: 30days
reset_mode: hard # unused credits don't carry over
Purchasable credit pack with expiry
topups:
ai_boost_pack:
description: 500 AI credits, expires in 90 days
credit: ai_credit
value: 500
price:
amount: 49.00
expires_after: 90days
Rollover credits (carry 50% of unused)
topups:
rollover_pack:
credit: ai_credit
value: 100
included: true
resets: true
reset_inc: 30days
reset_mode: rollover
rollover_pct: 0.5 # carry 50% of unused balance
rollover_max: 150 # never carry more than 150 credits
max_balance: 250 # total balance can't exceed 250
SDK
policy.applyCustomerTopup()
Applies a topup from the customer's plan to the customer, creating a grant. Returns true if successful.
await policy.applyCustomerTopup('user_abc', 'ai_boost_pack');
policy.ensureCustomerIncludedTopups()
Ensures all included topups on the customer's plan are applied, and removes grants for topups that are no longer applicable. Call this after a plan change or on customer load.
This is called automatically when creating new customers and when adding customers from Limitr Cloud. To control which included topups apply to which customers, use the included_scopes field on the topup.
await policy.ensureCustomerIncludedTopups('user_abc');
policy.remainingCredit()
Returns the total remaining balance of a specific credit across all of a customer's grants, after exchange conversion. This is grant balance only — it does not include entitlement meter state.
const balance = await policy.remainingCredit('user_abc', 'ai_credit');
// Total ai_credit remaining across all grants on this customer