Appearance
Automating on device data
To react to what a device sends, attach a Device MQTT trigger to a workflow. Each matching message a device publishes starts one run of the workflow, with the message as the workflow input.
Device MQTT triggers are independent of metric recording. They fire on any topic a device publishes to, including s/metrics and topics of your own such as events/door.
Create the trigger
In the console, open the workflow, go to Triggers, choose Add Trigger and select the type Device MQTT. Or use the createWorkflowTrigger mutation:
graphql
mutation AddDeviceMqttTrigger($input: CreateWorkflowTriggerInput!) {
createWorkflowTrigger(input: $input) {
trigger {
id
name
isEnabled
}
errors {
path
message
}
}
}json
{
"input": {
"workflowId": "wfl_…",
"name": "Oven too hot",
"config": {
"deviceMqtt": {
"topic": "s/metrics",
"deviceId": "dev_…",
"filter": "{\"some\":[{\"var\":\"mqtt.payload.points\"},{\"and\":[{\"==\":[{\"var\":\"name\"},\"temperature\"]},{\">\":[{\"var\":\"value\"},80]}]}]}"
}
}
}
}| Field | Notes |
|---|---|
topic | Required. The device-relative topic, as the device publishes it. MQTT wildcards + (one level) and # (all remaining levels) are allowed, for example events/+. Do not include a device or tenant prefix; a topic starting with t/ is rejected. |
deviceId | Optional dev_… ID. Leave it out to match every device in the tenant. |
filter | Optional JSONLogic rule, passed as a JSON string. The workflow only starts when the rule is truthy. |
Triggers are enabled when created (isEnabled defaults to true). A new trigger starts listening within a few seconds. Changing the topic, device or filter of a trigger that is already listening does not take effect on its own: disable the trigger, wait a few seconds, then enable it again.
Only messages from devices connected with a valid device token ID as client ID are considered (see Connecting a device).
What the workflow receives
The filter is evaluated against this object:
json
{
"mqtt": {
"relativeTopic": "s/metrics",
"topic": "…",
"deviceId": "dev_…",
"tenantId": "…",
"fromClient": "dtk_…",
"qos": 1,
"retain": false,
"receivedAt": "2026-09-24T10:15:00Z",
"payloadRaw": "{\"points\":[…]}",
"payload": { "points": [ … ] }
}
}relativeTopicis the topic as the device published it.topicis the broker's internal form of it and includes a namespace prefix.payloadis the message parsed as JSON, ornullif it is not JSON.payloadRawis the message as text.
The workflow input is the same object, plus the payload's top-level keys copied alongside mqtt. For the metrics message above, a workflow can read either mqtt.payload.points or points. A payload that is JSON but not an object (an array or a number) is available as payload instead.
In the filter, some sets var to each element of the array it walks, which is why the example reads name and value directly.
Example: a Lua script node
A root script node receives the workflow input as its first vararg. This one sends a notification when a temperature reading is above 80:
lua
-- A root script node receives the workflow input as its first vararg.
local input = ...
local mqtt = input.mqtt
local hot = {}
for _, point in ipairs(input.points or {}) do
if point.name == "temperature" and point.value > 80 then
table.insert(hot, point.value)
end
end
if #hot == 0 then
return { alerted = false }
end
local ok, res = ctx.notify.send({
group = "ops-team",
subject = "Temperature alert",
body = "Device " .. mqtt.deviceId .. " reported " .. tostring(hot[1]) .. " on " .. mqtt.relativeTopic,
})
if not ok then error(res) end
ctx.log.info("temperature alert sent for " .. mqtt.deviceId)
return { alerted = true, readings = #hot }Replace ops-team with the name or ID of one of your notification groups. The script's return value becomes the node's output. See the Lua API overview for what else a script can call.
Things to know
- One message starts one run. A device that publishes every second starts a workflow every second, so narrow the trigger with
deviceId, a specifictopicand afilter, and choose aconcurrencyPolicythat suits the load. - A device only sees its own topics: platform events (such as job changes) are not delivered to devices, and a device cannot subscribe to other devices' topics. To react to platform events, use an event trigger on the workflow instead.