# Campaign preflight

Preflight runs before a campaign can be scheduled. It returns the audience after scrubbing, a per-check result, and the distribution across receiving providers. A fail on any check blocks scheduling outright. A warning requires an explicit acknowledgement, which is recorded in the audit log with your user id against it.

```json
{
  "audience_size": 128430,
  "after_suppression": 121905,
  "after_dedupe": 121340,
  "checks": [
    { "id": "unsubscribe_header", "status": "pass" },
    { "id": "dmarc_present", "status": "pass", "detail": "p=quarantine" },
    { "id": "warmup_ceiling", "status": "warn",
      "detail": "Your current daily cap is 40,000. This campaign will send over 4 days." },
    { "id": "link_reputation", "status": "pass" },
    { "id": "spam_score", "status": "pass", "score": 1.4 },
    { "id": "image_text_ratio", "status": "warn", "detail": "82% image area" },
    { "id": "consent_evidence", "status": "pass" }
  ],
  "isp_distribution": {
    "gmail.com": 0.48, "hotmail.com": 0.17, "yahoo.com": 0.06, "other": 0.29
  }
}
```

## What the scrub removes

1. Your workspace suppression list
2. The global platform suppression list — known spam traps, repeat complainers, invalid domains
3. Role addresses (postmaster@, abuse@, noreply@) — configurable, on by default
4. Duplicates, collapsed by address hash
5. Contacts whose status is not subscribed
6. Addresses that have hard-bounced before
7. Disposable domains — configurable

Every skipped recipient carries a reason, exposed in the UI, so you can see exactly why 128,430 became 121,340 rather than being told a smaller number with no explanation.
