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

# Set up the pre-send policy scanner and DLP rules

> Step-by-step setup for the pre-send policy scanner: pick a scan mode (warn / strict / off), configure it from the console or the API, work the SHAFT / DLP / spam-keyword findings, verify with the lint and quiet-hours preview endpoints, and remediate the 422 / 503 error codes.

# Set up the pre-send policy scanner and DLP rules

Every outbound message — SMS, MMS, WhatsApp, email, RCS, and the IM channels —
runs through the policy scanner **before it dispatches to the carrier**. This
guide walks the setup end to end: what the scanner checks, where you set the
mode in the console and over the API, what each mode does to flagged traffic,
how to work the findings it returns, and how to verify your posture before a
campaign opens.

<Note>
  The policy scanner is a **tenant-owned control**. You pick the mode for your
  own organization; it defaults to `warn` (scan everything, refuse nothing),
  and Orbit never uses it to gate your traffic globally. This page is not
  legal advice — confirm your TCPA / SHAFT / GDPR / PCI obligations with
  counsel.
</Note>

## Step 1 — Know what the scanner checks

The scanner folds eight rule families into one verdict per message:
`pass`, `warn`, or `block`.

| Rule family | What it flags | Default severity |
| - | - | - |
| TCPA quiet hours | Marketing SMS / WhatsApp to US `+1` recipients outside 08:00–21:00 recipient-local. Transactional traffic (OTP, alerts) is exempt. | Advisory — becomes enforceable in `strict` mode on the SMS marketing lane. |
| SHAFT content | Sex, hate, alcohol, firearms, tobacco, and cannabis keywords on US SMS. | `block` |
| Missing opt-out | A promotional-looking body with no opt-out phrase (STOP, PARAR, ARRÊTER, 退订, across 14 locales). | `warn` |
| Short URLs | Public shorteners (`bit.ly`, `t.co`, …) on any channel — carriers filter them aggressively. | `warn` |
| Spam-keyword score | A SpamAssassin-style score from 0–100 on SMS, WhatsApp, email, RCS. 60–79 warns; ≥ 80 blocks. | `warn` / `block` |
| GDPR sender identity | Email to EU recipients with no From display name or no physical postal address in the footer. | `warn` |
| Country registration & sender gate | An unregistered or mismatched sender in a country that requires registration (India DLT, KSA, Turkey). Uncatalogued countries are never blocked. | `block` |
| DLP — sensitive data | A full credit card / PAN, US Social Security number, or IBAN in the message body, on **every** channel. Passport detection is opt-in. | `block` |

A channel with no applicable rules returns a clean `pass`. The full
per-rule contract — channels, checks, and the precision validations that
keep false positives rare — is on the
[policy scanner](/compliance/policy-scanner) and
[DLP scanner](/compliance/dlp-scanner) reference pages.

### What "scan mode" means

The verdict is the scanner's opinion; the **scan mode** is your
organization's decision about what a verdict does. Three modes:

| Mode | What a `block` verdict does |
| - | - |
| `warn` (default) | The send proceeds. The findings are recorded on the message and returned in the `X-Policy-Violations` response header. |
| `strict` | The send is refused with `POLICY_VIOLATION` (HTTP 400, or 422 for TCPA quiet-hours on the SMS marketing lane). |
| `off` | The scan is skipped on the send path entirely. |

Think of `warn` as *pass with an audit trail*, `strict` as *block*, and
`off` as *don't scan*. New organizations read back `warn` until an owner
sets a mode explicitly.

## Step 2 — Set the mode in the console or over the API

**Console:** open **Settings → Compliance** and set **Policy scan mode** to
`warn`, `strict`, or `off`. The change applies to the very next send and is
written to your audit log as `settings.policy_scan_mode_updated`.

**API:** the mode is the one self-serve compliance toggle on the public
surface. Read it (any workspace member):

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/settings/compliance/policy-scan-mode \
  -H "Authorization: Bearer $API_KEY"
```

```json 200 theme={null}
{
  "data": { "policy_scan_mode": "warn" },
  "meta": { "request_id": "req_9f2c…", "timestamp": "2026-08-31T12:00:00Z" }
}
```

Set it (owner role required):

```bash theme={null}
curl -X PATCH https://api.orbit.devotel.io/api/v1/settings/compliance/policy-scan-mode \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"policy_scan_mode": "strict"}'
```

A `PATCH` with anything outside `strict`, `warn`, `off` returns
`422 VALIDATION_ERROR`.

The DLP rule's own knobs — its category set and its
`block` / `redact` / `warn` / `off` response mode — are **not** self-serve:
they go through support, apply on the very next scan, and land on the same
audit feed. The floor is `credit_card`, `ssn`, `iban` at severity `block`;
ask support to add the context-gated `passport` category, narrow the set,
or step the mode from `block` to `redact` (matched spans are substituted
with typed sentinels like `[REDACTED_CARD]` before dispatch).

## Step 3 — Choose how flagged messages flow

Pick the mode against what a refusal costs your traffic:

* **Start in `warn`.** Every send proceeds and every finding lands on the
  message metadata and your audit feed. Watch the findings for a launch
  cycle so you learn your content's real hit rate before you let the gate
  refuse anything.
* **Move to `strict` when the finding stream is clean.** A `block` verdict
  then refuses the send — this is how you make SHAFT, a spam score ≥ 80,
  DLP, and the country sender gate a hard stop, and how TCPA quiet hours
  becomes enforceable on the SMS marketing lane. The DLP scanner validates
  every candidate twice (shape, then checksum or registry ranges) before it
  counts, so `strict` refuses actual card and identity data — not order
  ids, tracking codes, or phone-shaped numeral runs.
* **`off` is for a lane you have vetted another way.** It removes every
  check, including the PCI/PII floor — a deliberate posture decision, not a
  debugging step.

There is no per-message override in any mode: no "send anyway" flag and no
per-message waiver. The tenant-level mode and the DLP category set are the
only supported levers.

## Step 4 — Work the findings: SHAFT, DLP, and spam keywords

A violation entry names the rule, its severity, an operator-facing message,
and a suggestion. Three worked examples:

**SHAFT rule hit.** A US SMS body containing an alcohol term:

```json theme={null}
{
  "rule": "shaft_alcohol",
  "severity": "block",
  "message": "SMS content contains SHAFT-restricted keyword (alcohol): \"vodka\". US carriers will filter or fine this traffic.",
  "suggestion": "Remove the flagged term, or route through an age-gated campaign with carrier approval."
}
```

Fix the content, not the gate — rephrase, or move that promotion to an
age-gated campaign. Hate-speech violations deliberately never echo the
matched term back.

**DLP rule hit.** The DLP detector is precision-built: a credit card
candidate must pass the Luhn checksum **and** the card-network
prefix/length table; an SSN needs a separator and valid SSA
area/group/serial; an IBAN must pass the ISO 7064 mod-97 checksum and the
per-country length registry. On a hit, the finding carries a **category and
character offsets only — never the matched text** — so the scanner cannot
leak a card or SSN into logs, headers, or the audit trail. Remediate by
moving the regulated value to a surface you own end to end (a hosted
payment page or form), never by waiving the gate.

**Spam-keyword score.** Email and messaging bodies are scored 0–100
against a SpamAssassin-style ruleset. The lint response carries the score
as a top-level `spam_score` field and lists every matched rule so you can
rephrase the call-to-action — scores ≥ 80 are a `block` verdict, 60–79 a
`warn`. Keep the body specific — name the product, the date, the action —
and the score drops. The full matched-rule list comes back on every lint
call, so you iterate on the draft, not on live sends.

## Step 5 — Verify before a campaign opens

Never discover your posture mid-blast. Two read-only endpoints answer
"would this go through?" without sending anything:

* **Content verdict** — `POST /api/v1/messages/lint` runs the **same
  scanner** against a draft body and returns the same verdict, violations,
  spam score, and matched rules. Pass the `to`, `subject`, `from_name`,
  `recipient_country`, and `scheduled_at` hints so the lint sees the same
  context the send-path scan would. The dashboard compose dialog already
  calls it debounced; do the same in your campaign tooling.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/lint \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "body": "Flash sale — wine tasting this Friday!",
    "to": "+14155552671",
    "scheduled_at": "2026-10-09T22:30:00Z"
  }'
```

* **Timing verdict** — `GET /api/v1/compliance/quiet-hours/preview`
  answers "would this send to this recipient dispatch right now?" on the
  timing axis, with `next_allowed_at` when the window is closed. Worked
  per-channel calls are in the
  [quiet-hours preview](/compliance/quiet-hours-preview) reference; the
  setup walkthrough is in
  [quiet hours configuration](/guides/quiet-hours-configuration).

Run both as the last two items of the
[outbound pre-flight checklist](/guides/send-gates-preflight-checklist)
before a launch — content through lint, timing through the preview — and a
blocked row on launch day is a surprise you chose.

## Step 6 — Understand the transcript scrubber relationship

The DLP scanner is a **send gate**: it refuses or flags regulated data at
dispatch time. It does not clean stored data. Two sibling controls cover
the storage side:

* The **transcript PII scrubber** strips personal data from agent
  conversation transcripts as they are written, so the stored record never
  holds the original text. It is destructive by design — it tolerates some
  false positives rather than hold up a conversation write. The contrast
  with the send gate is spelled out in
  [DLP vs. the transcript scrubber](/compliance/dlp-scanner).
* The **[PII vault](/compliance/pii-vault)** tokenizes contact identifiers
  (email, phone, SSN, national ID) so segmentation and activation run on
  opaque tokens instead of cleartext.

Use all three: the send gate stops regulated data from leaving, the
scrubber keeps stored conversations clean, and the vault keeps raw
identifiers out of audiences and exports.

## Step 7 — Error codes and remediations

Three distinct failure surfaces come off this stack — tell a content block
from a scanner outage before you retry:

| Code | HTTP | What it means | Remediation |
| - | - | - | - |
| `POLICY_VIOLATION` | 400 (content-class block) or 422 (TCPA quiet-hours in `strict` mode) | The scanner returned `block` while your org is in `strict` mode. The response carries the violations list. | Fix the content against the named rule and retry; for quiet-hours, schedule to `next_allowed_at`. |
| `VALIDATION_ERROR` | 422 | A `PATCH` to the mode endpoint with a value outside `strict` / `warn` / `off`. | Send one of the three enum values. |
| `POLICY_SCAN_MODE_LOOKUP_FAILED` | 503 | The send-path guard could not read your org's mode and its cache was cold. The send fails closed — this is a transient lookup failure, not a policy verdict. | Retry; if it persists, check status and contact support. |
| `POLICY_SCANNER_UNAVAILABLE` | 503 | The scanner itself threw (bad import, malformed rule). Also fails closed so an unscanned message never ships. | Retry; persistent 503s are a platform issue — contact support. |

A refused send in `strict` mode also writes one audit event
(`policy.message_blocked`) with the channel, verdict, and rule names — no
matched text, ever — so your reviewers work the refusal feed as a review
queue, not a retry loop. The full taxonomy is in
[Error codes](/reference/error-codes).

## Related pages

* [Pre-send policy scanner](/compliance/policy-scanner) — the full rule
  contract, verdicts, and the mode endpoint
* [DLP scanner — data classes & strict mode](/compliance/dlp-scanner) —
  categories, precision checks, redact mode, and the transcript-scrubber
  contrast
* [Quiet-hours preview](/compliance/quiet-hours-preview) — the timing-side
  read-only check, field by field
* [Contact PII vault](/compliance/pii-vault) — tokenize identifiers out of
  audiences and exports
* [Outbound compliance pre-flight checklist](/guides/send-gates-preflight-checklist) —
  the launch-time pass this setup feeds
* [Send gates](/compliance/send-gates) — the gates that run alongside the
  scanner


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.