Documentation

Alerts

AuditTrail's anomaly alert engine lets you define threshold rules over your agent telemetry and fire notifications when an observed metric crosses the threshold. Rules are tenant-scoped, evaluated on a background loop, and fan out through your configured webhooks.

How it works

Each alert rule names a built-in metric, a rolling window, a threshold, and a comparator. On its check_interval, the engine computes the metric over the last window_minutes of the caller's data and compares it to the threshold. When the comparison is true, an alert_events row is written and the event is delivered to your webhook destinations. Every rule and event is scoped by user_id, so cross-tenant access is impossible.

Rule fields

FieldTypeNotes
namestring1–200 chars
metricstringMust be a supported metric (see GET /alerts/metrics)
window_minutesint1–1440, default 60 — the lookback window
check_interval_minutesint1–60, default 5 — how often the engine evaluates
thresholdfloatThe trip value
comparatorenumgte (default) / gt / lte / lt / eq
severityenuminfo / amber (default) / red
enabledboolDefault true

The supported metric identifiers are returned by GET /api/v1/alerts/metrics (the dashboard populates its dropdown from this), so create a rule only against a metric the engine actually computes — an unknown metric returns 400.

REST surface

routes/alerts.py — prefix /api/v1/alerts. All routes require a session cookie or an sk-at-… key (require_user_or_apikey).

MethodPathPurpose
GET/alerts/rulesList the caller's rules (newest first)
POST/alerts/rulesCreate a rule (validates the metric)
PATCH/alerts/rules/{rule_id}Update any rule field
DELETE/alerts/rules/{rule_id}Delete a rule (events cascade-delete)
GET/alerts/eventsRecent fired events (limit, 1–500, default 50)
POST/alerts/test/{rule_id}Dry-run — compute the metric NOW without firing
GET/alerts/metricsSupported metric identifiers

Create a rule

json
POST /api/v1/alerts/rules
{
  "name": "Cost spike",
  "metric": "total_cost",
  "window_minutes": 30,
  "check_interval_minutes": 5,
  "threshold": 5.0,
  "comparator": "gte",
  "severity": "red",
  "enabled": true
}

Test before enabling

POST /api/v1/alerts/test/{rule_id} computes the metric over the rule's window right now and tells you whether it would fire — without writing an event. Use it to sanity-check a threshold before turning the rule on:

json
{
  "rule_id": "…",
  "metric": "total_cost",
  "observed_value": 3.21,
  "threshold": 5.0,
  "comparator": "gte",
  "would_fire": false,
  "context": { "...": "metric-specific detail" }
}

Events & delivery

Fired events are persisted to alert_events with the rule id, fired_at, the metric + observed value + threshold, severity, a metric-specific context blob, and a delivered flag. Delivery reuses the same outbound webhook machinery as constitutional violations (fan_out_violation), so an alert lands in Slack / PagerDuty / a signed HTTPS endpoint exactly like a governance violation. See the webhooks section of the API reference.

Data model

TableHolds
alert_rulesRule definition (metric, window, threshold, comparator, severity, enabled)
alert_eventsOne row per fired event; FK to the rule with ondelete=CASCADE