Migrate from Mailchimp to Unitpost

Contacts, suppressions, topics, templates and more come over in one import.
What comes with you
To leave Mailchimp, paste a Mailchimp API key into Unitpost's Migration Assistant. It imports every audience's contacts with their consent, merge fields, tags and saved segments, groups as opt-in topics, cleaned addresses and spam complaints as suppressions, US SMS numbers with their opt-in state, drafts and your 25 most recent campaigns as templates, verified domains and webhooks. Pending contacts are left out. Saved template designs, Customer Journeys and reports stay at Mailchimp.
| Data | Result | What happens |
|---|---|---|
| Contacts | Imports | Subscribed, unsubscribed, cleaned and non-subscribed members of every audience import, merged into one contact when the same address is in two audiences. FNAME and LNAME become the name, other merge fields become custom fields, and address fields are split into parts. Archived contacts are not read. |
| Unsubscribes and consent | Imports | Unsubscribed and cleaned contacts import unsubscribed. Non-subscribed contacts, who never opted into marketing, import unsubscribed from marketing: campaigns skip them and transactional email still reaches them. Pending contacts never confirmed double opt-in and are left out. When two audiences disagree, the stricter state wins. |
| Suppressions (bounces and complaints) | Imports | Cleaned addresses import as bounce suppressions and each audience's abuse reports as complaint suppressions. |
| Topics | Imports | Groups become opt-in topics named after the group category and group (Interests: Product news), with each contact's choices. Hidden groups are skipped. |
| Sending domains | Imports | Verified domains are recreated as verifying with new DKIM keys, with each audience's default From address as a sender. |
| Tags and segments | Partly imports | Each tag becomes a static segment with its members; Mailchimp's API returns up to 50 tags per contact. Saved segments come over as a snapshot of today's members, and advanced segments are not read. With more than one audience, each audience also becomes a segment. |
| Templates | Partly imports | Mailchimp's API doesn't return the HTML of saved templates, so every draft campaign and your 25 most recent sent campaigns import as code-mode drafts instead. Exact merge tags are rewritten; *|IF:|* blocks and Mailchimp-only tags stay for you to review. Saved templates are listed by name only. |
| Webhooks | Partly imports | Audience webhooks import switched off with a new signing secret. subscribe, unsubscribe, profile, upemail and cleaned are mapped; campaign and the SMS events are not. |
| SMS numbers and consent | Partly imports | Mailchimp SMS numbers (US) become the contact's SMS number. An SMS opt-in imports only with the date Mailchimp recorded for it; without one the number arrives with no consent. SMS opt-outs always carry over. A phone merge field becomes the SMS number, with no consent. |
| API keys | Rebuild | Mailchimp keys stay at Mailchimp. Create Unitpost API keys instead, and revoke the Mailchimp key you used for the import. |
| Send history and analytics | Stays behind | Campaign reports, opens, clicks and sent campaigns older than the 25 most recent stay at Mailchimp. |
| Automations | Stays behind | Classic automations are listed in the preview with their email count; Customer Journeys have no read API. Rebuild them in Unitpost Automations, which are in early access, or send those emails from your code. |
| Forms, landing pages and integrations | Stays behind | Not readable through Mailchimp's API. Unitpost has no hosted signup forms, so new signups reach it through POST /api/v1/contacts. |
Before you start
- A Mailchimp API key: Profile → Extras → API keys → Create A Key. An Owner, Admin or Manager can create one.
- Mailchimp keys always have full access to the account; there is no read-only key. The importer only reads, and the last step revokes the key.
- A Unitpost API key for your app with
emails:send, pluscontacts:writeandsegments:writeif your signup form adds contacts, created in API keys. - A second Unitpost key for the migration steps, with
contacts:write,templates:writeandwebhooks:manage, saved asUNITPOST_MIGRATION_KEY. Keep it out of your app and revoke it once the move is done. - Access to your DNS host.
- If you text contacts: SMS on Unitpost is on paid plans only, and marketing texts need consent on record per contact.
The cut-over plan
Mailchimp keeps sending until the last step, so there is no gap in delivery.
Run the Migration Assistant
Paste the key in Settings → Migration. The preview counts what it found and lists what it leaves out: pending contacts, saved templates it can't read, advanced segments and your automations. Suppressions are written before contacts, so cleaned and complained addresses land unsubscribed.
Verify your domains next to Mailchimp
Open Domains and publish the records shown: MX and SPF on
unitpost.<your domain>and a DKIM TXT under_domainkey. They sit beside Mailchimp's DKIM records, so both stay authenticated while you move. Keep your single_dmarcrecord.Review merge tags and publish
Exact merge tags are already rewritten:
*|FNAME|*is{{first_name}},*|LNAME|*is{{last_name}},*|EMAIL|*is{{email}},*|UNSUB|*is{{unsubscribe_url}}, and your own merge fields map by tag. Unitpost fills flat{{name}}placeholders only, with no conditionals, so rewrite*|IF:…|*blocks,*|LIST:ADDRESS|*and*|ARCHIVE|*by hand. Imports land as marketing drafts with Unitpost's unsubscribe footer; publish withPATCH /api/v1/email/templates/{id}.Rebuild the saved templates you still use
Mailchimp's API doesn't share saved template HTML, and Mailchimp can't export drag-and-drop templates at all. For a custom-coded template, export its HTML in Mailchimp and paste it into a new template in code mode. Rebuild drag-and-drop ones in the visual editor.
Point signups at Unitpost
Mailchimp forms and landing pages keep adding people to Mailchimp. Send signups from your own form with
POST /api/v1/contacts, then add them to a segment or topic. Mailchimp'sstatus: "subscribed"is the default here. Unitpost has no built-in email double opt-in, so if you used Mailchimp's, confirm the address in your app before you create the contact.signup.ts// Before: Mailchimp await fetch("https://api.mailchimp.com/3.0/lists/" + AUDIENCE_ID + "/members", { method: "POST", headers: { Authorization: "Bearer " + process.env.MAILCHIMP_API_KEY, "content-type": "application/json" }, body: JSON.stringify({ email_address: "user@example.com", status: "subscribed", merge_fields: { FNAME: "Ada", LNAME: "Lovelace" }, }), }); // After: Unitpost import { Unitpost } from "unitpost"; const unitpost = new Unitpost(process.env.UNITPOST_API_KEY); const { error } = await unitpost.contacts.create({ email: "user@example.com", first_name: "Ada", last_name: "Lovelace", }); // 409 conflict: the contact already exists, which is fine for a signup. if (error && error.code !== "conflict") throw new Error(error.message); await unitpost.segments.addMember("seg_123", { contact: "user@example.com" });Switch transactional sends
Only if you send through Mailchimp Transactional (formerly Mandrill).
messages.sendbecomesemail.send:from_emailandfrom_namebecome onefromstring andtotakes addresses. For a stored template,merge_varsbecometemplate.variables. Over SMTP, change the host and credentials as in the SMTP table. Sends here accept anIdempotency-Key.send.ts// Before: Mailchimp Transactional import mailchimpTransactional from "@mailchimp/mailchimp_transactional"; const mailchimp = mailchimpTransactional(process.env.MAILCHIMP_TRANSACTIONAL_KEY); await mailchimp.messages.send({ message: { from_email: "hi@acme.com", from_name: "Acme", to: [{ email: "user@example.com", type: "to" }], subject: "Your receipt", html: receiptHtml, }, }); // After: Unitpost const { data, error } = await unitpost.email.send({ from: "Acme <hi@acme.com>", to: "user@example.com", subject: "Your receipt", html: receiptHtml, });Re-key and enable webhooks
Mailchimp posts audience events as form fields, signed with
X-Mailchimp-Signaturewhen signing is on. Unitpost posts JSON and signs it with awhsec_secret insvix-id,svix-timestampandsvix-signature. Rotate the secret in Webhooks to see it, update your handler, then setstatustoenabled.Check SMS consent before texting
Numbers whose opt-in had no date arrived without consent. Record consent where the contact gave it (
POST /api/v1/contacts/{id}/sms-consent) or ask them to confirm withPOST /api/v1/contacts/{id}/sms-opt-in-request. Mailchimp SMS opt-outs came over and stay in force.Test, move traffic, then revoke the key
Send each template with
POST /api/v1/email/templates/{id}/test-sendand firePOST /api/v1/webhooks/{id}/testat each endpoint. Send your next campaign from Unitpost to a small segment first, then to everyone. Then revoke the Mailchimp key under Profile → Extras → API keys.
Name changes
Same ideas, new names
| Mailchimp | Unitpost | Note |
|---|---|---|
| Audience | Contacts | Several audiences merge into one list; each also becomes a segment. |
| Tag | Segment (static) | |
| Saved segment | Segment (dynamic) | Imported as a static snapshot; rebuild the filter. |
| Group | Topic (opt-in) | |
| Merge field | Contact field | |
*|FNAME|* | {{first_name}} | |
| Cleaned contact | Suppression (bounce) | |
| Non-subscribed contact | Unsubscribed contact | Still gets transactional email. |
| Pending contact | Not imported | |
| Regular campaign | Campaign | |
| Customer Journey | Automations | In early access. |
| Mailchimp Transactional (Mandrill) | Transactional email | Same API, contacts and bill as campaigns. |
API calls
| Mailchimp API | Unitpost API | Note |
|---|---|---|
| POST /3.0/lists/{list_id}/members | POST /api/v1/contacts | |
| PUT /3.0/lists/{list_id}/members/{subscriber_hash} | PATCH /api/v1/contacts/{id or email} | Mailchimp's PUT adds or updates; here create and update are separate calls. |
| GET /3.0/lists/{list_id}/members | GET /api/v1/contacts | |
| POST /3.0/lists/{list_id}/members/{subscriber_hash}/tags | POST /api/v1/segments/{id}/contacts | |
| GET /3.0/lists/{list_id}/segments | GET /api/v1/segments | |
| GET /3.0/lists/{list_id}/interest-categories | GET /api/v1/email/topics | |
| POST /3.0/campaigns | POST /api/v1/email/campaigns | |
| POST /3.0/campaigns/{campaign_id}/actions/send | POST /api/v1/email/campaigns/{id}/send | |
| POST /3.0/lists/{list_id}/webhooks | POST /api/v1/webhooks | |
Transactional messages/send | POST /api/v1/email | |
Authorization: Bearer, or Basic with any username | Authorization: Bearer <api key> |
SMTP settings
| Mailchimp SMTP | Unitpost SMTP | Note |
|---|---|---|
| smtp.mandrillapp.com | smtp.unitpost.com | |
| Username: any string | Username unitpost | |
| Password: a Mailchimp Transactional API key | Password: a Unitpost API key with emails:send | |
| 25, 587, 2525 (STARTTLS) | 587, 2587, 25 (STARTTLS) | TLS 1.2 or later is required before login. |
| 465 (SSL) | 465, 2465 (implicit TLS) | |
X-MC-Template, X-MC-MergeVars headers | No equivalent | Send the finished message; subject and body come from the message itself. |
Webhook events
| Mailchimp event | Unitpost event | Note |
|---|---|---|
| subscribe | contact.created | |
| unsubscribe, profile, upemail | contact.updated | |
| cleaned | email.bounced | Mailchimp's is an audience change; ours fires per message. |
| campaign | No equivalent | Not mapped by the importer. |
| sms_subscribe, sms_unsubscribe, upsms, sms_campaign | No equivalent | Not mapped by the importer. |
| Transactional: send | email.sent | |
| Transactional: delivered | email.delivered | |
| Transactional: deferral, soft_bounce | email.delivery_delayed | A soft bounce that gives up is email.bounced. |
| Transactional: hard_bounce | email.bounced | |
| Transactional: spam | email.complained | |
| Transactional: open, click | email.opened, email.clicked | |
| Transactional: reject | email.suppressed | When the suppression list stops a send; other refusals are email.failed. |
| Transactional: unsub | contact.updated |
Before you cancel Mailchimp
- Export each audience as CSV, including archived contacts, which the importer doesn't read.
- Export the campaign reports you want to keep; send history doesn't move.
- Write down what each Customer Journey and classic automation does, with its timing.
- Export the HTML of every custom-coded template you still use.
- Delete Mailchimp's domain authentication records once no mail goes through Mailchimp. Keep
_dmarcand Unitpost's records. - Revoke the Mailchimp API key you used for the import.
Facts about Mailchimp checked 2026-11-05. Providers change their APIs and plans; check their docs before you rely on a detail.
Questions about leaving Mailchimp
Why didn't my saved Mailchimp templates come over?
What happens to pending and non-subscribed contacts?
Do my Customer Journeys come over?
Is it safe to give the importer a Mailchimp key?
Will my SMS subscribers come over?
Leaving Mailchimp?
Preview the migration on the free tier
Verify a domain, paste an API key, and see what would come across before anything is written.
