Metric Alerts
Create threshold-based alerts on interaction metrics with state tracking and recovery.
Metric Alerts monitor Interaction Metrics and fire when a metric's value crosses a threshold. They track state (OK vs FIRING) per dimension and only create interactions on state transitions — preventing duplicate alerts.
Overview
Interaction Metric (e.g. "Failed Logins (5m)")
↓
Metric Alert (e.g. "Failed Logins > 100")
↓ evaluate
Calculate metric value
↓
Compare against threshold
↓
State changed?
├── OK → FIRING: Create interaction with alertActionId
├── FIRING → OK: Create interaction with recoveryActionId
├── OK → OK: No interaction (skip)
└── FIRING → FIRING: No interaction (skip)Creating a metric alert
POST /api/interaction-alerts
{
"title": "High Failed Logins",
"description": "Alert when failed logins exceed 100 in 5 minutes",
"metricId": "failed-logins-metric-uuid",
"operator": "gt",
"threshold": 100,
"alertActionId": "fired-action-uuid",
"recoveryActionId": "recovered-action-uuid",
"resourceTypeId": "alert-sec-uuid",
"enabled": true
}Fields
| Field | Required | Description |
|---|---|---|
title | Yes | Human-readable alert name |
description | No | What this alert monitors |
metricId | Yes | The interaction metric to monitor |
operator | Yes | Comparison operator |
threshold | Yes | Numeric threshold value |
alertActionId | Yes | Action to trigger when alert fires |
recoveryActionId | No | Action to trigger when alert recovers |
resourceTypeId | No | Resource type for alert interactions (defaults to system ALERT type) |
enabled | No | Toggle the alert on/off (default: true) |
Operators
| Operator | Label | Meaning |
|---|---|---|
gt | > | Greater than |
gte | ≥ | Greater than or equal |
lt | < | Less than |
lte | ≤ | Less than or equal |
eq | = | Equal to |
neq | ≠ | Not equal to |
Alert and recovery actions
Unlike subscriber alerts which use system defaults, metric alerts require you to configure explicit actions. This gives you full control over what resource type and action appear in the alert interaction — and therefore which subscriptions get triggered.
Alert Action (alertActionId) — the action used when the metric crosses the threshold (OK → FIRING). This is required.
Recovery Action (recoveryActionId) — the action used when the metric drops back below the threshold (FIRING → OK). This is optional. If not set, no interaction is created on recovery.
Example action setup
For a pizza-place tenant:
| Action | Mnemonic | Purpose |
|---|---|---|
| Fired | fired | Used as alertActionId — threshold exceeded |
| Recovered | recovered | Used as recoveryActionId — back to normal |
# Create alert with both actions
POST /api/interaction-alerts
{
"title": "High Cart Abandonment",
"metricId": "cart-abandonment-metric-uuid",
"operator": "gt",
"threshold": 50,
"alertActionId": "fired-action-uuid",
"recoveryActionId": "recovered-action-uuid",
"resourceTypeId": "alert-biz-uuid"
}State tracking
Metric alerts track state per dimension combination. This prevents duplicate interactions when an alert is already firing.
State transitions
| Previous State | Current Value vs Threshold | New State | Interaction Created? |
|---|---|---|---|
| OK | Exceeded | FIRING | Yes — with alertActionId |
| FIRING | Exceeded | FIRING | No — already firing |
| FIRING | Not exceeded | OK | Yes — with recoveryActionId (if configured) |
| OK | Not exceeded | OK | No — already OK |
Multi-dimensional state
For metrics with groupBy dimensions, state is tracked independently per dimension combination.
Example: A "Failed Logins by Country" metric with threshold > 100:
Metric results:
ZA: 150 → State: FIRING (150 > 100)
US: 50 → State: OK (50 < 100)
UK: 120 → State: FIRING (120 > 100)Each dimension has its own state record in interaction_alert_states. ZA firing doesn't affect US or UK.
State table
The interaction_alert_states table tracks:
| Column | Description |
|---|---|
alertId | Which alert |
dimensionKey | MD5 hash of dimension values (for uniqueness) |
dimensionValues | Actual dimension values as JSON |
isFiring | Current state: true = firing, false = OK |
currentValue | Most recent metric value |
lastTriggeredAt | When the alert last transitioned to FIRING |
lastRecoveredAt | When the alert last transitioned to OK |
triggerCount | Total number of times this alert has fired |
Alert interaction data
When a metric alert fires or recovers, it creates an interaction with this structure. See System Interactions for the complete field reference and examples of subscribing to alert events.
{
"alertId": "uuid",
"alertTitle": "High Failed Logins",
"metricId": "uuid",
"metricName": "Failed Logins (5m)",
"metricType": "COUNT",
"threshold": 100,
"operator": "gt",
"status": "exceeded",
"value": 150,
"dimensions": { "interaction_country": "ZA" },
"evaluatedAt": "2026-03-20T14:30:00.000Z"
}For recovery interactions, status is "recovered" instead of "exceeded".
The interaction is created with:
| Field | Value |
|---|---|
resourceId | The alert's alertId |
resourceTypeId | Alert's configured resource type, or system ALERT type |
actionId | alertActionId (firing) or recoveryActionId (recovery) |
originId | System METRIC_EVAL origin |
externalReference | alert-{alertId}-firing-{timestamp} or alert-{alertId}-recovery-{timestamp} |
System entities required
Metric alerts require these system entities:
| Entity | Mnemonic | Purpose |
|---|---|---|
| Resource Type | ALERT | Default resource type (if not overridden per alert) |
| Origin | METRIC_EVAL | Origin for all metric alert interactions |
The alert and recovery actions are user-configured per alert — there are no system defaults.
Evaluating alerts
Manual evaluation
POST /api/interaction-alerts/evaluateReturns:
{
"evaluated": 3,
"triggered": 1,
"recovered": 1,
"errors": 0,
"details": [
{
"alertId": "uuid",
"alertTitle": "High Failed Logins",
"metricName": "Failed Logins (5m)",
"metricValue": 150,
"operator": "gt",
"operatorLabel": ">",
"threshold": 100,
"triggered": true,
"stateChanged": true,
"previousState": "ok",
"newState": "firing",
"dimensions": "interaction_country: ZA"
},
{
"alertId": "uuid",
"alertTitle": "High Failed Logins",
"metricName": "Failed Logins (5m)",
"metricValue": 50,
"operator": "gt",
"operatorLabel": ">",
"threshold": 100,
"triggered": false,
"stateChanged": false,
"previousState": "ok",
"newState": "ok",
"dimensions": "interaction_country: US"
}
]
}Cron job
# Evaluate every minute
* * * * * curl -X POST https://your-api.com/api/interaction-alerts/evaluate \
-H "x-api-key: your-key"View current states
GET /api/interaction-alerts/statesReturns all alert states with their current firing status:
[
{
"alertId": "uuid",
"dimensionKey": "abc123",
"dimensionValues": { "interaction_country": "ZA" },
"isFiring": true,
"currentValue": 150,
"lastTriggeredAt": "2026-03-20T14:30:00.000Z",
"lastRecoveredAt": null,
"alertTitle": "High Failed Logins",
"threshold": 100,
"operator": "gt",
"metricName": "Failed Logins (5m)"
}
]Condition-based alerts
Metric alerts also support a conditions array as an alternative to metricId + threshold. This is for simpler rule-based alerts that don't need a pre-defined metric.
POST /api/interaction-alerts
{
"title": "Large Order Alert",
"conditions": [
{ "field": "interaction.total", "operator": "gt", "value": 10000 }
],
"alertActionId": "fired-action-uuid",
"resourceTypeId": "alert-biz-uuid"
}Either metricId or conditions must be provided — not both.
API reference
| Method | Endpoint | Description |
|---|---|---|
GET | /api/interaction-alerts | List all metric alerts |
GET | /api/interaction-alerts/:id | Get alert by ID |
POST | /api/interaction-alerts | Create alert |
PUT | /api/interaction-alerts/:id | Update alert |
DELETE | /api/interaction-alerts/:id | Delete alert |
GET | /api/interaction-alerts/states | Get current alert states |
POST | /api/interaction-alerts/evaluate | Evaluate all enabled alerts |