Appearance
Workflow triggers
A trigger starts a workflow automatically. Each trigger belongs to one workflow, has a name and a configuration, and can be enabled or disabled without deleting it. In the console, you manage triggers in the workflow's editor.
Besides triggers, a workflow can be started by hand from the console or with the executeWorkflow mutation (see Running a workflow). Each execution records how it was started in its triggerType, for example MANUAL or SCHEDULED. The API's TriggerType enum does not yet include edge app event and folder watch triggers, so don't select triggerType on executions those triggers started.
Trigger types
| Type | Fires when | Workflow input |
|---|---|---|
| Scheduled | A CRON schedule is due | Empty |
| Event | A record of a given kind is created, updated or deleted | The result of the optional data query, under data |
| Device MQTT | A device publishes an MQTT message on a matching topic | The message under mqtt, with the fields of a JSON payload also copied to the top level |
| Edge app event | An edge app on a controller publishes an event | The event under edgeAppEvent |
| Folder watch | A new file appears in a Folder | folderId and the file's key |
Scheduled
| Field | Meaning |
|---|---|
cronExpression | CRON expression with 5 or 6 fields: minute, hour, day of month, month, weekday and an optional year |
timezone | IANA time zone the schedule is evaluated in, for example Europe/Berlin. Optional. |
overlapPolicy | ALLOW, SKIP or CANCEL. Required, but not applied at run time: what happens when a previous run is still going is decided by the trigger's concurrency policy. |
Event
| Field | Meaning |
|---|---|
topic | The change to react to, as <resource>.<action>, for example job.created, order.updated or preflightReport.created |
dataQuery | Optional GraphQL query run when the event fires. The ID of the changed record is passed as $id, and the result appears in the workflow input under data. |
The listDataChangeEventTopics query returns every available topic. Not every listed topic fires yet, so test a trigger before you rely on it:
graphql
query EventTopics {
listDataChangeEventTopics {
topics { topic verb description }
}
}The event itself only identifies the record that changed and carries none of its fields, so use dataQuery to load the fields you need:
graphql
query DataQuery($id: JobID!) {
getJob(id: $id) {
id
name
status
quantity
}
}A workflow started this way reads the job as input.data.getJob.status, and so on.
Device MQTT
| Field | Meaning |
|---|---|
topic | Topic filter relative to the device, with the MQTT wildcards + and #. Do not include the tenant or device prefix; a topic starting with t/ is rejected. |
deviceId | Optional. Limits the trigger to one device (dev_...); without it, messages from every device in the tenant match. |
filter | Optional JSON Logic rule evaluated against the message envelope |
The workflow input contains mqtt with topic, relativeTopic, deviceId, qos, retain, receivedAt, payload (the parsed JSON payload) and payloadRaw. If the payload is a JSON object, its fields are also copied to the top level of the input. A filter that starts the workflow only when the payload's state is open:
json
{ "==": [{ "var": "mqtt.payload.state" }, "open"] }A new Device MQTT trigger starts listening within a few seconds. A change to the topic, device or filter of a trigger that is already enabled is not picked up on its own: disable the trigger, wait a few seconds, and enable it again.
For the MQTT side, see Getting started with MQTT.
Edge app event
All fields are optional. Leave them out to match every event in the tenant.
| Field | Meaning |
|---|---|
controllerId | Only events from this controller (ctl_...) |
edgeAppHandle | Only events from this edge app |
topic | Only events published on this topic (exact match, no wildcards) |
The workflow input contains edgeAppEvent with controllerId, edgeAppHandle, topic, emittedAt and payload.
Folder watch
| Field | Meaning |
|---|---|
folderId | The Folder to watch (fld_...) |
prefix | Folder-relative path to watch for new files. Only files directly under the prefix are picked up, not files in subdirectories. |
Folders are polled, so a new file starts the workflow after a short delay rather than instantly. Folder-watch triggers must use the concurrency policy ALLOW and no execution cap.
Concurrency
Every trigger has a concurrencyPolicy that decides what happens when it fires while an execution of the workflow is still pending or running (from any trigger or a manual run):
| Policy | Behaviour |
|---|---|
ALLOW | Start another execution (default) |
QUEUE | Queue the firing and start it when an execution finishes (at most 100 waiting; a firing waiting longer than an hour is dropped). The queued run does not get the original firing's input, such as the MQTT message. |
SKIP | Skip the new execution |
CANCEL | Cancel the workflow's pending and running executions and start the new one |
With ALLOW, maxConcurrentExecutions caps how many executions of the workflow may be pending or running when the trigger fires; beyond that the firing is skipped. Leave it out for no cap.
A trigger runs the workflow's active version. Set versionId to pin it to one specific version instead.
Managing triggers with the API
graphql
mutation NightlyReport($input: CreateWorkflowTriggerInput!) {
createWorkflowTrigger(input: $input) {
trigger { id name isEnabled }
errors { path message }
}
}json
{
"input": {
"workflowId": "wfl_...",
"name": "nightly-report",
"config": {
"scheduled": {
"cronExpression": "0 2 * * *",
"timezone": "Europe/Berlin",
"overlapPolicy": "SKIP"
}
}
}
}config takes exactly one of scheduled, event, deviceMqtt, edgeAppEvent or folderWatch. List a workflow's triggers, and turn one off or on:
graphql
query Triggers($workflowId: WorkflowID!) {
workflowTriggers(workflowId: $workflowId) {
id
name
isEnabled
concurrencyPolicy
config {
__typename
... on ScheduledTriggerConfig { cronExpression timezone overlapPolicy }
... on EventTriggerConfig { topic filter dataQuery }
}
}
}
mutation Disable($id: WorkflowTriggerID!) {
disableWorkflowTrigger(input: { id: $id }) {
trigger { id isEnabled }
errors { path message }
}
}enableWorkflowTrigger, updateWorkflowTrigger and deleteWorkflowTrigger work the same way.