Anomaly insights: detector shapes, baselines, and the alert handoff
The anomaly-insights page (Insights → Anomalies) is a time-stamped ledger of everything the platform’s detectors have raised against your traffic — split into a wallet scope (spend channels) and a webhook scope (inbound and delivery-signal hints) — with per-scope totals, open counts, and a day-by-day trend. It is the read model of the insights rollup model: no new table, no write-path change — every anomaly the detectors already record becomes a query. The endpoint surface is three GET routes under/insights:
GET /insights/anomaly-insights/by-scope— per-scope totals (wallet,webhook, and the combined count), open counts, and high/critical counts over the window, with the newest detection timestamp.GET /insights/anomaly-insights/history— the windowed ledger rows themselves: id, category, severity, scope, scrubbed summary, and triage/resolve timestamps.GET /insights/anomaly-insights/timeseries— a zero-filled per-(scope, UTC day) histogram for the trendline, so a flat day reads as zero, not as a gap.
?days= (default 30, capped at 365) or an explicit from/to ISO pair.
History pages at 100 rows; the aggregate and trendline reads are scan-bounded
underneath and surface a truncated marker if a pathological window ever hits
that bound, so a misreport is never silent.
Detector families — what shapes an anomaly
Each ledger entry carries a detector family — the shape of anomaly that fired. One row per family:
The
wallet/webhook split is a classification over the entry’s details, not
two separate detectors: entries whose channel is a wallet spend channel
(sms, whatsapp, email, rcs, voice, agents, or the synthetic all)
land in wallet scope; entries carrying an inbound phone or webhook-delivery
hint land in webhook scope. The filter (?scope=) never restricts what is
scanned — it only reclassifies the rows.
Baseline window — trailing, per family
Every detector builds its baseline over a trailing window defined by its family, and the anomaly-insights view shows you what those comparisons recorded. For the spend-velocity family the baseline is the average daily spend over the trailing 14 complete UTC days, with operator wallet adjustments excluded from both sides so bookkeeping never reads as usage. Other families keep their own windows — the point of the ledger is that each recorded entry tells you which comparison it failed. Cold-start semantics. A tenant with no baseline history at all is treated as a surge the moment an absolute floor clears — for spend velocity, the projected daily rate must also clear a 0.30/day tenant never pages on a “3x” spike; the surge ratio reportsnull instead of a
meaningless multiple. An empty window reads as no data, never as zero — the
same convention the insights ratio surfaces follow.
Threshold → breach → alert
A breach is recorded once, then handed off. The platform’s detector writers record each anomaly through one choke point (the fraud-review writer), then:- Ledger row — the anomaly-insights page you are on: an append-only row
with
openstatus for triage. - Customer alert fan-out — the same detection reaches the tenant whose
traffic tripped it as an
account.fraud.alertwebhook event, an in-app notification (security-critical: owners and admins get it even when they opted out of routine bells), and an email to owners/admins. - Severity tier —
low/mediumread as info/warning;high/criticalread as an error badge and are what the dashboardhigh_or_critical_countstrip counts.
critical when the
surge reaches 25x baseline (2.5x the 10x flag threshold) or $1,250/day absolute
(5x the floor). That family’s graduated auto-mitigation — 10/min clamp at
high, 0/min pause at critical, self-expiring within 6 hours — is owned by
the spend-anomaly model; the
anomaly-insights page records which of those mitigations applied on the
spend-velocity entries.
What a breach carries
Each ledger item answers six questions an operator asking “what fired” needs:- category — which detector family (the table above).
- severity + status — how bad, and whether anyone has triaged or resolved it.
- scope —
walletvswebhook. - summary — a scrubbed one-liner (phone numbers and emails are masked on write, and the read re-scrubs the structured details as defence in depth, so this view never widens into a PII viewer).
- detected/triaged/resolved timestamps — the breach timeline.
- details — the structured context the detector passed (channel, phone prefix, correlation id), never raw PII.
Boundary with spend anomalies
Two pages deliberately do NOT duplicate each other:- Spend alerts, burn rate, and the velocity-anomaly model owns the wallet-burn detection math — thresholds, the burn-rate projection, the graduated auto-mitigation.
- This page owns the traffic/quality anomaly ledger — every detector family’s recorded breaches, across scopes, with triage status.
Two worked examples
A delivery-rate cliff on one destination. Your account sends SMS at a steady pace into a domestic prefix. A campaign starts bursting sends into a premium-rate international prefix. The spend-velocity detector compares today’s projected rate against the 14-day baseline per channel; if it clears the $250/day floor and 10x the baseline, it records aspend_velocity_anomaly at high. If the prefix is on the blocked list, the
destination detectors record irsf_blocked instead. Either way the entry
lands on this page with the channel, the surge ratio, and the prefix hint —
and, on the spend-velocity family, the throttle or pause that mitigated it.
A conversation-mix shift across scopes. A tenant whose traffic was
historically SMS + WhatsApp starts seeing SIM-swap flags on new verify
traffic. Each flag lands as a webhook-scope entry. The by-scope strip shows
the shift: open webhook-scope entries rising while wallet stays quiet, so you
recognise “inbound/verify anomalies” are the new shape instead of reading each
one in isolation.
Tenant-owned response — flag, never gate
An anomaly-insights entry is a flag for your operators, not a platform-mandated gate. The two controls exist on purpose:- Triage status flips between
open,triaged,dismissed, andescalatedon your own operators’ review — the page never presumes the right posture for you. - Auto-mitigation is opt-in to the detector, not to this page — only the spend-velocity family applies one, and it is always self-expiring and tenant-clearable.
See also
- Spend alerts, burn rate, and the velocity-anomaly model — the wallet-burn detection this page points at for spend scope.
- Per-tenant request metrics — the pipeline behind the quality/latency metrics many detectors read.
- Messaging deliverability alerting — the sibling alert engine for delivery-rate/failure-rate/bounce-rate thresholds.
- Insights rollup model — the family design (read-time rollup, no new tables) this page belongs to.