Ephemeral Alerts
Client-side alerts that run custom evaluation logic in your application. You define the condition as a function — the SDK handles timing, recovery, and notification delivery. Use ephemeral alerts when RelayX alerts built-in operators aren't enough.
Access via app.alert.
Ephemeral alerts only evaluate data while your application is running. If your app stops, the evaluator stops — no alerts will fire until it's restarted.
API
createEphemeral()
Create a client-side alert with custom evaluation logic.
- JavaScript
- Python
await app.alert.createEphemeral(params)await app.alert.create_ephemeral(params)Returns the alert object with listen(), setEvaluator(), and stop() methods attached
- JavaScript
- Python
const alert = await app.alert.createEphemeral({
name: "custom_check",
type: "EPHEMERAL",
config: {
topic: {
source: "TELEMETRY",
device_ident: "sensor_01",
last_token: "cpu_usage",
},
duration: 30,
recovery_duration: 15,
recovery_eval_type: "VALUE",
cooldown: 60,
},
notification_channel: [channelId],
});
alert = await app.alert.create_ephemeral({
"name": "custom_check",
"type": "EPHEMERAL",
"config": {
"topic": {
"source": "TELEMETRY",
"device_ident": "sensor_01",
"last_token": "cpu_usage",
},
"duration": 30,
"recovery_duration": 15,
"recovery_eval_type": "VALUE",
"cooldown": 60,
},
"notification_channel": [channel_id],
})
Example response:
{
"name": "custom_check",
"type": "EPHEMERAL",
"config": {
"topic": {
"source": "TELEMETRY",
"device_ident": "sensor_01",
"last_token": "cpu_usage"
},
"duration": 30,
"recovery_duration": 15,
"recovery_eval_type": "VALUE",
"cooldown": 60
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
The returned object also has listen(), setEvaluator(), and stop() methods attached.
Topic Config
| Field | Type | Description |
|---|---|---|
source | string | "TELEMETRY", "COMMAND", or "EVENT" |
device_ident | string | Device identifier, or "*" for all devices |
last_token | string | Metric/command/event name, or "*" for all |
Recovery Eval Type
The recovery_eval_type field controls how the alert resolves after firing. Defaults to "VALUE".
| Value | Behavior |
|---|---|
"VALUE" | The evaluator must continuously return false for the full recovery_duration before the alert resolves. Data must keep flowing — silence alone won't resolve it. |
"TIMER" | A countdown starts when the alert is in a fired or acknowledged state. If no data arrives for the recovery_duration, the alert resolves automatically. If data does arrive and the evaluator returns false for the recovery_duration, it also resolves normally. |
Use "VALUE" when you need certainty that the condition has genuinely cleared — the evaluator must actively confirm it.
Use "TIMER" when silence itself means the problem is gone — for example, if a device stops sending error events, the alert should auto-resolve.
setEvaluator()
Define the custom evaluation function. Called on every incoming data point — return true to fire, false to clear. Only one evaluator can be attached per ephemeral alert — calling this again will throw an error.
- JavaScript
- Python
alert.setEvaluator(fn)alert.set_evaluator(fn)The function receives a rolling state object. As new data arrives, the rolling state is updated — it always contains the latest value for each metric. Previous metrics are preserved even when new ones arrive.
For example, if temperature arrives at t=1 and humidity arrives at t=30, the rolling state at t=30 looks like:
{
"sensor_01": {
"temperature": { "value": 22.5, "timestamp": 1774690200000 },
"humidity": { "value": 61.3, "timestamp": 1774690229000 }
}
}
The evaluator is called every time a new data point arrives, with the full rolling state.
- JavaScript
- Python
alert.setEvaluator((rollingState) => {
const temp = rollingState["sensor_01"]?.temperature?.value;
const humidity = rollingState["sensor_01"]?.humidity?.value;
// fire if both conditions are met
return temp > 80 && humidity > 70;
});
def evaluator(rolling_state):
device = rolling_state.get("sensor_01", {})
temp = device.get("temperature", {}).get("value", 0)
humidity = device.get("humidity", {}).get("value", 0)
# fire if both conditions are met
return temp > 80 and humidity > 70
alert.set_evaluator(evaluator)
listen()
Start the evaluator and subscribe to alert state changes.
- 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 specific device's alert was acknowledged |
onAckAll / on_ack_all | All devices' alerts were acknowledged |
onError / on_error | Evaluator threw an error |
- JavaScript
- Python
await alert.listen({
onFire: (data) => console.log("FIRED:", data),
onResolved: (data) => console.log("RESOLVED:", data),
onAck: (data) => console.log("ACK:", data),
onAckAll: (data) => console.log("ACK_ALL:", data),
onError: (err) => console.error("Error:", err),
});
await alert.listen({
"on_fire": lambda data: print("FIRED:", data),
"on_resolved": lambda data: print("RESOLVED:", data),
"on_ack": lambda data: print("ACK:", data),
"on_ack_all": lambda data: print("ACK_ALL:", data),
"on_error": lambda err: print("Error:", err),
})
stop()
Stop the evaluation engine and reset its state.
- JavaScript
- Python
await alert.stop()await alert.stop()updateEphemeral()
Update an ephemeral alert's configuration.
- JavaScript
- Python
await app.alert.updateEphemeral(params)await app.alert.update_ephemeral(params)- JavaScript
- Python
const updated = await app.alert.updateEphemeral({
id: alertId,
config: { duration: 60 },
});
updated = await app.alert.update_ephemeral({
"id": alert_id,
"config": {"duration": 60},
})
Example response:
{
"name": "custom_check",
"type": "EPHEMERAL",
"config": {
"topic": {
"source": "TELEMETRY",
"device_ident": "sensor_01",
"last_token": "cpu_usage"
},
"duration": 60,
"recovery_duration": 15,
"recovery_eval_type": "VALUE",
"cooldown": 60
},
"notification_channel": ["69cb22a4ed4d9fe786145d01"]
}
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()
Query the history of alert state changes.
- JavaScript
- Python
await app.alert.history(params)await app.alert.history(params)| Parameter | Type | Description |
|---|---|---|
name | string | The alert name |
device_idents | string[] | Device identifiers to query |
start | string | Start time (ISO 8601) |
end | string | (optional) End time (ISO 8601). Defaults to now |
- JavaScript
- Python
const history = await app.alert.history({
name: "custom_check",
device_idents: ["sensor_01"],
start: "2026-03-01T00:00:00.000Z",
end: "2026-03-02T00:00:00.000Z",
});
history = await app.alert.history({
"name": "custom_check",
"device_idents": ["sensor_01"],
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-02T00:00:00.000Z",
})
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",
})
ackAll()
Acknowledge an alert across all devices.
- JavaScript
- Python
await app.alert.ackAll(params)await app.alert.ack_all(params)| Parameter | Type | Description |
|---|---|---|
alert_id | string | The alert ID |
acked_by | string | Who is acknowledging |
ack_notes | string | (optional) Notes about the acknowledgement |
- JavaScript
- Python
await app.alert.ackAll({
alert_id: alertId,
acked_by: "operator_jane",
ack_notes: "Maintenance window",
});
await app.alert.ack_all({
"alert_id": alert_id,
"acked_by": "operator_jane",
"ack_notes": "Maintenance window",
})
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)