> ## Documentation Index
> Fetch the complete documentation index at: https://docs.politicalcomms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lists and address screening

> Importing email contacts, recording consent, what is removed at import, send-time address screening, acquired lists, segments, and the sunset policy for unengaged contacts.

## Importing a list

Upload a file or sync from WinRed or Anedot. A list has one of four sources:

| Source | Where it comes from |
| - | - |
| `uploaded` | A file you imported |
| `segmented` | A segment built from contacts already in the platform |
| `winred` | Synced from WinRed |
| `anedot` | Synced from Anedot |

A list is `processing` while the import runs and `ready` when it is done. You
cannot send to a list that is not `ready`; the API returns
`409 EMAIL_LIST_NOT_READY`.

### Where the uploader lives

Every file you upload to the platform now goes through one page: **Assets >
File Uploads**. Pick **Email recipients** as the kind and drop the file: the
file you upload *is* the list, named after the file unless you rename it, so
there is no list to create first. Name it, choose whether it is available to
every campaign or only to one sending domain, then map the columns and record
consent. Texting contacts, opt-out numbers, email unsubscribes, texting media,
and email images are the other kinds on the same page.

A campaign combines lists by attaching several of them, exactly as a texting
project attaches contact lists, so there is no reason to import two files into
one list.

The old **Email > Lists > Import** link still works and redirects to the
uploader with the kind already selected, so existing bookmarks are fine.

### Importing over the API

`POST /v1/email/lists/import` does the whole thing in one call: give it an
HTTPS URL to a CSV you host, and it fetches, stages, and commits it as a new
list. `name` defaults to the file name; `email_domain_id` scopes the list to one
sending domain, and omitting it leaves the list available to every campaign.

```json theme={null}
{
  "source_url": "https://files.example.com/exports/august-donors.csv",
  "name": "August donors",
  "consent": { "source": "donation_form", "note": "Donate page opt-in checkbox" }
}
```

`consent.source` is one of `donation_form`, `petition`, `signup_form`, `event`,
`purchased`, `rented`, or `other`. It is the same attestation the wizard asks
for, and the same reasons to answer honestly apply.

`mapping` is optional. Leave it out and the platform uses the mapping it
recognizes from the export's own headers, which is what you want when the file
came from a common ESP. If neither your mapping nor the recognizer finds an
email column, the call returns `400 VALIDATION_ERROR` and `details.headers`
lists the headers it read, so the retry can name the right column instead of
guessing.

The response is `202` with an import id; the import runs in the background and
its progress and row counts are shown on the list in the dashboard, and in the
import `summary` over the API. The request takes no `options`: sending that
field returns `400 VALIDATION_ERROR`.

### What import removes and holds back

Every import, from the uploader or the API, applies the same checks:

* **Malformed addresses** are rejected and reported.
* **Likely typos in the domain** are skipped, not corrected. A row whose domain
  is a known misspelling of a major mailbox provider, such as `gmial.com`, is
  counted in the import summary as "Likely typo in the domain". Fix the address
  in your file and import it again.
* **Role addresses** such as `info@`, `admin@`, and `sales@` are always
  removed. They reach a shared inbox rather than a person and complain at a far
  higher rate than personal addresses, so there is no option to keep them.
* **Disposable addresses** from throwaway-mailbox services are removed. The list
  we check covers several thousand such domains.
* **Addresses that cannot receive mail**, such as a domain with no mail server,
  and addresses that already bounced or complained on this platform, are
  removed, as are duplicate rows in the same file.
* **Addresses currently held by screening** are imported but held back, and the
  import summary reports them as `screened`. See
  [Address screening at send time](#address-screening-at-send-time).

Suppressed addresses stay suppressed: re-importing a file does not bring back
anyone who unsubscribed, bounced, or complained.

### Donor syncs and segments

Two of the four sources are worth calling out because they are not files:

**WinRed and Anedot syncs** build an email list from your donor records
automatically, so people who give keep arriving on the list without anyone
exporting a CSV. Consent comes from the donation form itself, which is the
cleanest provenance an email list can have. Create one from **Email > Lists >
New dynamic list** (or from Contact Management); one connection feeds both a
text list and an email list, and the email list's page shows the connection
details and settings. The same picker offers **Recent openers** and **Recent
clickers**: lists of everyone who opened or clicked a campaign in a window you
choose, refreshed nightly. A refund can park a donor off the list, and a later gift brings them
back; retention works the same way. These lists are managed through the
connection rather than deleted, so the Delete action is not offered on them.

**Segments** are lists built from contacts already in the platform, using rules
rather than a file. Engagement rules are the useful ones: everyone who opened in
the last 30 days, everyone who clicked a particular campaign, everyone who has
not engaged in 90 days. Giving rules read your synced donations: total given,
recurring, gave recently, number of gifts, any single gift above an amount, and
the donation source (refcode, source code, UTM parameters, page name). Giving is
counted net of refunds: a refunded or charged-back gift does not count toward a
total or a gift count, and a gift that arrives through both a webhook and a
scheduled report is counted once. A random
split rule takes a stable slice of a list, so two segments with 0-50 and 50-100
never overlap; that is how you run two campaigns against halves of one list.
Because a segment is defined by its rules, it reflects the current state of your
contacts each time you use it rather than a snapshot from the day you built it,
and it can refresh nightly.

## Consent attestation

Every list requires a consent attestation when you create it: a short statement
of **how** the people on it agreed to hear from you, plus an optional note.

This is not a checkbox to click past. It is the record you will want if a
complaint is ever escalated, and the honest answer determines what happens next.
A list from your own donate-page opt-in behaves differently from a list you
bought.

## Acquired lists

If a list came from anywhere other than your own sign-up flow, set its
`acquired` field. Doing so is in your interest, not against it: an acquired list
that is not declared is far more likely to trip a deliverability breaker
mid-send and pause your campaign.

An acquired list is not blocked from sending. Declaring it has one consequence:
**warm-up runs on half steps.** The daily ceiling is halved at every stage of
the [warm-up ramp](/help/email/campaigns-and-deliverability#warm-up), so a
list with less certain provenance reaches mailbox providers more slowly.

## Address screening at send time

There is no separate cleaning step and no charge for one. Every campaign email
is screened as it is sent, using the strictest setting of the mail provider's
automatic validation. An address it rates as likely undeliverable is not sent
to.

What happens to a screened address:

* The message is recorded with status `suppressed` and reason
  `auto_validation`.
* It is **not counted as a bounce**, so it does not count toward your bounce
  rate or the [deliverability breakers](/help/email/campaigns-and-deliverability#deliverability-breakers),
  and it does not fire the `email.bounced` webhook.
* The address is **held out of all sends on the platform for 90 days**, for
  every organization. After 90 days it is checked again on its next send.
* The campaign report shows the total as **Screened as invalid**.
* You are billed the normal per-recipient email rate for it, the same as a
  bounce, because the message was handed to the mail provider.

Screening is a safeguard, not a guarantee. It does not confirm that a mailbox
exists at the large providers, so an address that has never been seen before and
is dead can still bounce once. A permanent bounce from a bad address suppresses it for
good; see [Bounces, complaints, and suppressions](/help/email/bounces-complaints-and-suppressions).

A campaign sends to every subscribed contact on its lists that is not
suppressed. There is no recipient policy to choose.

### Exporting a list

**Export** on the list page downloads a plain contact export: the columns from
the file you uploaded, in the order they arrived, followed by `email`, `status`,
and `status_reason`. There are no verdict columns and no state filters.

## Contact statuses

| Status | Meaning |
| - | - |
| `subscribed` | Sendable. |
| `unsubscribed` | Opted out. Never sent to again. |
| `bounced` | Permanently bounced. |
| `complained` | Marked a message as spam. Never sent to again. |
| `invalid` | Not a usable address. |
| `sunset` | Dropped by the sunset policy. |

Only `subscribed` contacts are mailed. The rest are excluded automatically, and
none of them can be restored by re-importing the same file.

## Removing contacts

Removing an address **unsubscribes it rather than deleting the row**, because the
row carries the bounce and complaint history that makes suppression survive your
next import. To erase an address entirely, use
[forget this address](/help/email/compliance-and-link-tagging#forget-this-address).

## Suppressions and the sunset policy

Suppressions, bounces, complaints, unsubscribes, and the 90-day hold and 180-day
sunset all work the same way regardless of which list an address is on, so they
have their own page:
[Bounces, complaints, and suppressions](/help/email/bounces-complaints-and-suppressions).

The short version: an address that bounces permanently or complains is
suppressed for you automatically, unsubscribes are honored immediately, and
contacts who have not engaged in 90 days stop being mailed before they can
damage your reputation. Opening or clicking one of your emails counts as
engagement and starts that clock over.

## Bulk contact upsert over the API

`POST /v1/email/lists/{id}/contacts` takes up to 1,000 contacts per call and
returns a per-row outcome, so one bad address does not cost you the batch:

```json theme={null}
{
  "written": 2,
  "accepted": 2,
  "rejected": 1,
  "duplicates": 1,
  "results": [
    { "index": 0, "email": "a@example.com", "status": "accepted", "reason": null },
    { "index": 1, "email": "nope", "status": "rejected", "reason": "invalid_email" },
    { "index": 2, "email": "a@example.com", "status": "duplicate", "reason": "duplicate_in_request" }
  ]
}
```

Fix and resend only the rows that came back `rejected`.


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