Mailchimp → Customer.io
Export each Mailchimp audience by status, then import people into Customer.io with email as the identifier. Bring unsubscribes in before anyone can send, and rebuild journeys as campaigns.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
Mailchimp
- A broad small-business marketing suite combines email, contacts, forms, and campaigns
- Audience, campaigns, automations, and reporting live in one publishing system
- Pricing, interface complexity, and audience-based billing can become frustrating
- Automation logic, analytics history, and deliverability settings are not very portable
Customer.io
- People, attributes, and events drive campaigns without a separate list for every segment
- Audience, campaigns, automations, and reporting live in one publishing system
- Journeys, Liquid, and event history have to be rebuilt, and a CSV import can fire automations while it runs
- Automation logic, analytics history, and deliverability settings are not very portable
Customer.io: People, attributes, and events drive campaigns without a separate list for every segment. This removes a major source-side concern: Pricing, interface complexity, and audience-based billing can become frustrating.
What you lose: A broad small-business marketing suite combines email, contacts, forms, and campaigns. What you inherit: Journeys, Liquid, and event history have to be rebuilt, and a CSV import can fire automations while it runs.
Jump to a section
Know the shape of the move.
This timeline assumes
- One Mailchimp audience, under 100 columns of merge fields, and a CSV under 100 MB. Split larger files. Customer.io says an import takes about a minute per 20 to 30 thousand rows.
- You own the Mailchimp account, the Customer.io workspace, and DNS for the sending domain.
- Email is enabled as an identifier in the Customer.io workspace before the import.
- The site already documents Mailchimp → Klaviyo and Mailchimp → Kit if a list-based tool is a better fit than an event-driven one.
- No Customer.io campaign or broadcast sends to this audience until counts, unsubscribes, and a seed message pass.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Subscribed contacts | partial | critical | Mailchimp can export an audience as a ZIP with separate CSVs for subscribed, unsubscribed, non-subscribed, and cleaned contacts. Customer.io imports a CSV when a column maps to id, email, or phone. A column named email maps to the reserved email attribute. | Import the subscribed file only after unsubscribes are in. Map Email Address to email. Record the Mailchimp count and the Customer.io import result. |
| Unsubscribes and cleaned addresses | partial | critical | Customer.io stores a global unsubscribed attribute. true means the person is unsubscribed from email, SMS, WhatsApp, and push. Mailchimp's cleaned contacts are hard bounces, not the same flag. | Import unsubscribed people with unsubscribed set to true before any send. Keep cleaned addresses out of the subscribed file. Do not treat a cleaned row as a new subscriber. |
| Tags and groups | manual | high | Mailchimp tags and groups are not Customer.io segments. Extra columns become attributes if you map them. Customer.io builds segments from attributes and events, not from a stored list membership. | Map each tag you still use to a boolean attribute. Rebuild the segments you send to. Drop tags nobody references. |
| Customer journeys | lost | critical | Mailchimp automations do not import as Customer.io campaigns. A CSV row can trigger an automation while the import runs, because Customer.io processes imports row by row. | Pause or draft every campaign that triggers on a new person or an attribute change. Rebuild journeys after the import, from the event you actually care about. |
| Campaign content and templates | manual | high | Mailchimp templates do not become Customer.io designs. The drag-and-drop editor inserts an unsubscribe block with the Liquid tag for you. A hand-built message needs that link. | Rebuild the next three campaigns. Confirm each message contains an unsubscribe link before it can send. |
| Open and click history | lost | medium | A Mailchimp contact export does not include open and click rates. Those live on email reports. Customer.io will not have that history on the person. | Export any report you still need from Mailchimp and archive it. Do not expect a segment based on past opens to match. |
| Signup forms and landing pages | lost | high | Mailchimp forms and pages do not move. New signups still land in Mailchimp until you point them at Customer.io. | Replace each live form before cutover. Keep Mailchimp forms from accepting people after the freeze. |
| Sending domain | manual | critical | Customer.io authenticates on an account-specific subdomain such as cio#####.yourdomain.com, so the records do not replace the root domain. You add MX, SPF, DKIM, and a DMARC check. Verification can take up to 72 hours. Unless you use your own SMTP, the domain must verify before Customer.io will send from it. | Add the domain in Workspace Settings, publish the records Customer.io shows, and click Verify domain. Do not delete existing mail records on the root. |
| Multiple audiences | manual | high | Mailchimp audiences are separate. Customer.io has one workspace of people. The same email in two audiences can collapse into one person, or error if you update by email and two people already share it. | Pick one primary audience. Note which other audience a duplicate came from, as an attribute, before you merge. |
| Non-subscribed contacts | manual | high | Mailchimp's non-subscribed status is not a Customer.io concept. If unsubscribed is false or missing, Customer.io treats the person as subscribed, or as following topic preferences when a subscription center is on. | Do not import non-subscribed rows as ordinary people unless you set unsubscribed true or you have consent to mail them. |
| E-commerce events | lost | medium | Mailchimp purchase events do not ride along in the contact CSV. Customer.io can import events from a separate CSV, with _cio_name and _cio_customer_id, but that is a second file you build. | Leave historical orders out of the first import. Send new events from the store after cutover. |
| Export retention | manual | medium | Mailchimp keeps contact exports in the account for 30 days. Customer.io deletes an export from the workspace after 60 days. | Download both files and store them with checksums outside either account. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| Mailchimp Email Address | Customer.io email | automatic | Enable email as an identifier. One email per cell. |
| Subscribed CSV | People with unsubscribed false | transform | Import this file only after the unsubscribe file. |
| Unsubscribed CSV | unsubscribed true | transform | true also clears subscription-center topics. |
| Cleaned CSV | No destination | unsupported | Keep hard bounces out of the subscribed import. |
| Merge fields such as FNAME and LNAME | Attributes | transform | A new column title becomes a new attribute. |
| Tags and groups | Boolean attributes, then segments | manual | There is no list object to receive a Mailchimp audience. |
| Customer Journey | Campaign | manual | Rebuild the trigger. Do not leave the campaign live during import. |
| Audience export ZIP | Archived CSVs | manual | Only Owners and Admins can export an audience. |
| Mailchimp sending domain | Customer.io sending domain on cio##### subdomain | manual | Add the four DNS records. Do not remove root mail records. |
Make the move recoverable.
Create the source-of-truth backup
Keep a full Mailchimp audience export, campaign list, and DNS snapshot before Customer.io imports or sends.
- Export the audience. Unzip subscribed, unsubscribed, non-subscribed, and cleaned CSVs and record each row count.
- Export the email reports you still need. The contact CSV will not contain opens and clicks.
- List live journeys, signup forms, and the next scheduled campaign.
- Save the current DNS records for the sending domain.
- Store the files with checksums. Edit copies, not the originals.
Proof to capture: A dated note with CSV checksums, the four status counts, the journey list, and the DNS snapshot.
Split the Mailchimp ZIP by status
Stop cleaned and unsubscribed addresses from landing as mailable people.
- Download the ZIP from the export email. Mailchimp keeps it for 30 days.
- Count rows in each status file, minus the header.
- Add an unsubscribed column. Set true on the unsubscribed file and false on the subscribed file.
- Leave the cleaned file out of both imports.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Import people into Customer.io
Create people by email without firing live campaigns.
- Pause campaigns that trigger on a new person or an attribute change.
- Import the unsubscribed file first. Map email and unsubscribed. Add new people. Ignore empty values.
- Import the subscribed file the same way.
- Read the import email. Customer.io cannot stop an import after you click Import.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Authenticate the sending domain
Let Customer.io send from your domain without taking over root DNS.
- Add the domain and the From address.
- Publish the MX, SPF, DKIM, and DMARC records on the cio##### subdomain Customer.io shows. DMARC on the root is enough if one is already there.
- Click Verify domain. Wait, then verify again. This can take up to 72 hours.
- Do not delete the records that receive your own mail.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
The import sends mail
Customer.io processes the CSV row by row, and a new person or attribute change can enter a campaign.
- Consequence
- The whole list gets a journey email during the import.
- Mitigation
- Pause those campaigns first. Import unsubscribes before subscribers.
Stop if: Any live campaign still triggers on person creation or on the attributes you are writing.
Unsubscribes arrive as subscribed
A missing or false unsubscribed value means the person can be mailed.
- Consequence
- You mail people who opted out in Mailchimp.
- Mitigation
- Set unsubscribed true from the unsubscribed CSV. Sample both files after import.
Stop if: Any sampled opt-out is missing or has unsubscribed set to false.
Root DNS gets replaced
Customer.io's records belong on its subdomain. The docs say not to delete your own domain's records.
- Consequence
- Your inbox stops receiving mail.
- Mitigation
- Add only the records on the cio##### hostnames. Keep a copy of the previous zone.
Stop if: A root MX or SPF record was edited and inbound mail fails.
Two Mailchimp audiences collapse
Customer.io updates by email fail when two people already share that email.
- Consequence
- One audience's attributes overwrite the other, or rows error.
- Mitigation
- Import one audience, then review duplicate-email errors before the second.
Stop if: The import report shows duplicate-email errors you have not explained.
Do the work in this order.
- Day 1 · inventory
Export the audience and list what sends
2 to 3 hours active1 day elapsedMailchimp export email waiting- Export the audience and count each status file.
- List journeys, forms, and the next campaign.
- Save DNS for the sending domain.
Stop / go checkpointDo the status counts add up?
Go when: Subscribed, unsubscribed, non-subscribed, and cleaned counts match the audience.
Stop when: An owner cannot export, or a status file is missing.
- Day 2 · workspace
Prepare Customer.io and the domain
2 hours active1 to 3 days elapsedUp to 72 hours for DNS verification waiting- Enable email as an identifier.
- Add the sending domain and publish DNS.
- Draft the campaigns you will rebuild. Leave them unable to send.
Depends on: inventory
Stop / go checkpointCan this workspace receive people safely?
Go when: Email is an identifier and no campaign will fire on import.
Stop when: A live campaign still triggers on new people.
- Day 3 · import
Import unsubscribes, then subscribers
2 to 4 hours active1 day elapsedAbout a minute per 20 to 30 thousand rows waiting- Import unsubscribed people with unsubscribed true.
- Import subscribed people with unsubscribed false.
- Read both import results before you touch campaigns.
Depends on: workspace
Stop / go checkpointDid both files land?
Go when: Counts match, or each gap is an invalid email or a duplicate you logged.
Stop when: Opt-outs are missing or a campaign sent during the import.
- Day 4 · rebuild
Rebuild segments and the next campaign
3 to 4 hours active1 day elapsedNone waiting- Turn the tags you still use into attributes and segments.
- Rebuild the next message, including the unsubscribe link.
- Send only to a seed list.
Depends on: import
Stop / go checkpointIs the seed send clean?
Go when: Seeds get one message, the unsubscribe link works, and Mailchimp sent nothing.
Stop when: A seed is missing, duplicated, or an opt-out received mail.
- Day 5 · cutover
Freeze Mailchimp and send once
1 to 2 hours active1 day elapsedDomain verification if it is still pending waiting- Stop Mailchimp campaigns and forms.
- Import the last status delta.
- Send the first Customer.io campaign with one owner watching.
Depends on: rebuild
Stop / go checkpointWhich product sends the next campaign?
Go when: The domain is verified, counts still match, and the seed passed.
Stop when: Verification is incomplete or a Mailchimp campaign is still scheduled.
Cut over with a way back.
Cutover
Make Customer.io the only sender without mailing opt-outs or double-sending the list.
- Pause Mailchimp campaigns and journeys. Record the time.
- Export once more and import only the new rows.
- Confirm the sending domain shows verified.
- Send the Customer.io campaign to the seed list, then to the segment.
- Leave Mailchimp unable to send until the next morning's counts still match.
Proof to capture: Freeze time, both import emails, domain verification, and the seed headers.
Rollback
Send the next campaign from Mailchimp if Customer.io consent or delivery is wrong.
- Stop the Customer.io campaign and keep the import report.
- Do not delete people. You may need the unsubscribed flags to compare.
- Schedule the next send in Mailchimp only, from the pre-freeze audience.
- If new signups arrived in Customer.io, export them and add the consented ones in Mailchimp by hand.
- Leave the cio##### DNS records in place until mail is stable. Removing them is a separate change.
Proof to capture: Mailchimp is the only scheduled sender, and a sample of opt-outs stayed unsubscribed there.
- A known Mailchimp opt-out has unsubscribed set to false.
- A campaign sent while the CSV was still importing.
- The sending domain is still unverified when you meant to send.
- Subscribed counts differ and you cannot name the missing rows.
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
AUD-01Blocking | Status counts | Compare Mailchimp subscribed and unsubscribed counts with Customer.io filters on unsubscribed. | Counts match, or every gap is a logged invalid or duplicate email. | Mailchimp export counts and the Customer.io import emails. | |
CON-01Blocking | Opt-out sample | Pick 20 addresses from the unsubscribed CSV and open them in Customer.io. | Each has unsubscribed true. | The address list and screenshots of the attribute. | |
CLN-01Blocking | Cleaned addresses stayed out | Search Customer.io for 10 cleaned addresses. | They are absent, or present only with unsubscribed true and a note. | The search list. | |
SEG-01 | Tag attributes | Compare one Mailchimp tag count with the boolean attribute. | The counts match for every tag you still send to. | The tag table. | |
CAM-01Blocking | Nothing fired on import | Open campaign metrics for the import window. | No production campaign sent during the import. | Campaign delivery timestamps. | |
DNS-01Blocking | Sending domain | Read Verify domain and send a seed from the real From address. | SPF, DKIM, and DMARC checks are green. The seed arrives signed. | Verification screen and seed headers. | |
UNS-01Blocking | Link in the message | Open the seed and use the unsubscribe link on a test person. | unsubscribed becomes true and a later test does not send to them. | Before and after attribute values. | |
ONE-01Blocking | One sender | Look at the Mailchimp campaign calendar and Customer.io scheduled sends. | Only the named Customer.io campaign can send. | Both schedules. |
Remove the scaffolding safely.
Safe after: One full Customer.io send, a day of normal bounces and complaints, and a signed count check.
- Archive the Mailchimp ZIP, both import reports, and the DNS snapshot outside either product.
- Turn off remaining Mailchimp journeys and forms.
- Remove Mailchimp users that only existed for the export.
- Keep the Mailchimp audience readable for the agreed retention window before you cancel.
- Write the next review date on the playbook.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- Mailchimp: view or export contactsAccessed 2026-11-06
- Mailchimp: import a suppression listAccessed 2026-11-06
- Customer.io: import people or events via CSVAccessed 2026-11-06
- Customer.io: global unsubscribesAccessed 2026-11-06
- Customer.io: domain authenticationAccessed 2026-11-06