Pushrail Docs
Open app
Guides

Routing & filtering

How routing rules connect events to destinations and how filters narrow the match.

Routing & filtering

A routing rule is the join between an event and a destination. You can have many rules per destination, many destinations per rule, and many rules matching the same event. Filters narrow which events satisfy a rule based on payload contents. Together, routing and filtering let you keep destination configuration declarative, your application code never needs to know which destinations exist.

How routing works

When an event is accepted, Pushrail evaluates all routing rules in the event's tenant. Each rule that matches produces one delivery against its destination. A single event can result in zero deliveries (no rules matched), one delivery, or many, fan-out is the default behavior.

Rule evaluation is order-independent. Rules do not stop at the first match, and there is no priority field, every matching rule fires. If you need exclusion semantics, encode them inside each rule's filter rather than relying on rule order.

eventTypeFilter

The most common filter is on eventType. The match is exact or glob:

{
  "eventTypeFilter": "order.completed"
}
{
  "eventTypeFilter": "order.*"
}

The glob matches order.completed, order.refunded, order.shipped, anything under the order. prefix. Use globs sparingly; explicit lists make routing intent obvious in the dashboard.

For a fixed enumeration, pass an array:

{
  "eventTypeFilter": ["order.completed", "subscription.renewed", "subscription.canceled"]
}

fieldFilters

fieldFilters narrow the match based on payload contents. Each filter is a JSON-path selector plus an operator and a value:

{
  "eventTypeFilter": "order.completed",
  "fieldFilters": [
    { "path": "$.payload.totalCents", "op": "gte", "value": 10000 },
    { "path": "$.payload.currency", "op": "eq", "value": "USD" }
  ]
}

The supported operators are eq, neq, gt, gte, lt, lte, in, nin, exists, nexists, contains. Multiple filters on one rule are AND-ed; an OR needs two separate rules pointing at the same destination.

Filters operate on the original canonical event, before any transforms run. This is intentional, filtering on the post-transform shape would make rule semantics depend on every destination's transform, which is confusing.

Multiple destinations per event

A single event commonly fans out to several destinations: a webhook for a customer's app, a warehouse for analytics, an object store for the data lake. That's three rules, all matching the same event, each pointing at a different destination. Pushrail creates three independent deliveries, they retry, succeed, and fail independently, and a failure in one does not affect the others.

This pattern keeps each destination's reliability story isolated. The warehouse load can be down for an hour without affecting the customer's webhook delivery.

Examples

Route all events of a type to one destination:

{
  "eventTypeFilter": "order.completed",
  "destinationId": "dest_webhook_acme"
}

Route high-value orders to a separate analytics destination:

{
  "eventTypeFilter": "order.completed",
  "fieldFilters": [{ "path": "$.payload.totalCents", "op": "gte", "value": 100000 }],
  "destinationId": "dest_posthog_enterprise"
}

Customer-scoped routing, restrict a rule to one customer's events:

{
  "eventTypeFilter": "*",
  "customerExternalId": "cust_acme",
  "destinationId": "dest_webhook_acme"
}

When the customer manages their own destinations via the embedded portal, Pushrail injects the customerExternalId automatically, customers cannot create rules that match other customers' events.

See the routing rules API reference for the full schema and CRUD endpoints.