Routing Rules
Routing rules decide who gets notified when an event arrives. Without a matching rule, an event is stored but no notification is sent.
How matching works
When an event arrives, FlowAlert checks all enabled rules. A rule matches when its event key pattern matches the incoming event key.
Event: booking.created
↓
Rule 1: booking.* ✓ matches → notify "Booking Staff"
Rule 2: payment.* ✗ no match
Rule 3: * ✓ matches → notify "Managers"Multiple rules can match a single event — all matched rules fire.
Event key patterns
| Pattern | Matches |
|---|---|
* | Every event |
booking.created | Exactly booking.created |
booking.* | booking.created, booking.cancelled, etc. |
payment.failed | Exactly payment.failed |
Use specific keys in production where possible. Wildcard rules like * are useful as a catch-all during onboarding, but can cause over-notification in busier workspaces.
Rule fields
| Field | Description |
|---|---|
| Name | Human-readable label for the rule |
| Event key pattern | *, resource.*, or an exact key like booking.created |
| Target group | Which group receives the notification |
| Priority override | Override the event's priority for this rule's notifications |
| Requires acknowledgement | If enabled, the notification must be acknowledged by a staff member |
| Enabled | Disabled rules are skipped entirely |
Filter conditions
Filter conditions let you add payload-based matching on top of the event key pattern. If conditions are set, the rule only fires when both the event key matches and all conditions pass.
Field paths
Conditions use dot-notation paths to reference values on the incoming event:
| Field | Value |
|---|---|
title | The event's title string |
body | The event's body string |
priority | LOW, NORMAL, HIGH, URGENT, or CRITICAL |
data.fieldName | Any key inside the data object you send |
data.nested.key | Nested keys are supported (arbitrary depth) |
For example, if you send:
{
"event": "order.created",
"title": "New order",
"body": "3 items",
"priority": "HIGH",
"data": {
"branch": "pattaya",
"customer": {
"tier": "vip"
}
}
}Then data.branch resolves to "pattaya" and data.customer.tier resolves to "vip".
Operators
| Operator | Description | Example value |
|---|---|---|
equals | Exact string or number match | pattaya |
not_equals | Does not equal | bangkok |
contains | String contains substring | urgent |
starts_with | String starts with | VIP- |
ends_with | String ends with | -critical |
is_empty | Field is null, undefined, or "" | — |
is_not_empty | Field has a value | — |
one_of | Matches any of a comma-separated list | pattaya,phuket,samui |
greater_than | Numeric greater than | 1000 |
less_than | Numeric less than | 500 |
between | Numeric between two values (inclusive) | 100,999 |
is_true | Boolean true | — |
is_false | Boolean false | — |
time_of_day | Current time is within range (workspace timezone) | 09:00-18:00 |
day_of_week | Current day matches | weekday, weekend, or monday,tuesday |
AND / OR logic
Conditions are organised into groups:
- Conditions within a group are AND'd — all must be true
- Multiple groups are OR'd — any one group matching is enough
Group 1: data.branch = "bangkok" AND priority = "HIGH"
OR
Group 2: data.branch = "phuket" AND priority = "HIGH"This fires the rule if the event is HIGH priority from either Bangkok or Phuket.
End-to-end example — branch routing
Goal: Notify the Bangkok Branch Team only when an order.created event comes from the Bangkok branch.
1. Send the event from your app — using whatever event key you choose (order.created here is just an example):
POST /v1/events
{
"event": "order.created",
"title": "New order #1042",
"body": "2 items · ฿890",
"priority": "NORMAL",
"data": {
"branch": "pattaya",
"order_id": "ORD-1042"
}
}2. Configure the routing rule:
- Event key:
order.created - Target group:
Bangkok Branch Team - Add a filter condition:
- Field:
data.branch - Operator:
equals - Value:
pattaya
- Field:
Only orders from the Bangkok branch will reach the Bangkok team. You can duplicate the rule for each branch, pointing each one at the appropriate group.
Field values are always compared as strings unless you use a numeric operator (greater_than, less_than, between). For example, data.total equals 1000 compares the string "1000", while data.total greater_than 1000 compares numerically.
Requires acknowledgement
When enabled, FlowAlert tracks whether a staff member has opened and acknowledged the notification. If no acknowledgement is received within the escalation window, the escalation policy fires (if configured).
See Acknowledgements for details.