Skip to content

Triggers

on:
mail.received:
inbox: support@acme.com
connection: acme-google
as: message
when: "{{ message.recipient != 'noreply@acme.com' }}" # optional guard
queue_by: "{{ message.thread_id }}" # optional

on names exactly one trigger, connector.event, from the catalog. Implementations MUST validate its configuration keys against the trigger’s declared input schema.

as names the binding for the trigger payload. It is optional, because every trigger declares a default binding name, binds, in its manifest. as overrides that default.

as is REQUIRED in two cases: when a file declares more than one trigger, and when the manifest’s default collides with a step id in the root scope. If as is absent in either case, implementations MUST reject the file. Implementations MUST NOT resolve the collision without an explicit as.

connection names a credential binding that the deployment environment resolves. It MAY be omitted when the environment defines one connection for that connector.

{{ trigger.name }} is the as name of the entry that fired, or the manifest default when the entry declares no as.

when guards the trigger. It MAY reference only the trigger binding and inputs. When it evaluates to false, no run starts. Implementations SHOULD record a suppressed event.

An evaluation fault in a trigger’s when has no run to fail. The event MUST NOT start a run, and the fault MUST be recorded as an error, distinct from ordinary suppression.

queue_by declares an ordering key. Implementations evaluate it once per event, over the trigger binding and inputs, in the same scope as when.

Runs whose keys are equal form one queue and execute one at a time, in the order that the events arrived at the implementation. Runs whose keys differ are unconstrained. Without queue_by, the implementation starts a run for each event with no ordering.

A queue is identified by the pair of the workflow’s identity and the key value, so runs of different workflows never share a queue. Two entries of a multi-trigger declaration that produce equal keys share one queue.

Keys compare by the equality rules. A key that evaluates to null is an evaluation fault, handled as a fault in a trigger’s when.

A run holds its key until the run reaches a terminal outcome, including while it is suspended.

batch groups events before a run starts.

on:
mail.received: { inbox: support@acme.com }
batch: { size: 100, max_wait: 5s, partition_by: "{{ message.account_id }}" }

size bounds the number of events in one batch. max_wait bounds how long the implementation waits to fill a batch. partition_by guarantees that a batch holds events from one partition.

Implementations MUST evaluate when and partition_by once per event, over that single event. The trigger binding holds one event during those evaluations. The run’s trigger binding holds the list of events in the batch.

A trigger entry MUST NOT declare both batch and queue_by. Ordering across batches is not defined in this version.

on MAY be a list of triggers. Each entry then requires its own as.

on:
- rss.item_published:
feed: https://blog.acme.com/feed.xml
as: item
- sheet.row_added:
sheet: Content Calendar
as: row

Exactly one entry fires for a given run, and its binding holds the payload. The binding of every entry that did not fire resolves to null, as a reference to a skipped step does.

Each entry MAY declare into: a projection of its payload into shared bindings, evaluated before the first step.

on:
- rss.item_published:
feed: https://blog.acme.com/feed.xml
as: item
into:
post: { title: "{{ item.title }}", body: "{{ item.summary }}", link: "{{ item.url }}" }
- sheet.row_added:
sheet: Content Calendar
as: row
into:
post: { title: "{{ row.title }}", body: "{{ row.body }}", link: "{{ row.link }}" }

into expressions are evaluated over the payload of the entry that fired, and are subject to purity. When one entry declares into, every entry MUST declare into. All entries MUST bind the same names with the same nested key structure, so a path that resolves for one trigger resolves for every trigger. The bound names are ordinary root-scope bindings under the scope rules.

A single-trigger file MUST NOT declare into. With one payload shape, a value step is the one representation of that projection.