---
title: "Migrate from Mailchimp to Unitpost"
description: "Leave Mailchimp in order: import audiences, tags, groups, bounces and campaign content with one key, rewrite merge tags, re-verify DNS, then move signups."
url: https://www.unitpost.com/migration/mailchimp
section: Migration
updated: 2026-11-07
---
# Migrate from Mailchimp to Unitpost

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.

Contacts, suppressions, topics, templates and more come over in one import.

## What comes with you

| 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`, plus `contacts:write` and `segments:write` if your signup form adds contacts, created in [API keys](/dashboard/api-keys).
- A second Unitpost key for the migration steps, with `contacts:write`, `templates:write` and `webhooks:manage`, saved as `UNITPOST_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

### 1. Run the Migration Assistant

Paste the key in [Settings → Migration](/dashboard/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.

### 2. Verify your domains next to Mailchimp

Open [Domains](/dashboard/email/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 `_dmarc` record.

### 3. 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 with `PATCH /api/v1/email/templates/{id}`.

### 4. 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.

### 5. 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's `status: "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.

```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" });
```

### 6. Switch transactional sends

Only if you send through Mailchimp Transactional (formerly Mandrill). `messages.send` becomes `email.send`: `from_email` and `from_name` become one `from` string and `to` takes addresses. For a stored template, `merge_vars` become `template.variables`. Over SMTP, change the host and credentials as in the SMTP table. Sends here accept an `Idempotency-Key`.

```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,
});
```

### 7. Re-key and enable webhooks

Mailchimp posts audience events as form fields, signed with `X-Mailchimp-Signature` when signing is on. Unitpost posts JSON and signs it with a `whsec_` secret in `svix-id`, `svix-timestamp` and `svix-signature`. Rotate the secret in [Webhooks](/dashboard/webhooks) to see it, update your handler, then set `status` to `enabled`.

### 8. 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 with `POST /api/v1/contacts/{id}/sms-opt-in-request`. Mailchimp SMS opt-outs came over and stay in force.

### 9. Test, move traffic, then revoke the key

Send each template with `POST /api/v1/email/templates/{id}/test-send` and fire `POST /api/v1/webhooks/{id}/test` at 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.

## 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 `_dmarc` and Unitpost's records.
- Revoke the Mailchimp API key you used for the import.

Facts about Mailchimp checked 2026-11-05.

## FAQ

### Why didn't my saved Mailchimp templates come over?

Mailchimp's API returns a saved template's name and settings but not its HTML, and Mailchimp can't export drag-and-drop templates at all. The importer brings the content of your draft campaigns and 25 most recent sent campaigns instead, which is usually the email you'd reuse.

### What happens to pending and non-subscribed contacts?

Pending contacts never confirmed Mailchimp's double opt-in, so Mailchimp can't email them and they are left out. Non-subscribed contacts never opted into marketing, so they import unsubscribed from marketing: they still get receipts and other transactional email, and no campaigns.

### Do my Customer Journeys come over?

No. Mailchimp's API can trigger a journey but can't read one. Classic automations are listed in the preview with their email count. Rebuild them in Unitpost Automations, which are in early access, or send those emails from your code.

### Is it safe to give the importer a Mailchimp key?

The importer only reads, but Mailchimp keys always carry full account access and can't be limited. Create a key for the migration, and revoke it under Profile → Extras → API keys when you're done.

### Will my SMS subscribers come over?

US SMS numbers do. An opt-in carries over only with the date Mailchimp recorded for it, and an opt-out always carries over. Texting from Unitpost needs a paid plan.

## Related

- [Migrate from MailerLite to Unitpost](https://www.unitpost.com/migration/mailerlite): Leave MailerLite in order: import subscribers, groups, segments, bounces and spam reports, rebuild designs from plain-text drafts, re-verify DNS, move signups.
- [Migrate from Kit to Unitpost](https://www.unitpost.com/migration/kit): Leave Kit in order: import subscribers, tags, bounces, broadcasts and sequence emails with a V4 key, rebuild segments and automations, then move signups.
- [Migrate from beehiiv to Unitpost](https://www.unitpost.com/migration/beehiiv): Leave beehiiv in order: copy the suppression list by hand, import subscribers, publications, segments and posts, rewrite merge tags, then move signups.
