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

# Deliverability Lab: lint filtering risk and predict per-carrier delivery before you send

> Score a campaign body for carrier-filtering risk (spam-trigger vocabulary plus URL/link heuristics) and project per-carrier delivery rates from your own send history — one read-only pre-flight report, distinct from the regulatory policy scanner.

# Deliverability Lab

Carrier filtering and regulatory compliance are two different problems. The [policy scanner](/compliance/policy-scanner) gates on legal rules — TCPA, SHAFT-content, GDPR, India DLT — and it can pass a campaign that carriers still silently filter for looking like spam. The Deliverability Lab fills that gap: it scores the draft campaign body for **filtering risk** and projects **delivery rates per destination carrier** from your own history, so "was this filtered?" is a measurement you take before launch, not an inference you make after the fact.

The lab runs on `POST /api/v1/campaigns/deliverability-lab`. It is read-only: no campaign row is required, nothing sends, no provider is called, and the wallet is untouched. Pass the draft body in the request; re-score on every edit while you compose.

## 1. The two signals in one report

Each call returns both signals plus a warning roll-up the composer renders directly.

**Signal one — content filtering-risk lint (`content_risk`).** The body is scanned twice:

* **Spam-trigger vocabulary.** The shared spam-keyword classifier (`@devotel/compliance`'s scanning package — the same engine behind the composer's live underlines) matches promotional, urgency, and financial vocabulary against the channel's filtering profile. Email campaigns pass the subject line too.
* **URL and link heuristics.** Layers on top of the keyword scan, because carriers also filter on link shape: public URL shorteners (bit.ly, t.co, goo.gl, and friends), bare IP-address links, plain `http://` links, and link-stuffing (three or more links in one message).

The two are blended into a 0–100 `score`, a bucketed `risk_level` (low / medium / high / critical), and a `verdict` of `pass`, `warn`, or `block`. Each finding carries a severity, the matched fragments, and a remediation suggestion.

**Signal two — predicted per-carrier delivery (`predicted_delivery`).** The controller aggregates your tenant's own terminal-status sends per destination operator (per MCCMNC for SMS/MMS, joined to the shared operator catalogue; a single account-level bucket for other channels) over a 7-, 30-, or 90-day window. Each carrier's observed delivery rate is discounted by a content-risk penalty that scales with the lint score, up to 35 percentage points at a content-risk score of 100. Rates are sample-weighted, so a low-volume carrier cannot swing the headline number; samples under 30 sends are flagged `low` confidence. When you have no history yet, the lab says so instead of inventing a number.

**The roll-up.** `blocking_warnings` lists anything you should fix before launch (a `block` verdict, or a predicted overall delivery rate at or below 50%); `advisories` lists non-blocking nudges (a `warn` verdict, a predicted rate at or below 75%, or carriers projected under the block floor with medium/high confidence). `ready_to_send` is true when the blocking list is empty. `campaigns:read` scope is sufficient.

## 2. Not the policy scanner

The regulatory [policy scanner](/compliance/policy-scanner) answers "is this send legal?" — TCPA consent, SHAFT-content categories, GDPR lawful basis, India DLT template rules. The Deliverability Lab answers "will carriers let this through?" — vocabulary and link shape vs. your observed per-carrier history. A pre-flight gate firing in the composer can be either one; the report tells you which lens flagged it, so a clean legal posture doesn't settle the filtering question and a filtering flag isn't a legal verdict.

And the lab's counterpart for measured (not predicted) truth is the seed lab: register your own test handsets on `POST /api/v1/campaigns/deliverability-seed-lab` and score actual terminal deliveries to them. Predict from history with this endpoint; measure with the seed roster; gate legality with the policy scanner.

## 3. Where it lives

The campaigns create wizard's Review step runs the lab as part of pre-flight, and the composer re-scores on edit — the same shape as the [dry-run](/guides/campaign-end-to-end) (which checks audience, cost, and quiet hours) and audience preview, but scoped to content and carrier risk. Every read in the flow below accepts `campaigns:read`; only launch needs `campaigns:write`.

## 4. Call it from the API

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/deliverability-lab" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "body": "Final hours! Claim your FREE 50% OFF at http://bit.ly/xY9z2k before it expires!",
    "window": "30d"
  }'
```

| Field     | Notes                                                                                                                                                                                                                |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel` | `sms`, `mms`, `whatsapp`, `email`, `rcs`, `viber`, `telegram`, `line`, `messenger`, `instagram`, `push`, `voice`, `fax`. Push, voice, and fax have no content-filtering layer, so only the URL heuristics run there. |
| `body`    | The draft message body, up to 100,000 chars. Defaults to empty.                                                                                                                                                      |
| `subject` | Email-only subject line, included in the keyword scan.                                                                                                                                                               |
| `window`  | `7d`, `30d`, or `90d` lookback for the carrier history baseline. Defaults to `30d`.                                                                                                                                  |

## 5. Example: before and after a shortened link

Running the body above returns a blocking result driven by the shared shortener and the urgency/discount vocabulary:

```json theme={null}
{
  "content_risk": {
    "applicable": true,
    "score": 78,
    "risk_level": "high",
    "verdict": "block",
    "spam_score": 42,
    "url_analysis": {
      "url_count": 1,
      "shortener_hosts": ["bit.ly"],
      "has_raw_ip_url": false,
      "has_insecure_url": true
    },
    "findings": [
      {
        "id": "spam_keywords",
        "label": "Spam-trigger vocabulary",
        "severity": "warn",
        "matched": ["free", "expires"],
        "suggestion": "Rework high-risk phrasing — promotional / urgency vocabulary is the main carrier-filter trigger.",
        "weight": 42
      },
      {
        "id": "url_shortener",
        "label": "Public URL shortener",
        "severity": "block",
        "matched": ["bit.ly"],
        "suggestion": "Replace shared shorteners with a branded/dedicated link domain — shared shorteners are heavily filtered by carriers.",
        "weight": 30
      }
    ]
  },
  "predicted_delivery": {
    "has_history": true,
    "overall_predicted_rate": 61.4,
    "overall_historical_rate": 82.7,
    "total_sample": 4580,
    "carriers": [
      {
        "mccmnc": "310260",
        "carrier_name": "T-Mobile US",
        "country": "US",
        "terminal": 3900,
        "delivered": 3310,
        "historical_delivery_rate": 84.87,
        "content_penalty_points": 27.3,
        "predicted_delivery_rate": 57.57,
        "confidence": "high"
      }
    ]
  },
  "blocking_warnings": [
    "Message content scored 78/100 for carrier-filtering risk — it is likely to be filtered or blocked. Revise the flagged content before sending."
  ],
  "advisories": [
    "Predicted delivery rate for this content is 61.4% — below the 75% healthy threshold. Carrier history + content risk both contribute."
  ],
  "ready_to_send": false,
  "channel": "sms",
  "window": "30d",
  "evaluated_at": "2026-09-06T12:00:00.000Z"
}
```

Fix the content where the penalties come from. Replace the shared shortener with a branded link domain (see [short links and click tracking](/guides/short-links-and-click-tracking)), move to `https://`, and pull the countdown vocabulary:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/deliverability-lab" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "body": "Your member discount is ready: 50% off at https://shop.acme.example/sale. Reply STOP to opt out.",
    "window": "30d"
  }'
```

The lint score drops to the single digits, the verdict flips to `pass`, `blocking_warnings` empties, `ready_to_send` returns true, and the predicted per-carrier rates climb back toward their historical baselines. That round-trip — flag, fix, re-score — is the operating loop the lab is built for.

Penalty cheatsheet:

| Finding                  | Severity     | Points           |
| ------------------------ | ------------ | ---------------- |
| Public URL shortener     | block        | +30              |
| Bare IP-address link     | block        | +25              |
| Link-stuffing (3+ links) | warn         | +10              |
| `http://` link           | warn         | +8               |
| Spam-trigger vocabulary  | warn / block | classifier score |

## 6. Related pre-flight surfaces

* [Send a campaign end-to-end](/guides/campaign-end-to-end) — the full lifecycle including the dry-run report.
* [Send-gating model](/concepts/send-gating-and-quiet-hours) and the [pre-flight checklist](/guides/send-gates-preflight-checklist) — the admission chain every send walks.
* [Policy scanner](/compliance/policy-scanner) — the regulatory gate this page deliberately does not replace.
* [Contact deliverability health](/guides/contact-deliverability-health) — per-recipient address hygiene, a different axis from campaign-content risk.
