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

# Spam-likelihood prediction: the score, its factors, and the remediation playbook

> How Orbit's predictive 0–100 spam-likelihood score is composed from DLR-derived rates and health-trend direction, what the four label bands mean, and when to run the ordered remediation playbook before a carrier spam label lands.

# Spam-likelihood prediction

A carrier spam label is expensive to remove and cheap to see coming. Once an aggregator's analytics (Hiya, TNS, First Orion) mark one of your outbound numbers "Spam Likely", every party you call sees the tag on their handset, answer rates collapse, and lifting the label is a weeks-long registration and dispute process. Orbit's predictive spam-likelihood endpoint exists so you act in that window — between your own delivery-receipt data degrading and the published label landing. This page explains how the prediction is composed and when to execute the playbook it returns. The endpoint contract itself lives in [Number health, warming, and carrier reputation tracking](/numbers/health#predictive-spam-likelihood-scoring--remediation-playbook).

## Why forward-looking beats reactive handling

Orbit's number surfaces cover three different time horizons:

| Surface                                   | Question it answers                                 | Posture                             |
| ----------------------------------------- | --------------------------------------------------- | ----------------------------------- |
| `GET /api/v1/numbers/:id/health`          | How is this number delivering right now?            | Point in time                       |
| `GET /api/v1/numbers/:id/health/trend`    | Where is that health heading?                       | Trajectory + forecast               |
| `GET /api/v1/numbers/:id/reputation`      | What label do carriers currently display?           | Reactive — the label is already out |
| `GET /api/v1/numbers/:id/spam-likelihood` | How likely is a spam label, and what do I do first? | Predictive                          |

The health endpoints tell you deliverability slipped; the reputation endpoint tells you the label already landed. Neither tells you how close you are to being labelled. The spam-likelihood score fills that gap — read it as an early-warning threshold, not another health graph.

## Signal inputs

The score is a weighted blend of four signals, all derived from your own outbound delivery receipts over the lookback window (`7d`, `14d`, or `30d`, defaulting to 30 days):

* **Failure rate** — failed / sent across the window. This is what carrier filters weight most heavily, so it carries the largest single component (up to 40 points).
* **Complaint-coded failure rate** — rejections that came back with a spam or opt-out error code, also weighted up to 40 points. A 20% complaint rate saturates this component on its own; past that, the plain failure component keeps adding, so extreme cases still move up rather than capping out.
* **Health-trend direction** — a `declining` trajectory from the health-trend kernel adds 15 points; `improving` and `stable` add nothing. The trend input pulls forward the same least-squares fit that powers the forecast, so a slow bleed counts even before today's failure rate looks bad.
* **Anomaly flag** — a detected step-change (the latest day dropping far below its preceding baseline) adds 5 points. This catches the "a spam flag just landed between ticks" signature a smooth slope would average away.

**Low-volume handling.** A score computed over a handful of sends would scare you with noise, so the endpoint refuses to score thin windows: if the window has fewer scored days than the trend kernel needs, or total sends fall under the health kernel's minimum sample size (50 outbound messages), the response returns `spam_likelihood_score: null` with label `unknown`. The thresholds are echoed back as `min_sample_size` so you can render the gate honestly. Waiting for signal is a better answer than a bad answer — do not treat `unknown` as `low`.

## The four label bands

The raw score (0–100, rounded to one decimal) maps onto the bands operators expect from carrier-reputation tooling:

| Label      | Score | What it means                                  |
| ---------- | ----- | ---------------------------------------------- |
| `low`      | \< 25 | Routine traffic; nothing to do                 |
| `moderate` | 25–50 | Worth watching; check the contributing factors |
| `high`     | 50–75 | Act now, pre-label                             |
| `severe`   | 75+   | Rotate the number; the label clock is running  |
| `unknown`  | null  | Insufficient signal in the window              |

The score is strictly monotonic in risk: more failures, more complaint-coded rejections, a declining trajectory, or an anomaly all push it up. Nothing in the blend reads carrier-published labels, so it never double-counts the reputation you already see on the reputation endpoint.

## The contributing-factors breakdown

The response ships an explicit per-component breakdown rather than a bare number, so you can see which lever to pull:

```json theme={null}
"contributing_factors": {
  "failure_rate": 0.18,
  "complaint_rate": 0.11,
  "trend": "declining",
  "anomaly_detected": true,
  "failure_component": 7.2,
  "complaint_component": 40,
  "trend_component": 15,
  "anomaly_component": 5
}
```

The four raw inputs come first, then the four `*_component` values in the same order as the formula. The components sum to the score, so when a number reads `severe` you can tell whether the driver is plain delivery failures (routing or content quality), complaint-coded rejections (registration or opt-out hygiene), a degrading trend, or a fresh anomaly — and the playbook step you take differs for each.

## The ordered remediation playbook

Alongside the score, the response returns a numbered playbook — the manual front-half of the closed-loop remediation run. Automated quarantine and replacement only run safely after a human has acknowledged these steps, and the playbook is structured so a dashboard can switch on each step's `action` identifier:

1. **Pause outbound** (`pause_outbound`) — always step one, whatever the score. Stop adding new volume on the number while the signals settle.
2. **Verify registration** (`verify_registration`) — re-check that the sender's registered campaign (10DLC brand/campaign, toll-free verification, or the corridor's free-entry form) is current, and close any gaps. Registration problems are the most actionable single fix; an unregistered or lapsed campaign is the carrier's most common filter trigger.
3. **Reduce volume** (`reduce_volume`) — included once the score reaches `moderate` (25+) or worse. Drop outbound volume until failure and complaint rates normalise, then re-ramp gradually.
4. **Warm a replacement** (`warm_replacement`) — also gated to `moderate`-or-worse. Warm a replacement number in a warmed sender pool so inbound routes, dialer campaigns, and sender pools can migrate without a hard cutover.

When the score is still `low`, steps 3–4 collapse into a single **monitor** step: check the number daily while the trend stays within normal bounds. The playbook is a pure, ordered list of instructions — executing it is your action; the endpoint changes nothing by itself.

## How prediction composes with the health-score kernel

Spam likelihood is not a separate data pipeline. The endpoint buckets the same tenant `messages` rows the point-in-time health score reads — outbound traffic from the specific number — into daily aggregates, then runs the health-trend kernel over those buckets to get direction and anomaly before scoring. Two consequences follow:

* **Same window semantics.** `failure_rate` and `complaint_rate` in the breakdown mean exactly what they mean on the health page; a `high` spam-likelihood score on a number whose health tier is `excellent` usually means the trend/anomaly input is carrying the risk — check `trend.direction` before assuming the blend disagrees.
* **Same sample-honesty gates.** The `min_sample_size` and insufficient-data behaviour are mirrored from the health kernel outright, so a number that cannot be health-scored cannot be spam-scored either.

Because the whole response is a pure read over locally-stored receipts with no carrier call, calling it is free of provider-side effects and safe to poll from a monitoring loop.

## When to act

Execute the playbook **before** the label, on this schedule:

* `high` or `severe` → run the playbook now. Step 1 (pause) applies while you work steps 2–4.
* `moderate` → run steps 1–2 (pause, verify registration) and put the number on daily watch.
* `low` → the monitor step only; re-check on your normal cadence.
* `unknown` → wait for signal; a null score is a gate, not a green light.

Tie the playbook to your other guardrails: when it returns `reduce_volume`, lower the campaign's pacing cap and tighten quiet-hours windows as described in [send gating and quiet hours](/concepts/send-gating-and-quiet-hours); when it returns `warm_replacement`, fold the number into the [closed-loop reputation remediation](/numbers/health#closed-loop-reputation-remediation) policy (quarantine → replace → migrate → retire) early instead of after a support ticket. For the fleet-wide view of numbers already flagged, poll the reputation-alerts endpoint; the spam-likelihood score is the per-number drill-down that should surface risk before it ever reaches that list.

## See also

* [Number health, warming, and carrier reputation tracking](/numbers/health) — the endpoint surface, including the spam-likelihood contract
* [Sender warming and reputation](/concepts/sender-warming-and-reputation) — the ramp and the trust-vs-reputation distinction the health score feeds
* [Sent, delivered, and the three successes](/concepts/submitted-vs-delivered-three-successes) — why DLR-derived rates are the signal carriers trust
* [Send gating and quiet hours](/concepts/send-gating-and-quiet-hours) — the volume controls behind `reduce_volume`
