> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Anomaly insights: detector shapes, baselines, and the alert handoff

> The Insights → Anomalies ledger over the platform's fraud-review records — the detector families behind each entry (spend velocity, destination, SIM-swap, voice deepfake, API-key geo), baseline and cold-start semantics, the breach-to-alert handoff, and why anomaly insights flag but never gate.

# 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](/concepts/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.

All three resolve their window the same way the other insights surfaces do:
`?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:

| Family                                                                             | What it measures                                                                                                         | Typical scope                                |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
| **Spend velocity** (`spend_velocity_anomaly`)                                      | Today's spend rate extrapolated against your own trailing baseline, per wallet channel — a surge, not an absolute level. | `wallet`                                     |
| **Suspicious destination / campaign** (`irsf_blocked`, `suspicious_campaign_send`) | A burst of sends into a premium-rate prefix, one destination, or an implausibly wide country spray.                      | `wallet`                                     |
| **SIM-swap risk** (`sim_swap_detected`)                                            | A verify-eligible recipient whose line changed hands recently.                                                           | `webhook`                                    |
| **Voice deepfake score** (`voice_deepfake_high_score`)                             | An inbound or outbound call whose audio scored above the synthetic-media floor.                                          | `wallet`-classified via channel, often voice |
| **API-key geo anomaly** (`api_key_geo_anomaly`)                                    | An API key used from an impossible-travel-origin for its history.                                                        | scope by context                             |

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 $250/day floor so a $0.30/day tenant
never pages on a "3x" spike; the surge ratio reports `null` 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:

1. **Ledger row** — the anomaly-insights page you are on: an append-only row
   with `open` status for triage.
2. **Customer alert fan-out** — the same detection reaches the tenant whose
   traffic tripped it as an `account.fraud.alert` webhook 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.
3. **Severity tier** — `low` / `medium` read as info/warning; `high` /
   `critical` read as an error badge and are what the dashboard `high_or_critical_count`
   strip counts.

For the spend-velocity family the severity escalates to `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](/concepts/spend-anomaly-and-alert-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** — `wallet` vs `webhook`.
* **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.

The suggested next step is encoded in the category + details: a spend-velocity
breach points at the channel's send rate; a destination breach points at the
prefix; a SIM-swap or deepfake breach points at the specific recipient or call
in the correlation id.

## Boundary with spend anomalies

Two pages deliberately do NOT duplicate each other:

* [Spend alerts, burn rate, and the velocity-anomaly
  model](/concepts/spend-anomaly-and-alert-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.

The spend page explains the math; this page is where the breach lands.

## 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 a
`spend_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`, and
  `escalated` on 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.

Wallets, conversations, and sends keep their own controls; the ledger never
drains a wallet, blocks a destination, or pauses a channel itself. That
separation is what makes the ledger safe to keep: it records what the
detectors judged, and it never decides.

## See also

* [Spend alerts, burn rate, and the velocity-anomaly
  model](/concepts/spend-anomaly-and-alert-model) — the wallet-burn detection
  this page points at for spend scope.
* [Per-tenant request
  metrics](/concepts/request-metrics-and-analytics-pipeline) — the pipeline
  behind the quality/latency metrics many detectors read.
* [Messaging deliverability
  alerting](/concepts/messaging-deliverability-alerting) — the sibling alert
  engine for delivery-rate/failure-rate/bounce-rate thresholds.
* [Insights rollup model](/concepts/insights-rollup-model) — the family design
  (read-time rollup, no new tables) this page belongs to.
