Importing Contacts CSV — Best Practices and the 6-Click Field Mapping Wizard

Format the CSV right before you upload — column headers in row 1, UTF-8 encoding, one email per row. AcelleMail's import wizard handles the rest in 6 clicks. Visual walkthrough of the wizard plus error-recovery patterns + bulk-update strategies in the collapsible section.

Format the CSV right (before you upload)

The single biggest cause of "import failed for 1,247 rows" is malformed CSV. Get these right and the import flows smoothly:

Requirement What it means
UTF-8 encoding Save the file as UTF-8 — Excel defaults to "CSV (Comma delimited)" which is often Windows-1252 and breaks accented characters. In Excel: Save As → CSV UTF-8. In Numbers: Export → CSV with default encoding.
Column headers in row 1 The first row is field names: email,first_name,last_name,city. Lowercase, snake_case, no spaces.
One email per row Don't pack john@example.com, jane@example.com into a single cell. One row per subscriber.
Quoted strings if they contain commas A city value of Berlin, DE must be quoted: "Berlin, DE". Most spreadsheet tools do this automatically; hand-rolled CSV files often forget.
Email column required Every row must have a non-empty email. AcelleMail rejects rows with blank email.
No extra whitespace Leading/trailing spaces in emails or field values are stripped, but john@example.com (with trailing space) might cause confusion in audit logs. Trim before export.
Date format consistent If you have a signup_date column, use ISO 8601 (2026-05-19) — most reliable. Mixed 5/19/2026 + 19/05/2026 will trip the parser.

The 6-click import flow (same for every source)

Once you have the CSV ready, AcelleMail's import wizard is the same regardless of where the file came from.

1. Open the destination list

In the left sidebar, click Audience → choose the list that will receive these subscribers (or create one — New list button top-right).

Lists index

The list detail page shows current subscriber counts and a per-list toolbar:

List overview

2. Click "Import" in the list toolbar

The wizard entrypoint is right there on the list detail page:

Import entry point

3. Upload your CSV

Drop the file into the upload area:

Upload empty state

AcelleMail parses the file and confirms detection — rows-counted + sample preview:

Upload success

4. Map the columns

The wizard auto-detects standard columns (email, first_name, last_name) and shows green Mapped to EMAIL chips. For non-standard columns, pick the destination from the dropdown:

Map columns

5. Pick duplicate handling

On the same screen, choose what AcelleMail does when a row's email already exists in this list:

  • Skip — keep the existing subscriber, don't overwrite anything
  • Update — overwrite name / tags / custom fields with values from this CSV
  • Unsubscribe — mark the existing row as unsubscribed (rare; for re-importing an opted-out list)

6. Run the import

Click Start import. The job runs in the background — you can close the popup, work elsewhere, return to Audience → [list] → Import to see progress:

Import history

Pending → Running → Complete, with rows-imported / rows-skipped / errors counts per job.

What happens after Start import

The job runs in the background. For a 10k-row file, expect 30-90 seconds. For 100k rows, 5-15 minutes. AcelleMail processes in batches of ~500 with database transactions per batch — safe to close the browser tab; the job continues server-side.

The Import history view in the list shows progress per batch:

  • Pending — job queued, not yet processing
  • Running — worker actively reading the CSV
  • Complete — done; click to see the result summary

Result summary breaks down:

  • Rows imported — net successful new subscribers + updates
  • Rows skipped — duplicates handled per your chosen rule
  • Rows with errors — bad emails, invalid date formats, FK violations — with a downloadable error CSV showing exactly which rows failed and why

Download the error CSV, fix the rows in your spreadsheet, re-import just those rows.

Common CSV import errors and fixes

Error in error.csv What it means Fix
Invalid email format Row has missing @, malformed domain, or trailing whitespace Validate email syntax in spreadsheet before upload (Excel: use regex)
Email already exists (with Skip mode) Subscriber on this list already; per skip-rule, not overwritten Expected; not an error — just informational
Email already exists (with Update mode) Should be auto-updating; if it's an error here, the import ran in Skip mode Re-run with duplicate handling = Update
Field 'birthday' has invalid format Mixed date formats in CSV Normalize all dates to ISO 8601 before upload
Field 'lead_score' must be numeric A non-number value in a Number-type field Check CSV — strip non-numeric values or change the field type
Required field 'email' is empty Row has no email Remove the row or fix the value
Charset error: invalid UTF-8 sequence at row 4523 File saved as Windows-1252 or other encoding Re-export as UTF-8
Cannot map column 'birth_date' to field 'birthday' CSV header name differs from field name; not auto-detected Use the field mapping step to manually map

Common UI patterns

Duplicate handling cheat sheet

Choose When to use
Skip Importing a fresh batch of leads where existing subscribers shouldn't have their names/tags overwritten
Update Re-importing the same list from a master CRM where the CRM is the source of truth
Unsubscribe Importing a bounced-list export to mark addresses inactive (rare; usually you'd just delete them)

When to import vs. when to use the API

Scenario Use
One-time migration from another platform CSV import (fastest, easiest)
Daily sync from a CRM-of-record API + scheduled cron (idempotent, automatic)
New batch of leads (every week or month) CSV import works fine, OR set up CSV-upload-to-folder integration via your storage tool
Real-time subscribe (signup form) AcelleMail's built-in signup forms OR your custom form posting to AcelleMail's API

Pre-import sanity check

Open the CSV in your spreadsheet tool. Five quick checks before uploading:

  1. Row count matches expectations (a leading blank row will offset everything)
  2. Column headers are present and snake_case (email, not Email Address)
  3. Email column has no blanks (sort by email; blanks float to top)
  4. No duplicate emails within the CSV itself (de-dupe via spreadsheet first; AcelleMail will skip dupes but it's cleaner to remove them upfront)
  5. Try a 10-row test import first if the CSV is large — verify field mapping works, then re-run with the full file

Common UI signals + fixes

Symptom Likely cause UI fix
Import stuck on "Pending" for >10 minutes Queue worker isn't running Operator: check that queue:work is running (see setup queue workers)
Import shows "Complete" but list count didn't increase All rows skipped due to duplicates Check duplicate handling — should be Update if you want overwrite
Custom field column not detected during mapping Column header doesn't match field tag exactly Either rename header in CSV, OR manually map in step 4
Wizard rejects the file at upload step Wrong file type (xlsx not csv), or too large (>50MB default) Export as plain CSV from your spreadsheet tool; for >50MB, split into multiple files
Some subscribers imported, others not Mixed error types in single file (some invalid emails, some duplicates) Download error CSV → fix → re-import; the successful rows aren't re-counted
Tags column joins as single string Tag values need to be comma-separated within the cell: tag1,tag2,tag3 Re-format the tags column in your spreadsheet
Advanced: bulk-update strategies, API-driven sync, audit + recovery

For large lists or ongoing-sync scenarios, the CSV-through-UI path is one tool among several.

Bulk update via re-import:

If you need to update a single field across thousands of subscribers (e.g. reset everyone's frequency to weekly), the easiest path:

  1. Export the list to CSV (Audience → list → Export → All fields)
  2. Open in spreadsheet, edit the column for the values you want
  3. Re-import with duplicate handling = Update

AcelleMail processes the file in 5-30 minutes depending on size. The advantage over API is no scripting required; the downside is no per-row tracking of which value changed.

Scripted update via API (when you need fine control):

ACELLE_TOKEN="..."

# Set everyone's 'frequency' to 'weekly' in one list:
curl -X POST "https://acellemail.com/api/v1/lists/{list_uid}/subscribers/bulk-update" \
  -H "Authorization: Bearer $ACELLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {"status": "subscribed"},
    "updates": {"fields": {"frequency": "weekly"}}
  }'

Returns {updated_count, failed_count} synchronously. Handle 429 with backoff for large updates.

Idempotent CSV import pipeline:

When syncing from a CRM nightly, the standard pattern is:

# 1. Export CRM contacts to CSV
crm-export --tag=marketing --output=/tmp/contacts.csv

# 2. Pre-validate (optional but recommended)
csvkit validate /tmp/contacts.csv

# 3. Upload to AcelleMail
curl -X POST "https://acellemail.com/api/v1/lists/{list_uid}/imports" \
  -H "Authorization: Bearer $ACELLE_TOKEN" \
  -F "file=@/tmp/contacts.csv" \
  -F "duplicate_handling=update" \
  -F "field_mapping={\"email\":\"email\",\"first_name\":\"first_name\"}"

# 4. Poll for completion + result
import_uid="..."  # from response
curl -H "Authorization: Bearer $ACELLE_TOKEN" \
  "https://acellemail.com/api/v1/imports/$import_uid"

The full audit trail (which rows were updated, which were skipped, which failed) is in the response JSON.

Resumable imports for very large files:

For files >100k rows, the wizard can run for 15+ minutes. If the queue worker is restarted mid-import, AcelleMail's import job is designed to resume from the last completed batch — no rows are double-processed. The audit trail shows the exact row offset where the resume happened.

Pre-import deduplication:

If your CSV has internal duplicates (same email twice within the file), AcelleMail processes the rows in order and the LAST row's values win for the duplicate. To control which row wins, sort the CSV in your spreadsheet first — most recent record last.

For server-side dedup, use the API in a loop with upsert: true and last_updated_at field comparison.

Recovery from a bad import:

If you imported with wrong field mapping (e.g. mapped first_name to email and now have garbage), the recovery path:

  1. Don't panic — the original data is still there in your spreadsheet
  2. AcelleMail keeps per-import audit logs (admin → Audit log → filter by import event)
  3. Bulk-update the affected field back to correct values via API or re-import

There's no automatic "undo last import" — recovery is always forward-looking. Always test with a 10-row sample first on a non-critical list before running a 100k import.

Field type changes:

If you've imported 100k subscribers with birthday as a Text field, then realize you want it as a Date field — the change is in two steps:

  1. Audience → list → Fields → edit birthday → change type Text → Date. AcelleMail re-validates all existing values; rows where the value doesn't parse remain as-is until you fix them.
  2. Either bulk-update the bad rows via API, or export → fix in spreadsheet → re-import.

Custom-field discovery during import:

If your CSV has a column AcelleMail doesn't know about (e.g. lifetime_value and the list doesn't have that field yet), the field-mapping step in the wizard offers Create new field on import. Pick the field type (Number / Text / Date) → AcelleMail creates the field on the list + maps the column to it in one click. Useful for one-time migrations from systems with custom schemas.

Compliance audit trail:

Every import is logged with:

  • Who triggered it (admin user ID)
  • When (timestamp)
  • Which file (filename + hash)
  • How many rows added / updated / skipped / errored
  • The full error CSV is retained 90 days (configurable in admin settings)

For GDPR audit requests ("show me when you obtained this subscriber's consent"), the import audit log answers it — the import row references the original CSV which presumably has the source/opt-in date column.

Related articles

15 条评论

6 条评论

  1. v.petrova.ru
    For double opt-in: do you re-confirm migrated subscribers or tust the previous platform's records? lol
    1. admin
      Good question — and one that comes up often enough we should add an FAQ section. Short answe: yes for the common case; the exception is when you're running custom plugins that override the default behavior
  2. cw.dev.sh
    For double opt-in: do you re-confirm migrated subscribes or trust the previous platforms records?
    1. admin
      we're aware of the silent-bail-out on deleted customers — there's an open issue for it. Workaround for now: monitor the campaign:rerun log for absence of expected log lines, alert when silent for > 20 min...
  3. akira.tnk88
    for double opt-in: do you re-confirm migrated subscribers or trust the previous platform's records?
    1. admin
      We don't recommend that approach in production. It works in dev but has subtle race conditions under concurrent load. Stick with the documented pattern
    2. admin (已编辑)
      There's no built-in way today. Two workarounds: (1) cron + custom script polling the API every N minutes, (2) webhook-driven if your event source supports it. Most operators go with #2.
    3. admin (已编辑)
      honest answer: it depends on your provider. ses handles it gracefully; mailgun is stricter. well add a provider-by-provider table in the next revision.
  4. d.cohen.tlv
    Tip: keep one 'master' list and use segments instead of multiple lists. Way easier to maintain over time
    1. admin (已编辑)
      Good tip. The Cloudflare-outbound-rate-limit case is something we hadn't documented.
    2. admin (已编辑)
      worth adding to the article. pr welcome if you want to author the addition.
  5. jmorrison.itop…
    we learned the hard way to validate before importing. One bad list reduced our domain reputation for 3 months.
  6. sofia.costa.pt
    used Kickbox for the validation step. ~$0.005/address; cleaned 47k subscribers in 90 minutes. Worth it
    1. admin (已编辑)
      Useful context. The fact that it took 3 weeks end-to-end is realistic; we sometimes get pushed to say 1-week timelines and they're not honest :)
    2. admin (已编辑)
      Thanks for the breakdown Saving for our customer-success team's reference library...

More in List Management