From an alarm to a webhook
This guide adds an alarm to a tag and sends it to another system. It is for the engineer who sets up the first alarm and the administrator who connects it to a receiver. Pipelines call out of the gateway, so only an administrator may create them.
Prerequisites
-
A tag with a numeric value, for example
Line1.Temperaturefrom An MQTT sensor on a dashboard, and a way to change its value. -
The engineer role for the alarm, the operator role to acknowledge, the admin role for the pipeline.
-
Something that accepts an HTTP
POSTwith a JSON body and answers2xx.python3 -m http.serveris not enough: it answers501toPOST. Use a request-bin style service you trust, or a few lines of your own:node -e 'require("http").createServer((q,s)=>{let b="";q.on("data",c=>b+=c).on("end",()=>{console.log(q.method,q.url,b);s.writeHead(200);s.end()})}).listen(8080)'The gateway must be able to reach the receiver's URL (
httporhttps).
Steps
1. Add the alarm
- Open Tags and types and select
Line1.Temperature. The alarms section of its editor lists the tag's alarms. - Add an alarm: Name
High temperature, Condition greater, Setpoint30, Deadband1, On-delay (seconds)5, Severity700. - Save.
Over the API, POST /api/v1/tags/<id or path>/alarms:
curl -b cookies.txt -H "Origin: https://app.scadex.io" -H "Content-Type: application/json" \
https://app.scadex.io/api/v1/tags/Line1.Temperature/alarms \
-d '{"name":"High temperature","condition":"greater","setpoint":30,"deadband":1,"onDelayMs":5000,"severity":700,"enabled":true}'
What the fields do, from Alarms:
| Field | Meaning |
|---|---|
condition: greater | Active when the value is above the setpoint |
deadband: 1 | Clears only once the value is at or below 30 - 1 = 29 |
onDelayMs: 5000 | The value must stay above 30 for 5 seconds (gateway clock) before the alarm goes active |
severity: 700 | 1 to 1000; the pipeline filters on it |
2. Watch it go active
- Open Alarms, then Active. The Active alarms list is empty for this alarm.
- Drive the tag above 30 (publish
31to the sensor's topic, or write the tag). For the first 5 seconds nothing happens; then the alarm appears in the list with its severity and value, in stateactive. - Drive it to
29.5: still active, because the deadband is 1. At29or below it clears. An alarm that clears before anyone acknowledged it stays in the list ascleareduntil it is acknowledged.
Over the API: GET /api/v1/alarms/active lists everything outstanding, most severe first. Over /sync, alarm.subscribe gives a snapshot and then alarm.active, alarm.cleared, ... frames.
3. Acknowledge it
In the Alarms list, acknowledge the alarm; it moves to acknowledged (still active) or, if it had cleared already, to normal and leaves the list. Who acknowledged it is recorded.
Over the API (operator role):
curl -b cookies.txt -H "Origin: https://app.scadex.io" -H "Content-Type: application/json" \
https://app.scadex.io/api/v1/alarms/<alarm id>/acknowledge -d '{"occurrenceId":"<occurrenceId from the active list>"}'
Passing the occurrenceId you were looking at means the request gets 409 instead of acknowledging a newer firing nobody saw. Alarms, History and GET /api/v1/alarms/history?alarmId=<id> show the lifecycle events.
4. Create the pipeline
- Open Alarms, then Pipelines; the page is titled Notification pipelines. The entry is shown to administrators only.
- Create a New pipeline: Name
Page the on-call line, Send whenactive, Only at or above600, Wait first (seconds)60, Webhook addresshttps://hooks.example.com/alarm. - Save.
Over the API, POST /api/v1/admin/alarm-pipelines:
curl -b cookies.txt -H "Origin: https://app.scadex.io" -H "Content-Type: application/json" \
https://app.scadex.io/api/v1/admin/alarm-pipelines \
-d '{"name":"Page the on-call line","enabled":true,"trigger":"active","minSeverity":600,"delaySeconds":60,"webhookUrl":"https://hooks.example.com/alarm"}'
| Field | Meaning |
|---|---|
trigger | active, acknowledged or cleared: which alarm event is sent |
minSeverity | 0 to 1000; alarms below it are not sent |
delaySeconds | 0 to 3600. For active, the gateway waits, then checks the alarm again and sends only if it is still active; otherwise the delivery is recorded as skipped with "The alarm was no longer active after the delay." |
webhookUrl | A full http or https URL, up to 500 characters |
A shelved alarm is never sent; its lifecycle is still recorded.
5. What the receiver gets
One POST per event, Content-Type: application/json, User-Agent: Scadex-Gateway, with a 10 second timeout. Any 2xx counts as delivered.
{
"event": "alarm.active",
"test": false,
"pipeline": "Page the on-call line",
"sentAt": "2026-11-08T13:02:11.482Z",
"alarm": {
"id": "a1b2c3",
"tagId": "t9f8e7",
"tagPath": "Line1.Temperature",
"name": "High temperature",
"severity": 700,
"condition": "greater",
"setpoint": 30,
"deadband": 1,
"onDelayMs": 5000,
"enabled": true,
"state": "active",
"occurrenceId": "o4d5e6",
"pendingSince": null,
"activatedAt": 1794488471000,
"acknowledgedAt": null,
"acknowledgedBy": null,
"acknowledgedByName": null,
"clearedAt": null,
"value": 31.2,
"quality": "Good",
"valueTimestamp": 1794488466000,
"stale": false,
"inherited": false,
"shelvedUntil": null
}
}
alarm is the same object a /sync alarm.* frame carries (see Alarms API). Timestamps inside it are Unix milliseconds; sentAt is ISO 8601.
6. Send a test
On the pipeline's row, use its test action. The gateway posts a made-up alarm at once, without the wait:
{ "event": "alarm.active", "test": true, "pipeline": "Page the on-call line", "sentAt": "...",
"alarm": { "id": "test", "name": "Test alarm", "tagPath": "Test.Tag", "severity": 500, "state": "active", "value": 1.0 } }
The toast says Test sent to Page the on-call line, or Test failed: ... with the reason. Over the API: POST /api/v1/admin/alarm-pipelines/<id>/test answers {"status":"sent","detail":"HTTP 200"} or {"status":"failed","detail":"..."}.
7. Check the send log
The pipelines page lists the latest deliveries beneath the pipelines and refreshes every 5 seconds. GET /api/v1/admin/alarm-pipelines/deliveries?limit=50 returns them newest first:
| Field | Meaning |
|---|---|
at, pipelineName, alarmName, tagPath, event | What was sent, when, for which alarm |
status | sent, failed or skipped |
detail | HTTP 200; The receiver answered HTTP 500.; The receiver didn't answer within 10 seconds.; or the connection error |
The gateway keeps the last 500 deliveries. A failed delivery is not retried.
Where to go next
- Alarms for states, quality rules and what happens on restart.
- Alarm pipelines for the administration page.
- Model a motor as a tag type to define the alarm once on a type for every instance.
- Event streams and Audit log for other ways events leave the gateway.