Documentation / Conception / Guides pratiques / Route an event to the right workflow

An event carries no workflow name. When one arrives — a prescription recorded, a lab result received — the platform has to decide which workflow runs. With a single workflow per event type there is nothing to decide. The moment a second one listens to the same event, you need to say what tells them apart.

The rule, in one paragraph

Among the workflows whose trigger declares that event type, the platform keeps the routable ones (status: active, or no status — see Organisation metadata), evaluates their selectors in file order, and runs the first one whose selector is true. A workflow with no selector is the catch-all of its event: it runs when no selector matched. One event still starts exactly one run.

Writing a selector

A selector is a jexl expression on the event payload, exactly the expression language of the Garde node — see Branch with guards and switch. It sits on the trigger:

trigger:
  type: trigger.medication-prescribed
  selector: patient.clairance < 30

Two workflows can then split one event honestly:

# renal-dosing.workflow.yaml
trigger:
  type: trigger.medication-prescribed
  selector: patient.clairance < 30

# interaction-analysis.workflow.yaml — no selector: the catch-all
trigger:
  type: trigger.medication-prescribed

A prescription for a patient with a clearance under 30 goes to the renal workflow; every other prescription falls through to the interaction analysis.

A selector that fails to evaluate excludes its candidate — it never wins, and it never fails the ingestion. A missing field, a typo in a path: the candidate is skipped, the incident is logged, and the event continues to the next candidate. A clinical event is not lost because an expression was wrong. The editor and POST /api/workflows/save still refuse a selector that does not compile, so the mistake is normally caught before it ships.

Two catch-alls on one event is refused

If two routable workflows declare the same event type and neither has a selector, nothing distinguishes them — file order would decide silently, and one of the two would never run. That is refused:

  • when you save from the console, with a 400 naming both files. Nothing is written;
  • at startup, if such a pair reaches the instance another way — a git pull, a file edited by hand. The proxy refuses to boot rather than run half of what the files describe.

Two ways out, both stated in the error: give one of them a selector, or unpublish one.

Note what is not a conflict: two distinct selectors (that is the whole point), or a selector plus one catch-all (the selector is tried first, the catch-all takes the rest).

Events that never compete

Some events address one precise workflow, so several workflows on the same event type are normal and nothing is ever shadowed:

  • webhooks — the URL carries the workflow's own UUID;
  • sub-workflow calls — the parent's workflow.execute node names its child;
  • schedules — each tick is emitted by the scheduler for the file that owns that schedule.

These need no selector, and the console shows them without a ranking.

Reading the routing table

/routing in the console lists every event type with its candidates in decision order, each one's selector (or « attrape-tout »), and its fate: routed, shadowed, or out of routing (unpublished, or never published). It is the screen that answers "why did that workflow run?" — and it names conflicts along with the gesture to settle them.

Pausing a workflow

Not by emptying anything: unpublish it. It is the platform's single pause, whatever the trigger — an event-triggered workflow leaves routing, and a scheduled one is disarmed on the scheduler. Its published versions are kept, so a rollback republishes it as it was. See Manage workflow storage.

See also

75 documents7 sectionssource : /docs · généré au build