Skip to main content

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:

  1. Policy notifications: every notification in policy.notifications whose matches(type, event) returns true. Meter, cap, and feature events only. See Notifications.
  2. Stof functions with the event's name as an attribute, anywhere in the document: #[meter-overage] fn bill(event: obj) { ... }.
  3. 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, and clearHandlers() 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.error and 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.

EventWhen
meter-changedThe meter moved (or a cost was recorded with no usage), with nothing left to bill.
meter-overageA soft limit (or force) went past the limit, and grants didn't cover all of it. Bill overage_price.
meter-limitA hard limit denied the call.
meter-governedThe governor (rate limit) denied the call.
meter-resetThe 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_diff is the call's margin. See Cost and margin.
  • price_diff sums to a period's billable worth. A decrement has a negative price_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​

EventPayload
cap-limitcustomer (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-crossedcustomer (the holder), caller, entitlement, cap. Once, when the cap's count reaches its value.
customer-cap-addedcustomer, cap
customer-cap-resetcustomer, cap
customer-cap-removedcustomer, 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​

EventPayloadSent by
customer-setthe customerCreating or replacing a customer; plan changes, overrides, gives and takes, alt IDs, caps, included topups.
customer-removedthe customerremoveCustomer
customer-plan-changedprevious_plan, new_plan, customersetCustomerPlan
customer-topup-grantedcustomer, topup_name, plan_name, topupapplyCustomerTopup
customer-entitlement-givencustomer, entitlement, plan (whose definition), expires_ongiveEntitlement
customer-entitlement-takencustomer, entitlement, expires_ontakeEntitlement
customer-entitlement-restoredcustomer, entitlementrestoreEntitlement
plan-setthe plansetPlan
plan-removedthe plandeletePlan

Feature events​

EventPayloadSent by
feature-checkedcustomer (whose entitlement it is for the call), caller, entitlement, enabled, plan (the caller's), event_datacheck, 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​

GoalEvents
Bill usagemeter-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 statecustomer-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 upsellA notification on meter-changed with a remaining threshold, or an observe_only cap and cap-threshold-crossed.
Rate-limit telemetrymeter-governed, meter-limit, and cap-limit.
Margin per callmeter.price_diff and meter.overhead_diff on every meter event.
Measure a rollout or experimentfeature-checked, joined with the meter events that follow.
Audit custom dealscustomer-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).