# CSV import

Bring in a list from anywhere. Mimeo takes any CSV with a header
row — there's no required column order and no required template, because
you do the mapping.

## Mapping columns

After you upload, Mimeo shows every column in your file and asks
what each one is. Your choices per column:

| Map to | Behavior |
| --- | --- |
| A core field | Email, first name, last name, or subscribed on. Email is the match key —<br> it's lowercased, and an existing person with that address is updated<br> rather than duplicated. |
| Subscribed on | The date the person joined the list — map your old tool's<br> subscribe-date column here. Only an **earlier** date<br> replaces what Mimeo already has, so re-running an import is safe and<br> the order you import batches in doesn't change the result. A cell<br> Mimeo can't read as a date leaves the stored date alone rather than<br> failing the row. |
| A custom field | Any field you've declared under Settings → Fields. If the<br> field doesn't exist yet, you can create it right here during<br> mapping instead of backing out to Settings first. |
| Tags | The cell's value becomes one or more tags. Multi-value cells are<br> split on `|`, `,`, and `;`, so<br> `customer, pro; beta` becomes three tags. |
| Unsubscribed | Marks the person as suppressed. A truthy value works, and so does<br> a timestamp — if you give a timestamp, it's<br> **preserved** as the unsubscribe date rather than<br> being reset to today. |
| Skip | Ignore this column entirely. |

## An import never sets your automations off

Applying a tag emits an event, and events are what start flows — so
importing 19,000 people with one tag apiece would, taken naively, start
19,000 flow runs from a spreadsheet and mail all of them.

It doesn't, because of a checkbox on the import — **"Treat this**
**import as history, so it triggers nothing"** — which is
**on by default**. Everything the import emits is marked
historical, and flows ignore historical events. You don't have to
deactivate your flows before importing, and you don't have to remember
to turn them back on.

**Turn it off only when you want the opposite.** A small
batch of genuinely new signups who should get the welcome journey is a
real case, and it's the reason the setting exists rather than the
behavior being fixed. On a large list, or on anything coming over from
another tool, leaving it off is how people accidentally mail thousands
at once — so the import screen says so plainly while it's off.

Over the API the same decision is `mark_historical` on the
import, defaulting to `true`. See
[backfilling without sending\
mail](/content/docs/api/events.html#historical/index.html) for what "historical" means everywhere else.

## Import posture: fill or overwrite

For people who already exist, you choose how the incoming data is
applied:

- **Fill blank fields only** — existing values are left
alone; the import only writes where Mimeo currently has nothing.
This is the safe default for merging a supplementary list.

- **Overwrite** — incoming values replace what's there. Use
this when the CSV is the authority, like a fresh export from the
system that owns that data.

The posture applies to field values. Tags are additive either way — an
import never strips tags a person already has.

## Static tags

Separate from any tag column, you can apply a set of tags to
every row in the import. This is how you keep provenance:
tagging an import `from-mailchimp-2026-07` means you can
always find, filter, and if necessary undo that batch later.

## Importing as already unsubscribed

There's a single switch that marks the entire import as
already-unsubscribed, regardless of any column mapping.

This is the one to reach for when migrating off another platform. Export
your old tool's suppression list, import it with this on, and those
addresses arrive in Mimeo already opted out. Do this
**before** importing your active list, and nobody who
unsubscribed from your old tool gets mail from your new one.

## Preview before you commit

Mimeo shows you a preview of how your mapping resolves against real
rows from your file before anything is written. Check that names landed
in name columns, that your tag delimiter split the way you expected, and
that the unsubscribe column was read correctly. Then commit.

## The import report

After the import runs you get a report with:

- **Created** — new people added.
- **Updated** — existing people matched by email and
modified.

- **Skipped** — rows that couldn't be imported, most
commonly a missing or malformed email address.

- **Row errors** — the line-by-line account of each
skipped row and why.

Row errors are per-row, not fatal: a handful of bad addresses doesn't
stop the rest of the file from importing. Fix those rows in a small
follow-up CSV rather than re-running the whole thing.

## Importing with your agent

The same import — upload, mapping, posture, preview, commit — is
available end to end through the
[Imports API](/content/docs/api/imports.html), and your
[manager repo](/content/docs/manager-repo/index.html) ships an
**import-a-csv** skill that walks an agent through it:
it reads the file, proposes the mapping, asks the questions above,
and commits only after you confirm.
