> ## 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: pre-send content-risk lint and per-carrier delivery prediction

> Where the Deliverability Lab runs in the campaign launch path, the two signals it combines — a channel-aware content filtering-risk lint and a per-carrier delivery projection built from your own DLR history — which warnings block versus advise, and what the lab cannot predict.

# Deliverability Lab: pre-send scoring

Carrier filtering and legal compliance are different problems, and only one of them is covered by the regulatory scanner. The [policy scanner](/compliance/policy-scanner) answers "is this send legal" — TCPA consent, SHAFT content, GDPR basis, India DLT templates. The **Deliverability Lab** answers the other question: "will carriers let this content through, and how will it land on the specific destination carriers my audience uses?" It is a read-only pre-send scoring surface that runs two independent signals — a **content filtering-risk lint** and a **per-carrier delivery prediction from your own history** — and rolls them into a blocking/advisory verdict before you commit a single message.

## Where the lab runs — before the campaign exists

The lab sits **server-side, before persistence and before the pre-flight gate chain**. You pass the draft body and channel in the request body; no campaign row is required to exist. That placement matters in two directions:

* **Pre-persist (compose time).** The composer re-scores on every edit, before the campaign is saved. Reworking the copy at this stage is free — nothing is scheduled, nothing is charged.
* **Pre-launch (review time).** The create wizard's Review step re-runs the lab as part of launch verification, before the pre-flight gates ([dry-run](/guides/campaign-end-to-end), audience preview, [policy scanner](/compliance/policy-scanner)) and before any of the runtime gates in the [send-gating chain](/concepts/send-gating-and-quiet-hours) admit a message.

The lab never gates a gate: it scores content, it does not route a send. It makes **no database writes, no provider calls, and no wallet touches** — launching stays under your control.

The endpoint is `POST /api/v1/campaigns/deliverability-lab`, accepting `{ channel, body, subject?, window? }` (`campaigns:read` scope is sufficient) and returning `content_risk`, `predicted_delivery`, `blocking_warnings`, `advisories`, and a `ready_to_send` convenience flag.

## 1. Inputs

The call takes — and only takes — the draft itself and its shape:

* **`channel`** — one of the campaign channels (`sms`, `mms`, `whatsapp`, `email`, `rcs`, `viber`, `telegram`, `line`, `messenger`, `instagram`, `push`, `voice`, `fax`). The channel decides which filtering model applies (see below) and whether a per-carrier projection is meaningful.
* **`body`** — the draft message body (the campaign's message template, up to 100,000 characters — the campaign body ceiling, so a full HTML email scores on the same Review screen it launches from).
* **`subject`** — email subject line, scored alongside the body when set.
* **`window`** — the historical lookback for the carrier delivery baseline: `7d`, `30d` (default), or `90d`.

What the lab joins against your account: for SMS and MMS, your account's past outbound sends bucketed per destination operator by MCCMNC code, labeled from the shared operator catalogue; for every other channel, a single account-level delivery baseline. The destination carrier list is **inferred from your own send history**, not taken as input.

## 2. Content filtering-risk lint

The lint evaluates only one axis: **will carrier and inbox filters treat this as spam-shaped content.** It deliberately does not score sender reputation, regulatory compliance, or recipient consent — those live elsewhere ([sender warming and reputation](/concepts/sender-warming-and-reputation) covers the reputation axis). Scoring is channel-aware:

* **Keyword spam scan.** The same spam-keyword classifier behind the composer's live underlines matches promotional, urgency, and financial vocabulary against the filtering profile of the mapped channel. MMS, viber, telegram, line, messenger, and instagram borrow the SMS heuristic (they either share the SMS carrier-filter profile or behave like SMS marketing copy for filtering purposes); RCS, WhatsApp, and email have their own profiles; email also scores the subject line. Push, voice, and fax have no meaningful carrier content-filtering layer, so no keyword model is consulted for them.
* **URL and link heuristics.** Layered on top, because carriers filter on link shape separately from vocabulary: public shared URL shorteners (bit.ly, t.co, goo.gl, tinyurl.com, and similar shared redirectors — each one is named with its fix), bare IP-address links (`http://203.0.113.5/...`), insecure `http://` links, and link-stuffing (three or more distinct links in one message). Shared shorteners hide the destination from carrier link-reputation engines and are the single strongest SMS filtering trigger; the lab distinguishes them from branded/dedicated link domains, which are fine.

The two signals blend into a 0–100 `score`, a bucketed `risk_level` (`low` / `medium` / `high` / `critical`), and a `verdict`. Every finding — keyword `spam_keywords` or a URL heuristic (`url_shortener`, `url_raw_ip`, `url_insecure`, `url_stuffing`) — carries a severity (`warn` or `block`), the matched fragments, a remediation suggestion, and the weight it contributed to the score. URL heuristics are evaluated on every channel, because a raw-IP link is suspect even where no keyword model applies.

For push, voice, and fax the lint reports `applicable: false` — URL heuristics still run and contribute to the score, but no keyword spam model exists for those channels, so the score is the URL-heuristic weight alone.

## 3. Per-carrier delivery prediction

The second signal answers: "given *this* content's risk score, how would it land on the carriers my audience actually uses?" It is built from **your account's own delivery history**, not platform-wide statistics:

1. Aggregate your outbound sends for the channel over the window(s) you chose, per destination operator for SMS/MMS (bucketed by MCCMNC, labeled and country-tagged from the operator catalogue) or a single account-level bucket for other channels.
2. Compute each carrier's historical delivery rate on a **terminal-status denominator**: delivered-equivalent statuses over terminal statuses. This frames the rate against sends that actually concluded, so a pile of still-in-flight messages cannot flatten the denominator and flatter the rate.
3. Apply a **content-risk penalty** that scales linearly with the lint score: at most 35 percentage points off the historical baseline at a content score of 100, clamped to the 0–100 band. A clean carrier is never predicted to zero on content alone — the carrier's structural baseline still dominates; filtered content suppresses delivery meaningfully but proportionally.

**Honesty about sample size is built in.** Carriers below 30 terminal sends are flagged `low` confidence; the overall headline rate is **sample-weighted**, so a tiny-volume carrier cannot swing the projection. Confidence turns `high` at 500 sends. If your account has no terminal outbound history for the channel at all, `has_history` comes back false and `overall_predicted_rate` is null — the lab says "no data yet" rather than invent a number.

## 4. Blocking vs advisory warnings

The lab separates **blocking** findings (a launch you should reconsider) from **advisories** (information worth having, safe to launch over). The composer surfaces both; `ready_to_send` is true when the blocking list is empty, and the verdict remains yours — the lab is advisory at launch, not a hard stop.

| Signal                 | When it blocks                                                                                                                                                                                                                        | When it advises                                                                                                                                 |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Content risk**       | Any single `block`-severity finding (a shared shortener, a bare-IP link, or spam vocabulary that crosses the block verdict), **or** a blended score at 80+ (`critical`) — raises *"content scored N/100, revise the flagged content"* | Score between 60 and 79 (`high` → `warn` verdict), or a warn-severity finding (`http://` link, link-stuffing) — *"review the flagged phrasing"* |
| **Predicted delivery** | Sample-weighted overall predicted rate at or below **50%** — *"well below a healthy send, improve content or audience"*                                                                                                               | Predicted rate between 50% and **75%**; or an individually high-confidence carrier projected below 50%, named explicitly                        |
| **Empty history**      | Never blocks                                                                                                                                                                                                                          | Advises that the projection sharpens as your account sends more traffic                                                                         |

Warnings of either class do not stop the API response, and only the blocking set flips `ready_to_send` to false — a launch gate reads the blocking list and ignore advisories without enumerating every warning text.

## 5. When it runs, and how it differs from the send-gating chain

Three moments in a campaign's life, three different lenses:

| Surface                                                                                                        | Question it answers                                                                  | When in the flow                                                                         |
| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| **Deliverability Lab (this page)**                                                                             | Will carriers filter this *content*, and how will it land on my audience's carriers? | Pre-persist compose, pre-launch review — before any send exists                          |
| **Pre-flight gates** ([dry-run, audience preview, policy scanner](/guides/campaign-end-to-end))                | Is this launch legal, costed, and audience-ready?                                    | Same review moment — adjacent lenses; the lab report names which lens flagged each issue |
| **Send-gating chain** ([quiet hours, wallet, opt-outs, frequency caps](/concepts/send-gating-and-quiet-hours)) | Is this specific message allowed to leave *right now*, to *this recipient*?          | Runtime, per message, after launch                                                       |

The earlier content lens is the cheap one: a rewrite in the composer costs nothing, and it is the only one that catches "legally clean but carrier-filter-shaped" copy before it silently blackholes.

The measured counterpart to this predicted lab is the **seed lab** — register your own test handsets via the campaigns seed-lab endpoints and score actual terminal deliveries to them. Predict from history with the lab; measure with the seed roster; gate legality with the policy scanner.

## 6. Worked example

A draft SMS campaign:

```json theme={null}
POST /api/v1/campaigns/deliverability-lab
{
  "channel": "sms",
  "body": "BIG SALE — up to 50% off today only!!! Claim now: http://bit.ly/xYz",
  "window": "30d"
}
```

The lint maps `sms` to the SMS spam profile. The keyword scan matches urgency and promotional vocabulary ("SALE", "50% off", "today only", "Claim now") toward a spam score; the URL heuristics find a public shortener (`bit.ly`) and an insecure `http://` link. The shortener is a `block`-severity finding with weight 30; the insecure link is a `warn` with weight 8; the keyword scan adds its own weight on top — pushing the blended score into the `high`/`critical` bands and the verdict to `block`.

Suppose your 30-day history on the destination carrier (MCCMNC `28601`, say) runs a healthy 92% historical delivery over a few hundred terminal sends. The content-risk penalty — the score scaled to at most 35 points — drags this carrier's projection under the 50% block floor: a blocking warning on the prediction joins the blocking warning on the content. Meanwhile a second destination carrier with a strong baseline (99% historical) discounts the same content penalty and lands above 75% — no advisory raised there.

The roll-up comes back with **two blocking warnings** (content verdict block; predicted overall delivery below 50%) and **one advisory** (the insecure `http://` link). `ready_to_send` is false. Fix it in the composer by swapping the shortener for a branded link domain, moving to `https://`, and softening the urgency phrasing — re-score, and the block clears.

## 7. Limits — what the lab cannot predict

The lab is a heuristic over **what it has seen**, and two blind spots are structural:

* **Novel content.** The keyword profiles and URL heuristics cover known carrier-filter triggers; a newly circulating spam pattern with no similarity to the profiled vocabulary scores low until carriers (and then the classifier) catch up. A 0–100 lint score is a filtering-risk estimate, not a guarantee.
* **A carrier you have never sent to.** The prediction is built from your account's own terminal history. A destination operator with no past sends contributes no baseline — the model does not borrow other tenants' outcomes, and a `low`-confidence rate is labeled, not silenced. The only way to close the gap is to send (or use the seed lab to measure a test handset on that carrier).

Two more boundaries worth keeping: the lab is **not the policy scanner** — a clean lint verdict says nothing about TCPA consent or SHAFT content — and for push, voice, and fax (no carrier content-filtering layer to model) the report still returns URL heuristics, but the `predicted_delivery` block stays null because there is no meaningful per-carrier SMS-style delivery rate to project.

## See also

* [Sender warming and reputation](/concepts/sender-warming-and-reputation) — the identity/reputation axis the lab deliberately does not score.
* [Outbound send gating and quiet hours](/concepts/send-gating-and-quiet-hours) — the runtime per-message admission chain that fires after launch; quiet-hours classification, not pre-send scoring.
* [Outbound route quality and circuit breakers](/concepts/outbound-route-quality-and-circuit-breakers) — the per-route health lens (grey routes, DLR latency) on the destination path itself; complementary to per-carrier delivery prediction.
* [Deliverability Lab guide](/guides/campaign-deliverability-lab) — the how-to surface: request/response shape, failure modes, and the Review-step wiring.
* [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.
