Skip to main content
Version: Next

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.Temperature from 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 POST with a JSON body and answers 2xx. python3 -m http.server is not enough: it answers 501 to POST. 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 (http or https).

Steps​

1. Add the alarm​

  1. Open Tags and types and select Line1.Temperature. The alarms section of its editor lists the tag's alarms.
  2. Add an alarm: Name High temperature, Condition greater, Setpoint 30, Deadband 1, On-delay (seconds) 5, Severity 700.
  3. 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:

FieldMeaning
condition: greaterActive when the value is above the setpoint
deadband: 1Clears only once the value is at or below 30 - 1 = 29
onDelayMs: 5000The value must stay above 30 for 5 seconds (gateway clock) before the alarm goes active
severity: 7001 to 1000; the pipeline filters on it

2. Watch it go active​

  1. Open Alarms, then Active. The Active alarms list is empty for this alarm.
  2. Drive the tag above 30 (publish 31 to 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 state active.
  3. Drive it to 29.5: still active, because the deadband is 1. At 29 or below it clears. An alarm that clears before anyone acknowledged it stays in the list as cleared until 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​

  1. Open Alarms, then Pipelines; the page is titled Notification pipelines. The entry is shown to administrators only.
  2. Create a New pipeline: Name Page the on-call line, Send when active, Only at or above 600, Wait first (seconds) 60, Webhook address https://hooks.example.com/alarm.
  3. 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"}'
FieldMeaning
triggeractive, acknowledged or cleared: which alarm event is sent
minSeverity0 to 1000; alarms below it are not sent
delaySeconds0 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."
webhookUrlA 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:

FieldMeaning
at, pipelineName, alarmName, tagPath, eventWhat was sent, when, for which alarm
statussent, failed or skipped
detailHTTP 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​