RelayX Alerts
Server-side alerts that run on the RelayX platform. Once created, they evaluate continuously without your application needing to be running.
Access via app.alert.
Alert Types
| Type | Description |
|---|---|
| Threshold | Triggers when a metric stays above or below a value for a sustained period. Use for boundary conditions like temperature > 80 or battery < 20. |
| Rate-change | Triggers when a metric changes too quickly over a time window. Use for detecting sudden spikes or drops like a pressure drop of 10 units in 1 minute. |
API
create()
Create a server-side threshold or rate-change alert.
- JavaScript
- Python
await app.alert.create(params)await app.alert.create(params)| Parameter | Type | Description |
|---|---|---|
name | string | Unique alert name |
description | string | (optional) Human-readable description |
type | string | "THRESHOLD" or "RATE_CHANGE" |
metric | string | The telemetry metric to monitor |
config | object | Alert configuration (see below) |
notification_channel | string[] | (optional) Notification channel IDs to deliver to |
Returns the alert object with a listen() method attached
Threshold Example
- JavaScript
- Python
const alert = await app.alert.create({
name: "high_temp",
description: "Alert when temperature exceeds 85",
type: "THRESHOLD",
metric: "temperature",
config: {
scope: {
type: "DEVICE",
value: deviceId,
},
operator: ">",
value: 85,
duration: 300,
recovery_duration: 120,
cooldown: 600,
},
notification_channel: [channelId],
});
alert = await app.alert.create({
"name": "high_temp",
"description": "Alert when temperature exceeds 85",
"type": "THRESHOLD",
"metric": "temperature",
"config": {
"scope": {
"type": "DEVICE",
"value": device_id,
},
"operator": ">",
"value": 85,
"duration": 300,
"recovery_duration": 120,
"cooldown": 600,
},
"notification_channel": [channel_id],
})
Example response:
{
"name": "high_temp",
"description": "Alert when temperature exceeds 85",
"type": "THRESHOLD",
"metric": "temperature",
"config": {
"scope": { "type": "DEVICE", "value": "69ca39e4ed4d9fe786145b48" },
"operator": ">",
"value": 85,
"duration": 300,
"recovery_duration": 120,
"cooldown": 600
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
Rate-Change Example
- JavaScript
- Python
const alert = await app.alert.create({
name: "pressure_drop",
type: "RATE_CHANGE",
metric: "pressure",
config: {
scope: { type: "DEVICE", value: deviceId },
operator: "<",
value: -10,
duration: 60,
recovery_duration: 30,
},
notification_channel: [channelId],
});
alert = await app.alert.create({
"name": "pressure_drop",
"type": "RATE_CHANGE",
"metric": "pressure",
"config": {
"scope": {"type": "DEVICE", "value": device_id},
"operator": "<",
"value": -10,
"duration": 60,
"recovery_duration": 30,
},
"notification_channel": [channel_id],
})
Config Reference
| Field | Type | Description |
|---|---|---|
scope.type | string | "DEVICE", "LOGICAL_GROUP" or "HEIRARCHY" |
scope.value | string | The device ID or group identifier |
operator | string | >, >=, ==, <=, < |
value | number | The threshold or rate value |
duration | number | Seconds the condition must hold before the alert fires |
recovery_duration | number | Seconds the condition must clear before the alert resolves |
cooldown | number | (optional) Minimum seconds between re-fires |
update()
Update a server-side alert's configuration.
- JavaScript
- Python
await app.alert.update(params)await app.alert.update(params)| Parameter | Type | Description |
|---|---|---|
id | string | The alert ID |
config | object | (optional) Updated config (partial update) |
- JavaScript
- Python
const updated = await app.alert.update({
id: alertId,
config: { value: 90 },
});
updated = await app.alert.update({
"id": alert_id,
"config": {"value": 90},
})
Example response:
{
"name": "high_temp",
"type": "THRESHOLD",
"metric": "temperature",
"config": {
"scope": { "type": "DEVICE", "value": "69ca39e4ed4d9fe786145b48" },
"operator": ">",
"value": 90,
"duration": 300,
"recovery_duration": 120,
"cooldown": 600
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
listen()
Subscribe to alert state changes (fire, resolved, acknowledged).
- JavaScript
- Python
await alert.listen(callbacks)await alert.listen(callbacks)| Callback | Description |
|---|---|
onFire / on_fire | Alert condition met, alert has fired |
onResolved / on_resolved | Alert condition cleared, alert resolved |
onAck / on_ack | A device's alert incident was acknowledged |
- JavaScript
- Python
await alert.listen({
onFire: (data) => console.log("FIRED:", data),
onResolved: (data) => console.log("RESOLVED:", data),
onAck: (data) => console.log("ACK:", data),
});
await alert.listen({
"on_fire": lambda data: print("FIRED:", data),
"on_resolved": lambda data: print("RESOLVED:", data),
"on_ack": lambda data: print("ACK:", data),
})
onFire continues to fire after acknowledgement
When a device's incident is acknowledged, RelayX stops dispatching internal notifications (webhooks, email, and any channel attached to the rule via notification_channel). However, while the underlying condition is still true, the alerting engine continues to publish fire events on a cooldown for the audit trail. Those events are delivered to every subscriber of listen(), so your onFire / on_fire callback will keep being invoked after an ack.
This is by design: it preserves a complete record of how long a condition stayed in breach.
If your onFire callback dispatches its own notifications (Slack, PagerDuty, SMS, etc.), it will continue to fire after the user has acknowledged the alert. The SDK does not silence custom notification logic for you. To match the built-in behavior, track the acknowledged state in your callback and skip dispatch while the incident is in acknowledged. The onAck / on_ack callback gives you the transition you need.
The following operations work the same for both RelayX and ephemeral alerts. State changes (ack, mute, unmute) are globally synced — if one App SDK instance acknowledges or mutes an alert, all other instances see the change.
delete()
Permanently remove an alert.
- JavaScript
- Python
await app.alert.delete(alertId)await app.alert.delete(alertId);
await app.alert.delete(alert_id)await app.alert.delete(alert_id)
list()
Retrieve all alerts.
- JavaScript
- Python
await app.alert.list()const alerts = await app.alert.list();
await app.alert.list()alerts = await app.alert.list()
Example response:
[
{
"name": "high_temp",
"type": "THRESHOLD",
"metric": "temperature",
"config": {
"scope": { "type": "DEVICE", "value": "69ca39e4ed4d9fe786145b48" },
"operator": ">",
"value": 85,
"duration": 300,
"recovery_duration": 120
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
]
get()
Retrieve a single alert by name.
- JavaScript
- Python
await app.alert.get(name)const alert = await app.alert.get("high_temp");
await app.alert.get(name)alert = await app.alert.get("high_temp")
Example response:
{
"name": "high_temp",
"type": "THRESHOLD",
"metric": "temperature",
"config": {
"scope": { "type": "DEVICE", "value": "69ca39e4ed4d9fe786145b48" },
"operator": ">",
"value": 85,
"duration": 300,
"recovery_duration": 120,
"cooldown": 600
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
history()
Fetch a chronological timeline of alert events (fire, resolved, ack) for a device, a rule, or the entire org. Each event carries an incident_id so you can group events by alerting episode.
- JavaScript
- Python
await app.alert.history(params)await app.alert.history(params)| Parameter | Type | Description |
|---|---|---|
rule_type | string | "DEVICE", "RULE", or "ORG" |
device_ident | string | Single-device shorthand. Required for "DEVICE" unless device_idents is set |
device_idents | string[] | Multi-device variant. Optional on every rule_type. Filters results to events from these devices |
rule_id | string | Required when rule_type is "RULE". Optional on "DEVICE" and "ORG" (narrows the result) |
rule_states | string[] | (optional) Subset of ["fire", "resolved", "ack"]. Defaults to all three |
incident_id | string | (optional) Filter to a single incident's events |
start | string | Start time (ISO 8601, UTC) |
end | string | End time (ISO 8601, UTC). Must be after start |
rule_type semantics:
rule_type | Required companion | Returns |
|---|---|---|
"DEVICE" | device_ident or device_idents | Every alert event for those devices |
"RULE" | rule_id | Every event for that rule. Optionally narrowed with device_idents |
"ORG" | (none) | Every alert event in the org. Optionally narrowed with device_idents and/or rule_id |
Returns { events: [...] } where each event has the shape:
| Field | Type | Description |
|---|---|---|
state | string | "fire", "resolved", or "ack" |
value | any | The rolling state captured at the event |
timestamp | number | Unix milliseconds |
incident_id | string | null | Server-generated UUID for the alerting episode. Persists across re-fires/acks until resolved |
rule_id | string | The rule that fired |
device_id | string | The device the event applies to |
Events are returned in chronological order across the whole window.
- JavaScript
- Python
// Full timeline for a device
const { events } = await app.alert.history({
rule_type: "DEVICE",
device_ident: "sensor_01",
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-25T00:00:00.000Z",
});
// Org-wide, every alert in the window. One round trip.
const { events } = await app.alert.history({
rule_type: "ORG",
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-25T00:00:00.000Z",
});
// Org-wide narrowed to several devices
const { events } = await app.alert.history({
rule_type: "ORG",
device_idents: ["sensor_01", "sensor_02", "gateway_a"],
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-25T00:00:00.000Z",
});
// One incident's full timeline
const { events } = await app.alert.history({
rule_type: "RULE",
rule_id: "rule_abc123",
incident_id: "uuid-here",
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-25T00:00:00.000Z",
});
# Full timeline for a device
result = await app.alert.history({
"rule_type": "DEVICE",
"device_ident": "sensor_01",
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-25T00:00:00.000Z",
})
events = result["events"]
# Org-wide, every alert in the window. One round trip.
result = await app.alert.history({
"rule_type": "ORG",
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-25T00:00:00.000Z",
})
# Org-wide narrowed to several devices
result = await app.alert.history({
"rule_type": "ORG",
"device_idents": ["sensor_01", "sensor_02", "gateway_a"],
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-25T00:00:00.000Z",
})
# One incident's full timeline
result = await app.alert.history({
"rule_type": "RULE",
"rule_id": "rule_abc123",
"incident_id": "uuid-here",
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-25T00:00:00.000Z",
})
Querying acknowledgements
Acknowledgement events are part of the same timeline. Pass rule_states: ["ack"] to query only acks.
- JavaScript
- Python
const { events } = await app.alert.history({
rule_type: "RULE",
rule_id: "rule_abc123",
rule_states: ["ack"],
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-25T00:00:00.000Z",
});
result = await app.alert.history({
"rule_type": "RULE",
"rule_id": "rule_abc123",
"rule_states": ["ack"],
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-25T00:00:00.000Z",
})
Grouping events into incidents
Every event carries an incident_id. An incident opens on the normal → alerting transition (UUID generated server-side), persists across re-fires and acks, and closes on the return to normal. To present an incident-level UI, reduce events by incident_id.
Alert history retention depends on your plan. For example, the free plan retains 3 days of history. Queries outside your retention window will return empty results.
ack()
Acknowledge a specific device's alert.
- JavaScript
- Python
await app.alert.ack(params)await app.alert.ack(params)| Parameter | Type | Description |
|---|---|---|
device_ident | string | The device identifier |
alert_id | string | The alert ID |
acked_by | string | Who is acknowledging (e.g., operator name) |
ack_notes | string | (optional) Notes about the acknowledgement |
- JavaScript
- Python
await app.alert.ack({
device_ident: "sensor_01",
alert_id: alertId,
acked_by: "operator_jane",
ack_notes: "Investigating issue",
});
await app.alert.ack({
"device_ident": "sensor_01",
"alert_id": alert_id,
"acked_by": "operator_jane",
"ack_notes": "Investigating issue",
})
mute()
Silence an alert so it stops delivering notifications.
- JavaScript
- Python
await app.alert.mute(params)await app.alert.mute(params)| Parameter | Type | Description |
|---|---|---|
id | string | The alert ID |
mute_config.type | string | "FOREVER" or "TIME_BASED" |
mute_config.mute_till | string | (TIME_BASED only) ISO 8601 timestamp to unmute at |
| Mute Type | Behavior |
|---|---|
"FOREVER" | The alert stays muted until explicitly unmuted via unmute(). The alert still evaluates but notifications are suppressed. |
"TIME_BASED" | The alert is muted until the specified mute_till timestamp, then automatically unmutes. |
- JavaScript
- Python
// mute forever
await app.alert.mute({
id: alertId,
mute_config: { type: "FOREVER" },
});
// mute until a specific time
await app.alert.mute({
id: alertId,
mute_config: {
type: "TIME_BASED",
mute_till: "2026-04-01T00:00:00.000Z",
},
});
# mute forever
await app.alert.mute({
"id": alert_id,
"mute_config": {"type": "FOREVER"},
})
# mute until a specific time
await app.alert.mute({
"id": alert_id,
"mute_config": {
"type": "TIME_BASED",
"mute_till": "2026-04-01T00:00:00.000Z",
},
})
unmute()
Re-enable a muted alert.
- JavaScript
- Python
await app.alert.unmute(alertId)await app.alert.unmute(alertId);
await app.alert.unmute(alert_id)await app.alert.unmute(alert_id)