Skip to content

Documentation

Triggers and rules

How rule trees evaluate against incoming events, how conditions are scoped and composed, and why every evaluation explains itself.

A trigger watches the events from one or more providers and decides whether they matter. When it decides they do, it fires, and the workflows attached to it run.

What a trigger owns is a rule tree: nested groups combining conditions with AND or OR. What makes it worth using is that the tree explains itself — every condition returns a plain-language reason whether it matched or not.

Before anything else: enable it

A trigger is created disabled, as is a provider and a workflow. If nothing is happening, that is the first thing to check. It is the single most common reason a rule that looks correct never fires.

The same switch is how a trigger is paused: disable it and it stops firing, enable it and it resumes, with its rules and links untouched. On a phone the trigger's page carries Pause trigger and Resume trigger in the bar above the tabs; its rules are shown there but edited on a larger screen.

Groups and conditions

A condition is a leaf. It reads a field out of the event by dot-path, applies an operator, and compares against one or more values:

employee.status        EQUALS        "terminated"
employee.department    IN            ["engineering", "design"]
alert.count            GT            5

A group is a branch. It has an operator — AND or OR — and contains conditions, other groups, or both. A trigger has exactly one root group, and groups nest up to six levels deep.

AND
├── employee.status EQUALS "terminated"
└── OR
    ├── employee.department IN ["engineering", "design"]
    └── employee.level GTE 5

AND requires every child to match. OR requires one. Both short-circuit, so a group stops evaluating as soon as the answer cannot change.

Two empty cases are worth knowing, because both fail closed rather than matching everything:

  • A group with no children evaluates to false
  • A trigger with no root group evaluates to false, with the reason trigger has no root rule group configured

Every evaluation explains itself

This is the part that makes a trigger debuggable. Each condition returns a reason string alongside its verdict, and they are written to be read rather than parsed:

value "terminated" equals "terminated"
value "full_time" not found in [contractor intern]
no array element matched (checked 3 elements)

You get these in three places: in the response to a webhook post, in the trigger's evaluation history, and in run history for the workflow that fired. When a trigger does not fire, you are not reduced to guessing which clause let you down — the tree tells you which one, and what it compared.

Rule operators lists all eighteen operators and the exact reason each one returns.

Field paths

A field path walks the event payload by dots. Given this event:

{
  "employee": {
    "id": "E-2231",
    "roles": [{ "name": "admin" }, { "name": "billing" }]
  }
}

employee.id resolves to "E-2231", and employee.roles.name resolves to both "admin" and "billing".

That second case is array traversal. When a path crosses an array, the values from every element are collected and the condition matches if any of them match. The reason says so, and counts what was checked:

array element matched: value "admin" equals "admin"
no array element matched (checked 2 elements)

You do not have to know the shape of an event in advance. The rule builder learns it from the messages a provider has actually sent — the newest hundred, so a field a sender started including last week is there — and field paths and their observed values autocomplete as you type rather than coming from a schema somebody had to write down.

Scoping a condition to one provider

A trigger can listen to several providers at once, which raises an awkward question: what should a condition about a Workday field do when the event came from CloudWatch?

Scoping a condition to a provider answers it. When an event arrives from a different provider, the condition is skipped and counts as matched:

provider scope 4 does not match current provider 7; skipped

Skipping to matched rather than false is deliberate. Inside an AND group, a condition that failed simply because it was about somebody else's event would veto the whole trigger, and a multi-provider trigger would never fire. This way a scoped condition constrains the provider it names and stays out of the way otherwise.

Composing triggers

The TRIGGER_MATCH operator lets one trigger reference another: the condition matches when the referenced trigger fires for the same event.

This is how you avoid restating logic. If "is a production incident" is already a trigger you trust, every other trigger can ask for it by reference rather than copying its clauses and letting the copies drift.

Referenced triggers are evaluated once per event no matter how many conditions ask for them. A condition satisfied from that cache says so:

result from memoization cache

A trigger can't depend on itself. If references loop back, whether a trigger names itself or A references B and B references A, the reference that closes the loop evaluates as not matched and says why:

ref trigger 42 is already being evaluated: TRIGGER_MATCH cycle, treated as not matched

Chains are also capped at 32 references deep. A reference past the cap is not evaluated and doesn't match.

Triggering on a correlation

A trigger can listen to the built-in Correlations provider like any other. Its events are correlations, so its conditions test fields such as confidence, rule.name and source.data.employee_id. See Acting on a correlation.

What a trigger does when it fires

It runs each workflow attached to it once, and the event's payload travels with it. That payload is what a step's input mappings and templates read from, via paths like trigger.event.data.employee.email, so the data that decided the trigger should fire is the same data the workflow acts on.

See Workflows and steps.

Deleting a trigger

Deleting a trigger deletes what is the trigger's: its rule tree, and its history of firings. What it started is kept. Workflow runs it fired stay in the run history with no trigger against them, the way a deleted workflow's runs stay. A trigger node on a workflow canvas that pointed at it stays too, pointing at nothing until you link it to another trigger; and a TRIGGER_MATCH condition in another trigger that deferred to it now defers to nothing, and never matches, which the evaluation's reason says.

Deleting a rule group deletes its conditions and the groups nested under it. Disable a trigger instead if you want to keep its history and rules.