Skip to main content

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.

ModeBehavior
hardResets balance to starting_value. Unused credit is discarded. Default.
addAdds starting_value to the current balance. Credit accumulates over time.
rolloverCarries 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:

  1. Multiply carried balance by rollover_pct (e.g. 0.5 = carry 50% of unused credit)
  2. Clamp result to [rollover_min, rollover_max]
  3. Add starting_value
  4. 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:

  1. Limitr checks the customer's grants for any that can cover the overage credit (directly or via the exchange table).
  2. It selects the best grant per the exchange grant_strategy.
  3. It converts the overage amount from the entitlement's credit into the grant's credit and deducts from the grant balance.
  4. If the grant fully covers the overage, meter-overage does not fire.
  5. If grants are exhausted or insufficient, meter-overage fires with the remaining uncovered amount in event.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.

Called automatically

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