Skip to main content

Notifications

Policy-defined event handlers.

Notifications run in-process whenever a metering event occurs, letting you filter and react to specific conditions — a customer crossing 80% of their limit, a specific entitlement going into overage, a hard limit firing on a high-value customer.

In Limitr Cloud, notification conditions defined in your policy also trigger managed routing to Slack, email, or webhook automatically — without changing your application code.


How it works

The notifications block in your policy contains named Notification objects. Each defines a matches() function that filters events by type and payload, and a fire function that handles matching events.

Limitr calls matches(type, event) for every metering event. If it returns true, fire is called with the event.

Event types: 'meter-changed' · 'meter-limit' · 'meter-overage'


Defining notifications in policy (Stof)

Notifications are defined in Stof, the policy runtime language. The matches and fire functions are Stof functions embedded in the policy document.

notifications: {
high-usage-warning: {
fn matches(type: str, event: obj) -> bool {
type == 'meter-changed' &&
event.entitlement == 'chat_input' &&
event.meter.value >= (event.meter.limit * 0.8)
}

fn fire(name: str, event: obj) {
// Fires in-process. Route to your app via addHandler().
// In Cloud, also triggers Cloud-managed alerting.
?App.event_handler(name, stringify('json', event));
}
}

hard-limit-hit: {
fn matches(type: str, event: obj) -> bool {
type == 'meter-limit'
}

fn fire(event: obj) {
// Single-arg fire — name not passed
?App.event_handler('meter-limit', stringify('json', event));
}
}
}

The fire function can accept (name, event), (event), or no arguments — Limitr calls whichever signature is defined. If fire is not defined as a function, Limitr sends the event as a named <Event>.send() call using the notification's key.


In-process handling with addHandler()

addHandler() receives all events — both from the engine's built-in event system and from fire calls in your policy notifications. You don't need to write notifications in Stof to handle events in TypeScript; addHandler() works directly.

policy.addHandler('alerts', (key: string, value: unknown) => {
const event = JSON.parse(value as string);

if (key === 'meter-limit') {
// Hard limit blocked a request
slack.send('#alerts', `${event.customer.id} hit hard limit on ${event.entitlement}`);
}

if (key === 'meter-overage') {
// Soft limit — overage after grants exhausted
billing.queueCharge(event.customer.id, event.entitlement, event.overage);
}

if (key === 'high-usage-warning') {
// Custom notification name fired from policy
crm.flag(event.customer.id, 'approaching-limit');
}
});

The key for built-in events is always 'meter-changed', 'meter-limit', or 'meter-overage'. The key for custom notifications is the notification's ID in the policy.


Setting notifications at runtime

Notifications can be loaded into the policy at runtime without a full policy reload — useful for dynamically updating alert conditions without a redeploy.

await policy.setNotifications(notifStofString, 'stof');

Event payload reference

All events share the same base payload structure. The value argument in addHandler is a JSON string.

{
customer: {
id: string,
plan: string,
type: string,
},
entitlement: string, // entitlement name
plan: string, // plan ID
credit: {
description: string,
// ... other credit fields
},
meter: {
value: number, // new meter value after operation
limit: number, // effective limit
invalid: number, // attempted value (meter-limit only)
},
overage: number, // meter-overage only
grant_value_applied: number, // meter-overage only
}

See Enforcement → Event payload shape for the full reference.


Cloud alerting

In Limitr Cloud, notification conditions defined in your policy are evaluated on the Cloud side as well. When a condition matches, Cloud routes the alert to your configured channels — Slack, email, or webhook — in real time with no polling.

The same enforcement event that blocks or meters the request also triggers the notification. You define the conditions once, in your policy. They fire both in-process (via addHandler()) and through Cloud without any additional code.


addHandler vs policy notifications

addHandler()Policy notifications
RoutingYour application codeCloud-managed (Slack, email, webhook)
Logic languageTypeScriptStof
Deployable without code changeNoYes
Part of versioned policyNoYes
Works without CloudYesYes (in-process only)

Use addHandler() when you need to react to events in application code — queue a charge, update a database, send an internal notification.

Use policy notifications when you want Cloud-managed routing, conditions that change without a deploy, or fine-grained filtering tied to the policy's versioning workflow.