Skip to content

Alerts

The Alerts page surfaces every operational signal the gateway fires that the operator should look at — blocked incidents, threshold breaches, system events. It lives at /control-center/alerts and is gated by the alerts plan feature (Starter and above). On Lite plans the sidebar link is hidden and direct URLs render an upgrade prompt.

The page reads from the same alerts feed that drives the sidebar bell badge (count next to Alerts), so dismissing or snoozing on the page brings the badge in line immediately.

The header reads Alerts with a running subtitle that breaks down the live state:

12 active · 5 resolved · 3 critical · 6 warning · 2 info

Zero-count tiers collapse out of the subtitle so a clean org shows just 0 active · 0 resolved with no visual noise. The counts cover every severity tier, so the info tier is counted alongside critical and warning — all three severities surface to operators.

The header action is Dismiss all (N) when at least one active alert exists; clicking it opens a confirmation Dialog ("Dismiss N active alerts? This action cannot be undone.") with Cancel and the destructive confirm button.

Alerts page — header with active / resolved / info counts, Dismiss all action, severity and category filters, Set up alerts entry point. Alerts page — header with active / resolved / info counts, Dismiss all action, severity and category filters, Set up alerts entry point.
Alerts · Set up alerts

Metric strip — not a row of four cards

The four tiles above the filter chips are:

Tile Sub-line example
Action sources Add one to begin / registered or observed
Verified 3 unverified — verify now (deep link to ?filter=unverified) / all sources verified
Tool calls Last 30 days across all sources
Drift Tools match the upstream catalog / 4 verification pending — not drift

If the list is still loading, every tile shows — rather than 0, so the operator never confuses "I don't know yet" with "the answer is zero".

Filter chips — two orthogonal dimensions

Two filter dimensions run side by side above the list:

  • Severity — All / Critical / Warning / Info.
  • Category — All / Prevented / System.

Severity is applied client-side (small enum, response shape unchanged); Category is also pushed to the server via useAlerts({ category }) so the wire doesn't even ship the filtered-out rows. The combination of the two narrows the list independently — Critical + Prevented is the typical "what incidents did the breaker actually stop today" view.

Alert row anatomy

Each row is an AlertCard rendered as a hairline-divided block. The components from top to bottom:

  • Severity left-border — critical/warning/info accent.
  • Icon-avatar — incident type (Wallet for budget_block, ShieldAlert for tool_block, Gauge for spend thresholds).
  • Title + body — structured for budget_block rows: "Projected vs budget" stats + a horizontal threshold bar (current spend over threshold_cents, live from the wire).
  • Timestamp + workflow name — when the alert is workflow- scoped; system alerts omit the workflow chip.
  • Snooze dropdown — 1h / 4h / 24h / 3d / 7d. The snoozed row is hidden from the active list until the snooze expires; a "Snoozed until …" footer line plus a live countdown appears on the row while the snooze is active.
  • Dismiss — single click, the row collapses into the Resolved section.

Resolved section

Beneath the active list, a Resolved today section shows dismissed alerts from the current calendar day. Each row renders the same AlertCard with a resolved flag — icon, title, body, but no Snooze / Dismiss actions. The resolved section is collapsed automatically when there are no resolved alerts.

How to wire up alerts

The Set up alerts button in the top-right of the header takes the operator to Notifications (/control-center/notifications) where they can:

  • Add Slack, Email, or Webhook channels.
  • Configure threshold rules (e.g. "spend reaches 80% of cap").
  • Subscribe the org's events to the enabled channels.

The Alerts page is the read surface; Notifications is the configure surface. They share the same wire, so a channel that fires lands both in the page and in the channel that the operator subscribed to.

API hooks

For automations, the same actions are exposed via REST and are mirrored in the audit log:

  • POST /api/orgs/alerts/{id}/snooze — { hours: number } body.
  • POST /api/orgs/alerts/dismiss-all — dismiss every active alert for the org in one call. Use sparingly; the gateway still writes one audit row per dismissal.

The plan-tier gate (alerts feature) is enforced server-side on every handler — a Lite user cannot dismiss alerts by hitting the API directly even if the page itself doesn't render.

  • Notifications — how to add channels and subscribe events.
  • Audit log — every dismiss / snooze is recorded as an audit row.

Deep dive

Severity and category answer different questions, and the two dimensions are orthogonal. Severity is urgency: three tiers exist, critical, warning, and info, with nothing in between, so the colour ladder is the only in-band signal available. Category is ownership: a prevented incident can be critical, and a system event can be purely informational. Spend-threshold alerts follow a fixed ladder where 80% and 95% of the cap are warnings and 100% is critical; key revocation is a warning. System-scope alerts are never bound to a workflow, and that invariant is enforced before the record is written rather than checked at read time.

The header counts and the listed rows are produced by the same query under the same category filter, so a count can never disagree with what is on screen. An alert counts as active when it is not dismissed and either carries no snooze or its snooze has expired; that one predicate drives both the list and every per-severity count. A category filter value that is not recognised is rejected with a 400 that names the supported set instead of being ignored.

Dismissal is idempotent: dismissing an already-dismissed alert changes nothing, and every dismissal writes exactly one audit row. Dismiss-all is organization-wide, and there is no per-workflow bulk path, so an operator cleaning up a single workflow dismisses row by row. A burst of system events of the same kind collapses rather than flooding the feed, so rapid repeated emissions are dropped at the channel layer. The per-row snooze actor is not surfaced on the alerts view; who snoozed and when is recorded in the audit log.