Rules#

The Rules service provides a rule engine for condition matching on incoming device messages — by threshold comparison, or by running a Lua script and using its result.

The engine is driven by the event stream: every message published by a thing is evaluated against all rules assigned to that thing. Rules are created within a group and then assigned to individual things. Scripts are also created within a group, and referenced by ID from a rule's script conditions.

Prerequisite: the thing's profile must have rule_enabled: true set in its config (see Dispatcher Flags) — otherwise its messages never reach the rules engine, and assigned rules simply never fire.

Rules#

A rule evaluates a set of conditions against an incoming payload. When conditions are met according to the configured operator (AND or OR), the rule triggers one or more actions.

Field Description
id Unique rule identifier
group_id ID of the group the rule belongs to
name Human-readable rule name
description Optional free-form description
input What triggers evaluation — type (message or alarm), thing_ids, and an optional config (e.g. subtopic filter)
conditions List of conditions to evaluate — threshold comparisons or script runs (see below)
operator AND or OR — required when more than one condition is defined
actions List of actions to trigger when the conditions are met (see below)

Conditions#

Each condition has a required type, which selects how it's evaluated.

Field Description
type threshold or script
field Threshold only. The payload field name to evaluate. For SenML messages, this matches the name key. For JSON messages, dot-notation paths are supported (e.g. sensors.temperature).
comparator Threshold only. Comparison operator: ==, >=, <=, >, <
threshold Threshold only. Numeric value to compare against
script_id Script only. ID of the script to run

A script condition runs the referenced Lua script and uses its return value as the result: a strict return true counts as met, anything else — return false, no return, or a runtime error — counts as not met. The script's result is combined with any other conditions on the rule the same way a threshold result would be, under the rule's operator. Script conditions run with a read-only API (only mfx.log is available, since a condition must not have side effects), and are only supported on message-input rules, not alarm-input rules. Every evaluation is recorded as a script run.

Actions#

Each action has a required type, which selects what happens when the rule triggers.

Field Description
type alarm, smtp, smpp, or webhook
id smtp/smpp only. ID of the notifier to send the notification through
level alarm only. Alarm severity, 1–5

A webhook action forwards the message to the webhooks registered for the thing that published it, so it takes no id. alarm actions are only supported on message-input rules — an alarm-input rule cannot raise another alarm.

Create Rules#

curl -s -S -i -X POST \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {
        "name": "Temperature and Humidity Alert",
        "description": "Triggers when temperature exceeds 45°C and humidity drops below 20%",
        "input": {
          "type": "message",
          "thing_ids": ["123e4567-e89b-12d3-a456-426614174000"]
        },
        "conditions": [
          {"type": "threshold", "field": "temperature", "comparator": ">", "threshold": 45},
          {"type": "threshold", "field": "humidity", "comparator": "<", "threshold": 20}
        ],
        "operator": "AND",
        "actions": [
          {"type": "smtp", "id": "513e2557-e09b-42d3-s456-425614175403"},
          {"type": "alarm", "level": 3}
        ]
      }
    ]
  }' \
  http://localhost/svcrules/groups/<group_id>/rules

A rule with a script condition instead:

curl -s -S -i -X POST \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {
        "name": "Condensation Risk Alert",
        "input": {
          "type": "message",
          "thing_ids": ["123e4567-e89b-12d3-a456-426614174000"]
        },
        "conditions": [
          {"type": "script", "script_id": "456e4567-e89b-12d3-a456-426614174abc"}
        ],
        "actions": [
          {"type": "alarm", "level": 3}
        ]
      }
    ]
  }' \
  http://localhost/svcrules/groups/<group_id>/rules

List Rules by Group#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  "http://localhost/svcrules/groups/<group_id>/rules?offset=0&limit=20"

List endpoints support offset, limit (max 200), order=name, dir (asc/desc), name (partial match), and input_type (message/alarm) query parameters.

List Rules by Thing#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  "http://localhost/svcrules/things/<thing_id>/rules"

View a Rule#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/rules/<rule_id>

Update a Rule#

curl -s -S -i -X PUT \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Temperature Alert",
    "input": {"type": "message"},
    "conditions": [{"type": "threshold", "field": "temperature", "comparator": ">", "threshold": 50}],
    "actions": [{"type": "alarm", "level": 3}]
  }' \
  http://localhost/rules/<rule_id>

Updating a rule does not affect its thing assignments — use the assign/unassign endpoints below for that.

Assign Things to a Rule#

curl -s -S -i -X POST \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"thing_ids": ["123e4567-e89b-12d3-a456-426614174000"]}' \
  http://localhost/rules/<rule_id>/things

Unassign Things from a Rule#

curl -s -S -i -X PATCH \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"thing_ids": ["123e4567-e89b-12d3-a456-426614174000"]}' \
  http://localhost/rules/<rule_id>/things

List Things Assigned to a Rule#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/rules/<rule_id>/things

Delete Rules#

curl -s -S -i -X PATCH \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"rule_ids": ["789e4567-e89b-12d3-a456-426614174abc"]}' \
  http://localhost/rules

Scripts#

A Lua script is a group-scoped resource, referenced by ID from a rule's script conditions — it is not assigned to things directly, and only runs when a rule's condition invokes it.

Field Description
id Unique script identifier
group_id ID of the group the script belongs to
name Human-readable script name
description Optional free-form description
script Lua source code (max 65,535 bytes)

Execution Environment#

Each script execution receives an isolated Lua environment with an mfx global:

Field / Function Description
mfx.message.payload Parsed message payload (JSON object or array item)
mfx.message.subtopic Message subtopic
mfx.message.created Message creation timestamp (Unix)
mfx.message.thing_id ID of the thing that published the message
mfx.log(message) Appends a message to the run log (max 256 lines, 2048 chars each).

mfx.log is currently the only bound function, since scripts only run as read-only rule conditions today. Available Lua standard libraries: base, math, string, table. The print function is disabled. Scripts are capped at 1,000,000 instructions and 65,535 bytes of source.

Example condition script — flags condensation risk from a payload's temperature and humidity fields:

local payload = mfx.message.payload
local temp = tonumber(payload["temperature"])
local hum  = tonumber(payload["humidity"])

if not temp or not hum or hum <= 0 then
  mfx.log("Invalid or missing fields")
  return
end

-- Magnus formula: dew point from temperature and relative humidity
local gamma = math.log(hum / 100.0) + (17.625 * temp) / (243.04 + temp)
local dew_point = 243.04 * gamma / (17.625 - gamma)
local spread = temp - dew_point  -- smaller spread → closer to condensation

mfx.log(string.format("temp=%.1f  hum=%.1f%%  dew_point=%.1f  spread=%.1f",
  temp, hum, dew_point, spread))

-- condensation risk if the surface is within 2°C of the dew point
return spread <= 2.0

Create Scripts#

curl -s -S -i -X POST \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "scripts": [
      {
        "name": "Condensation risk",
        "description": "Flags condensation risk from temperature and humidity readings",
        "script": "local payload = mfx.message.payload\n..."
      }
    ]
  }' \
  http://localhost/svcrules/groups/<group_id>/scripts

List Scripts by Group#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/svcrules/groups/<group_id>/scripts

View a Script#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/svcrules/scripts/<script_id>

Update a Script#

curl -s -S -i -X PUT \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "Condensation risk (v2)", "script": "..."}' \
  http://localhost/svcrules/scripts/<script_id>

Delete Scripts#

curl -s -S -i -X PATCH \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"script_ids": ["456e4567-e89b-12d3-a456-426614174abc"]}' \
  http://localhost/svcrules/scripts

Script Runs#

Every script condition evaluation is recorded as a run, capturing the outcome, logs, and any runtime error.

Field Description
id Unique run identifier
script_id ID of the script that was executed
rule_id ID of the rule whose condition triggered the run
thing_id ID of the thing that triggered the execution
logs Log lines written via mfx.log()
started_at Execution start timestamp (RFC 3339)
finished_at Execution end timestamp (RFC 3339)
status success or fail
error Runtime error message, if any

status reflects only whether the script ran without a Lua runtime error — it does not reflect whether the script's return value counted as the condition being met. rule_id is empty on runs recorded before rule-scoped tracking was added.

List Runs for a Rule#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/rules/<rule_id>/runs

List Runs for a Thing#

curl -s -S -i \
  -H "Authorization: Bearer <user_token>" \
  http://localhost/svcrules/things/<thing_id>/runs

Both listing endpoints support the same status, from, to (Unix ms), and pagination query parameters.

Delete Runs#

curl -s -S -i -X PATCH \
  -H "Authorization: Bearer <user_token>" \
  -H "Content-Type: application/json" \
  -d '{"script_run_ids": ["789e4567-e89b-12d3-a456-426614174xyz"]}' \
  http://localhost/svcrules/runs

For the full API reference, see the API documentation.