Events
The engine reports every change as an event: usage, denials, overage to bill, customer changes, feature checks. Your app receives them as they happen, during the call that caused them.
limitr.addHandler('billing', (name, value) => {
const event = typeof value === 'string' ? JSON.parse(value) : value;
if (name === 'meter-overage') {
billing.charge(event.customer.id, event.entitlement, event.overage_price);
}
});
Where events go
Each event is delivered, in order, to:
- Policy notifications: every notification in
policy.notificationswhosematches(type, event)returns true. Meter, cap, and feature events only. See Notifications. - Stof functions with the event's name as an attribute, anywhere in the document:
#[meter-overage] fn bill(event: obj) { ... }. - Your app, through the SDK's handlers (
addHandler). Objects arrive as JSON strings.
Handler details (TypeScript):
addHandler(name, handler)adds a handler under a name,removeHandler(name)removes it, andclearHandlers()removes all of them. Every handler gets every event as(name, value).- Handlers run during the engine call that sent the event. Async handlers aren't awaited: the call returns without waiting for them, so keep them quick.
- A handler that throws (or rejects) is logged with
console.errorand skipped. The error never reaches the call that sent the event, which has already recorded its usage, and the other handlers still run. - Numbers in payloads are rounded to 9 decimal places.
Meter events
Sent by allow, increment, decrement, and settle. A call sends at most one of meter-changed,
meter-overage, meter-limit, or meter-governed, plus meter-reset when it started a new period. A call denied
by a cap sends cap-limit instead. Calls with event: false send nothing, and neither do calls for unknown customers
or entitlements.
| Event | When |
|---|---|
meter-changed | The meter moved (or a cost was recorded with no usage), with nothing left to bill. |
meter-overage | A soft limit (or force) went past the limit, and grants didn't cover all of it. Bill overage_price. |
meter-limit | A hard limit denied the call. |
meter-governed | The governor (rate limit) denied the call. |
meter-reset | The call started a new period. |
meter-changed and meter-overage
{
"meter": {
"old": 120, // usage before the call
"value": 170, // usage after the call
"limit": 1000,
"diff": 50, // this call
"overhead": 0.0021, // cost recorded this period (runes)
"overhead_diff": 0.0004, // this call's cost (runes)
"price_diff": 0.001 // worth at list price, before grants; null with no price
},
"customer": { }, // whose meter moved (the org, if scoped)
"entitlement": "chat",
"plan": "pro",
"credit": { }, // the credit
"remaining": 830, // limit - value
"event_data": { }, // what the caller passed as event data, or null
"overage": 20, // meter-overage: units grants didn't cover
"grant_value_applied": 30, // when grants covered some or all of the overage
"overage_price": 0.0004 // meter-overage: what to bill for `overage` (runes)
}
price_diff - overhead_diffis the call's margin. See Cost and margin.price_diffsums to a period's billable worth. A decrement has a negativeprice_diff.- Prices are only computed when the call sends events.
meter-limit
meter: { value, limit, invalid, diff, overhead, overhead_diff, price_diff } (value is unchanged, and invalid
is what it would have been), plus customer, entitlement, plan, credit, invalid_value, overage, and
event_data.
meter-governed
meter: { value, limit, overhead }, customer, entitlement, plan, credit, event_data, and
governor: { tokens, capacity, requested }.
Cap events
| Event | Payload |
|---|---|
cap-limit | customer (the cap's holder; the caller for a one-call cap), caller, entitlement, plan, credit, cap, event_data, meter: { value, limit, diff, overhead, overhead_diff, price_diff }. A cap denied the call, and nothing changed. |
cap-threshold-crossed | customer (the holder), caller, entitlement, cap. Once, when the cap's count reaches its value. |
customer-cap-added | customer, cap |
customer-cap-reset | customer, cap |
customer-cap-removed | customer, id |
When a call moves a cap held by another customer (an org's shared cap), that customer also gets customer-set.
Customer and plan events
| Event | Payload | Sent by |
|---|---|---|
customer-set | the customer | Creating or replacing a customer; plan changes, overrides, gives and takes, alt IDs, caps, included topups. |
customer-removed | the customer | removeCustomer |
customer-plan-changed | previous_plan, new_plan, customer | setCustomerPlan |
customer-topup-granted | customer, topup_name, plan_name, topup | applyCustomerTopup |
customer-entitlement-given | customer, entitlement, plan (whose definition), expires_on | giveEntitlement |
customer-entitlement-taken | customer, entitlement, expires_on | takeEntitlement |
customer-entitlement-restored | customer, entitlement | restoreEntitlement |
plan-set | the plan | setPlan |
plan-removed | the plan | deletePlan |
Feature events
| Event | Payload | Sent by |
|---|---|---|
feature-checked | customer (whose entitlement it is for the call), caller, entitlement, enabled, plan (the caller's), event_data | check, allow, and features, for entitlements with track: true. |
Cloud events
internal-self-updated, internal-customer-updated, and internal-customer-invoices-updated come from Limitr Cloud
syncing a policy or customer. In Cloud mode, topup-purchase-failed also arrives from Cloud.
Which event to use
| Goal | Events |
|---|---|
| Bill usage | meter-overage (overage_price, after grants). Or read meters at the end of the period. Don't bill meter-changed: it fires for usage inside the plan too. |
| Save customer state | customer-set, and the meter events (their customer carries its meters). Meter events don't also send customer-set for the meter's owner. |
| Warn or upsell | A notification on meter-changed with a remaining threshold, or an observe_only cap and cap-threshold-crossed. |
| Rate-limit telemetry | meter-governed, meter-limit, and cap-limit. |
| Margin per call | meter.price_diff and meter.overhead_diff on every meter event. |
| Measure a rollout or experiment | feature-checked, joined with the meter events that follow. |
| Audit custom deals | customer-entitlement-given, -taken, and -restored. |
Event objects in Stof
Inside the engine, event payloads are temporary objects that are dropped after delivery. A Stof handler that keeps
anything must copy it (Ex. copy(event.meter) moved somewhere, or plain values).