> ## 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.

# Spend alerts, burn rate, and the velocity-anomaly model

> The three spend-protection instruments side by side — tenant-configured threshold alerts, the burn-rate projection, and automatic velocity-anomaly detection — plus the anomaly ledger, the self-expiring mitigation throttle, and the on-call escalation bridge.

# Spend alerts, burn rate, and the velocity-anomaly model

Orbit protects a prepaid wallet with three instruments that complement each
other: **threshold alerts** you configure yourself, a **burn-rate projection**
that forecasts runway, and **velocity-anomaly detection** that flags a sudden
spend surge and can choke it automatically. This page explains what each one
answers, how the anomaly model's math works, what the ledger records, how the
auto-mitigation throttle behaves, and where escalations and incidents fit.

The endpoint surface is the [Billing overview](/billing/overview); request and
response schemas are the [Billing API reference](/api-reference/billing). The
full incident model lives on [on-call and the escalation-policy
model](/concepts/oncall-escalation-model).

## The three instruments, side by side

| Instrument           | Answers                                                                  | Endpoint                            | Action on hit                                                                                         |
| -------------------- | ------------------------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Threshold alert**  | "Did spend or balance cross a line I set?"                               | `GET/POST /api/v1/billing/alerts`   | Email/SMS recipients, and optionally pause outbound (`notify` / `pause_outbound` / `block_outbound`)  |
| **Burn rate**        | "How fast am I draining the wallet, and how many days until it empties?" | `GET /api/v1/billing/burn-rate`     | None — forecast only                                                                                  |
| **Velocity anomaly** | "Is my spend rate surging far above my own recent baseline?"             | `GET /api/v1/billing/spend-anomaly` | Operator alert, graduated auto-throttle or pause of the affected channel, optional on-call escalation |

**Threshold alerts** are the instrument you shape. You pick a threshold type —
`spend_percent` (share of a reference spend), `spend_amount` or
`balance_remaining` (absolute cents), or `daily_spend` (cents per day) — a
threshold value, up to 20 email and/or SMS recipients, and the action that
fires when the line is crossed. A scheduler evaluates alerts on a 10-minute
tick. Prefer `notify`: an operator reviews the hit and decides.
`pause_outbound` stops outbound sending until you reset the alert in the
dashboard. Threshold alerts are tenant-owned controls: you own both the line
and the consequence.

**Burn rate** answers a runway question, not an abuse one. It averages your
daily spend over a lookback window (`?days=`, default 30, up to 90) and
divides the current wallet balance by that average to project
`projected_days_remaining` — `null` when there is no recent spend to
extrapolate from. Credit rows (top-ups) are excluded from the average, so a
recent top-up doesn't read as a burn surge. `GET /api/v1/billing/spend-series`
returns the same window plus a forward projection and anomaly flags. Burn
rate never acts; it informs auto-top-up thresholds and your own capacity
planning.

**Velocity anomaly detection** is the instrument neither of the above covers:
a sudden acceleration of spend. Absolute thresholds only bite once the line is
reached, and burn rate is a smoothed average — neither spots a compromised API
key or a hijacked campaign in its first minutes. The anomaly detector compares
today's velocity against your own trailing baseline per channel and flags a
surge before a hard cap is exhausted.

## Velocity anomaly detection — the model

The detector is pure window math over your debit ledger. For every channel
(`sms`, `whatsapp`, `voice`, `email`, `rcs`, `agents`, `other`) it builds a
pair of numbers, plus a synthetic `all` aggregate that catches a surge spread
thinly across channels:

* **Baseline** — the average daily spend over the trailing **14 complete UTC
  days** (today excluded). Manual operator wallet adjustments
  (`admin_grant` / `admin_deduct` rows and `admin*` references) are excluded
  from both sides, so bookkeeping never looks like usage.
* **Today** — spend so far in the current (incomplete) UTC day, extrapolated
  to a full-day rate: `projected = today_spend / day_fraction_elapsed`. The
  fraction is floored at one hour of the day, so a one-minute burst at 00:03
  does not project to an absurd rate.

A channel is flagged when **both** of these hold:

1. **Absolute floor** — the projected daily rate clears \*\*$250/day**
   (25,000 cents). This gate falls first, and it is what keeps a tenant
   spending $0.10/day from generating a page when \$0.30/day is a 3x surge.
2. **Surge multiple** — the projected rate reaches **10x the baseline** daily
   average. A new tenant with no baseline history at all is treated as a
   cold-start surge the moment the projection clears the floor — there is no
   prior spend to compare against, and an instant \$250/day projection for an
   org that never spent is itself the anomaly.

Each flagged channel reports its baseline, today's spend, the projection,
the surge ratio (`null` on a cold start, where the ratio is undefined), and a
severity. Severity is **critical** when the ratio reaches 25x the baseline
(2.5x the 10x threshold) or the absolute projection reaches \$1,250/day (5x
the floor); otherwise it is **high**. Both the dashboard read and the on-call
bridge below go through the same evaluator, so no two surfaces can disagree
on what counts as a surge.

No configuration is required and none is accepted — the tunables are platform
defaults. Your own controls remain the threshold alerts above; the anomaly
model exists to catch what a fixed line you picked yesterday can't.

## The anomaly ledger

Two reads expose the model:

* `GET /api/v1/billing/spend-anomaly` — the **current** evaluation. Returns
  the detected anomalies, the applied tunables (`surge_multiplier`,
  `min_projected_daily_minor`, `baseline_days`), and `day_fraction_elapsed`
  so you can re-derive any ratio yourself. Each newly detected channel also
  lands in the fraud-review queue for operators — deduplicated to one entry
  per (organization, channel, UTC day), so polling the endpoint can't spam
  the queue.
* `GET /api/v1/billing/spend-anomaly/ledger` — the **historical** ledger of
  everything the detector has recorded for your organization (the dashboard
  poll and the continuous background sweep both write to it). Optional
  `from`/`to` ISO timestamps bound the window; `limit` is bounded to 500
  (default 100), newest first. Every entry carries the channel, surge ratio,
  baseline/today/projected spend in minor units, any auto-mitigation applied,
  and the review's triage status (`open` / `triaged` / `dismissed` /
  `escalated`) with its acknowledgement and resolution timestamps.

Use the current read for "what is on fire right now" and the ledger for
forensics — trend analysis, chargeback attribution, and post-incident review.

## Mitigation — the self-expiring throttle

Detection closes its own loop. When a flagged channel maps onto an enforceable
fraud cap (`sms`, `whatsapp`, `rcs`, `email`, `voice`), Orbit applies a
graduated, reversible clamp on that channel's per-minute send rate:

* **high severity → throttle**: the channel is clamped down to **10 sends or
  calls per minute**. Legitimate low-volume traffic still flows; a
  toll-fraud pump is choked.
* **critical severity → pause**: the clamp is **0 per minute** — every send or
  call on that channel is refused until the mitigation expires or you clear
  it.

Channels without a matching guard cap (`agents`, `other`, the synthetic `all`)
are never auto-mitigated — the operator alert still fires for them.

The clamp has three properties worth modeling:

* **Min-only.** It can only ever *lower* the effective per-minute cap the
  fraud guards enforce. It can never raise your own configured cap, so an
  anomaly can never weaken protection you already set.
* **Self-expiring.** Every mitigation record carries an `expires_at`, capped
  at **6 hours** (and never longer than the rest of the UTC day). An
  automatic throttle must not linger on a false positive: when the record
  expires the guards ignore it and the channel reverts on its own — no revert
  job, no operator action.
* **Tenant-clearable.** You can clear an active mitigation early from the
  fraud-caps surface in the dashboard when you've confirmed the surge is
  legitimate (a real launch, a known campaign).

## Escalation and incidents

`POST /api/v1/billing/spend-anomaly/escalation` bridges the anomaly signal
onto the on-call pipeline. Send the caller's own escalation **policy** in the
request body — the same policy shape the [On-Call API](/api-reference/oncall)
accepts: ordered steps, each with a target (a rotation or a static member
list), the channels to page, and an acknowledge-wait window. The endpoint
re-evaluates your current spend velocity and, when an anomaly is flagged,
returns the incidence plane as pure compute:

* an **incident** snapshot anchored to your policy (`open`,
  `pages_fired: 0`), and
* the flattened **plan** — the ordered page timeline with `total_pages`,
  `next_tick_at`, and whether the policy is `exhausted`.

When nothing is flagged the response is `detected: false` with no incident
plane. Nothing is persisted or sent by this endpoint: your driver (the
dashboard's wire-escalation affordance, or the background sweep) walks the
returned plan through the on-call incident tick/transition endpoints, same as
any other incident. The full rotation, timeline, and incident-state model —
including the high-water mark that makes retries idempotent — is on
[on-call and the escalation-policy model](/concepts/oncall-escalation-model).

## Example flows

**Create a threshold alert:**

```json POST /api/v1/billing/alerts theme={null}
{
  "name": "SMS daily spend guard",
  "threshold_type": "daily_spend",
  "threshold_value": 50000,
  "notify_emails": ["finance@example.com"],
  "action_on_hit": "notify",
  "cooldown_hours": 6
}
```

```json 201 theme={null}
{
  "data": {
    "id": "alert_7f3c9a1b",
    "threshold_type": "daily_spend",
    "threshold_value": 50000,
    "action_on_hit": "notify",
    "enabled": true
  }
}
```

**Read the current anomaly evaluation:**

```json GET /api/v1/billing/spend-anomaly theme={null}
{
  "data": {
    "detected": true,
    "anomalies": [
      {
        "channel": "sms",
        "baseline_daily_minor": 4200,
        "today_spend_minor": 214000,
        "projected_daily_minor": 428000,
        "surge_ratio": 101.9,
        "severity": "critical"
      }
    ],
    "baseline_days": 14,
    "surge_multiplier": 10,
    "min_projected_daily_minor": 25000,
    "day_fraction_elapsed": 0.5
  }
}
```

**Read the historical ledger:**

```json GET /api/v1/billing/spend-anomaly/ledger?limit=50 theme={null}
{
  "data": {
    "entries": [
      {
        "id": "fr_2a91c4e8",
        "category": "spend_velocity_anomaly",
        "severity": "critical",
        "status": "open",
        "channel": "sms",
        "surge_ratio": 101.9,
        "baseline_daily_minor": 4200,
        "today_spend_minor": 214000,
        "projected_daily_minor": 428000,
        "auto_mitigation": {
          "mode": "pause",
          "channel": "sms",
          "max_per_min": 0,
          "ttl_seconds": 21600
        },
        "detected_at": "2026-09-04T11:58:20.000Z",
        "acknowledged_at": null,
        "resolved_at": null
      }
    ],
    "count": 1,
    "window": { "from": null, "to": null }
  }
}
```

All monetary fields are wallet-currency minor units (cents). `cooldown_hours`
on an alert caps how often the same alert re-notifies; the anomaly ledger has
no cooldown — it is the append-only record, while deduplication happens on
the operator-facing queue per (organization, channel, UTC day).

## Next steps

* [Billing overview](/billing/overview) — the endpoint surface map.
* [Wallets, credits, and charges](/concepts/wallets-credits-and-charges) —
  the ledger and the pause-gate semantics these instruments sit on.
* [On-call and the escalation-policy model](/concepts/oncall-escalation-model)
  — rotations, page timelines, and the incident state machine behind the
  escalation bridge.
* [On-Call API reference](/api-reference/oncall) — the incident tick and
  transition endpoints an escalation plan drives.
