Migrate from Mailchimp to Unitpost

A Mailchimp audience and campaigns importing as Unitpost contacts and code templates, with saved designs left to rebuild

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.

DataResultWhat happens
ContactsImportsSubscribed, 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 consentImportsUnsubscribed 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)ImportsCleaned addresses import as bounce suppressions and each audience's abuse reports as complaint suppressions.
TopicsImportsGroups become opt-in topics named after the group category and group (Interests: Product news), with each contact's choices. Hidden groups are skipped.
Sending domainsImportsVerified domains are recreated as verifying with new DKIM keys, with each audience's default From address as a sender.
Tags and segmentsPartly importsEach 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.
TemplatesPartly importsMailchimp'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.
WebhooksPartly importsAudience 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 consentPartly importsMailchimp 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 keysRebuildMailchimp keys stay at Mailchimp. Create Unitpost API keys instead, and revoke the Mailchimp key you used for the import.
Send history and analyticsStays behindCampaign reports, opens, clicks and sent campaigns older than the 25 most recent stay at Mailchimp.
AutomationsStays behindClassic 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 integrationsStays behindNot 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.
  • 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

Mailchimp keeps sending until the last step, so there is no gap in delivery.

  1. 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.

  2. 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 _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.

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

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

Name changes

Same ideas, new names

MailchimpUnitpostNote
AudienceContactsSeveral audiences merge into one list; each also becomes a segment.
TagSegment (static)
Saved segmentSegment (dynamic)Imported as a static snapshot; rebuild the filter.
GroupTopic (opt-in)
Merge fieldContact field
*|FNAME|*{{first_name}}
Cleaned contactSuppression (bounce)
Non-subscribed contactUnsubscribed contactStill gets transactional email.
Pending contactNot imported
Regular campaignCampaign
Customer JourneyAutomationsIn early access.
Mailchimp Transactional (Mandrill)Transactional emailSame API, contacts and bill as campaigns.

API calls

Mailchimp APIUnitpost APINote
POST /3.0/lists/{list_id}/membersPOST /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}/membersGET /api/v1/contacts
POST /3.0/lists/{list_id}/members/{subscriber_hash}/tagsPOST /api/v1/segments/{id}/contacts
GET /3.0/lists/{list_id}/segmentsGET /api/v1/segments
GET /3.0/lists/{list_id}/interest-categoriesGET /api/v1/email/topics
POST /3.0/campaignsPOST /api/v1/email/campaigns
POST /3.0/campaigns/{campaign_id}/actions/sendPOST /api/v1/email/campaigns/{id}/send
POST /3.0/lists/{list_id}/webhooksPOST /api/v1/webhooks
Transactional messages/sendPOST /api/v1/email
Authorization: Bearer, or Basic with any usernameAuthorization: Bearer <api key>

SMTP settings

Mailchimp SMTPUnitpost SMTPNote
smtp.mandrillapp.comsmtp.unitpost.com
Username: any stringUsername unitpost
Password: a Mailchimp Transactional API keyPassword: 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 headersNo equivalentSend the finished message; subject and body come from the message itself.

Webhook events

Mailchimp eventUnitpost eventNote
subscribecontact.created
unsubscribe, profile, upemailcontact.updated
cleanedemail.bouncedMailchimp's is an audience change; ours fires per message.
campaignNo equivalentNot mapped by the importer.
sms_subscribe, sms_unsubscribe, upsms, sms_campaignNo equivalentNot mapped by the importer.
Transactional: sendemail.sent
Transactional: deliveredemail.delivered
Transactional: deferral, soft_bounceemail.delivery_delayedA soft bounce that gives up is email.bounced.
Transactional: hard_bounceemail.bounced
Transactional: spamemail.complained
Transactional: open, clickemail.opened, email.clicked
Transactional: rejectemail.suppressedWhen the suppression list stops a send; other refusals are email.failed.
Transactional: unsubcontact.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. 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?

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.

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.