New from emailrHealthCheck Email iconHealthCheck Email
emailr_

Migrate from Mailchimp Transactional (Mandrill) to emailr_

Move from Mailchimp Transactional (Mandrill) to emailr_: map templates, merge vars, tags, metadata, rejection lists and webhooks without downtime.

Mailchimp Transactional iconMailchimp Transactionalemailr_

Mailchimp Transactional, formerly Mandrill, ties transactional email to a Mailchimp account. emailr_ runs transactional email, marketing broadcasts and shared inboxes on one platform, so you can move both halves of your email at your own pace.

This guide covers the @mailchimp/mailchimp_transactional client, merge vars, templates, rejection lists and webhooks.

The code change

A typical Mailchimp Transactional send call next to its emailr_ equivalent.

Before (Mailchimp Transactional)ts
import mailchimpFactory from '@mailchimp/mailchimp_transactional'

const mailchimp = mailchimpFactory(process.env.MANDRILL_API_KEY!)

await mailchimp.messages.send({
  message: {
    from_email: 'hello@mail.acme.com',
    to: [{ email: 'user@example.com', type: 'to' }],
    subject: 'Your receipt',
    html: '<p>Thanks for your order.</p>',
    tags: ['receipts'],
    metadata: { order_id: '123' },
  },
})
After (emailr_)ts
import { Emailr } from 'emailr'

const emailr = new Emailr({ apiKey: process.env.EMAILR_API_KEY! })

await emailr.emails.send({
  from: 'hello@mail.acme.com',
  to: 'user@example.com',
  subject: 'Your receipt',
  html: '<p>Thanks for your order.</p>',
  tags: { category: 'receipts', order_id: '123' },
})

Mailchimp Transactional concepts in emailr_

Use this table to find where each feature you rely on lives in emailr_.

Mailchimp Transactionalemailr_
Mandrill API keyAPI key (et_live_ prefix)
Sending domains (DKIM + SPF)Domain (DKIM TXT, SPF, send. MAIL FROM)
to: [{ email, type }]to, cc, bcc fields
Templates + template_content / merge varsTemplates (template_id + template_data)
tags array + metadatatags object
send_atscheduled_at (ISO 8601)
Rejection denylistContacts with subscribed: false
SubaccountsDomain workspaces
Inbound domains / routesInboxes and email.received webhooks

How to migrate from Mailchimp Transactional

  1. 01

    Create an API key

    Create a key under API keys in the dashboard. Keys start with et_live_ and are scoped to one domain workspace. Store it as EMAILR_API_KEY in your secret manager or environment, never in client-side code.

  2. 02

    Add and verify your sending domain

    In the dashboard, open Domains and add the domain you send from. A subdomain such as mail.yourcompany.com keeps transactional reputation separate from your corporate mailbox.

    Emailr generates the DNS records: a DKIM TXT record on the emailr._domainkey selector, an SPF TXT record, and an MX + TXT pair on the send subdomain used as the MAIL FROM / Return-Path. A DMARC record is optional but recommended.

    Mandrill uses the mandrill._domainkey selector; Emailr's selector is emailr._domainkey, so both can be live at once.

    Emailr's DKIM selector and send subdomain don't collide with other providers' records, so you can add them before cutting over and send from both providers during the migration. A domain can only have one SPF record: if one already exists, merge Emailr's include: into it instead of adding a second record.

  3. 03

    Swap the client

    Install the SDK and replace messages.send({ message }). The to array of { email, type } objects splits into to, cc and bcc. headers['Reply-To'] becomes reply_to_email, and tags plus metadata merge into one tags object.

    Attachments change from { type, name, content } to { contentType, filename, content }; content stays base64.

    Terminalbash
    npm install emailr
  4. 04

    Move templates and merge vars

    Replace messages.sendTemplate({ template_name, template_content, message: { global_merge_vars } }) with emails.send({ template_id, template_data }). Convert *|FNAME|* merge tags to {{FNAME}}.

    Emailr templates substitute flat {{variable}} tokens from template_data. There are no loops or conditionals, so pre-render lists and optional blocks in your code and pass the result as a variable.

  5. 05

    Move the rejection list

    Export your rejection denylist and import those addresses as contacts with subscribed: false. Broadcasts skip them automatically.

  6. 06

    Recreate your event webhooks

    Mandrill batches events into a mandrill_events form field. emailr_ posts one JSON event per request, so the handler gets simpler.

    Create a webhook of type transactional (delivery and engagement events), domain (verification status) or receiving (inbound email). Each webhook has its own signing secret. Every request carries an X-Emailr-Signature header: a hex-encoded HMAC-SHA256 of the raw request body. Verify it before trusting the payload.

    verify-webhook.tsts
    import { createHmac, timingSafeEqual } from 'node:crypto'
    
    // rawBody must be the exact request body string, before JSON.parse
    export function isValidEmailrWebhook(rawBody: string, signature: string | null) {
      if (!signature) return false
      const expected = createHmac('sha256', process.env.EMAILR_WEBHOOK_SECRET!)
        .update(rawBody)
        .digest('hex')
      const a = Buffer.from(expected)
      const b = Buffer.from(signature)
      return a.length === b.length && timingSafeEqual(a, b)
    }
    
    // const ok = isValidEmailrWebhook(body, req.headers.get('x-emailr-signature'))
    // const { event, data } = JSON.parse(body)  // e.g. event === 'email.bounced'

Webhook event mapping

emailr_ webhooks post one JSON event per request with an event name and a data object. Translate event names in your handler with this table.

Mailchimp Transactional eventemailr_ event
sendemail.sent
hard_bounceemail.bounced
spamemail.complained
rejectemail.failed
openemail.opened
clickemail.clicked

Things to watch for

  • Per-recipient merge_vars in a single call aren't supported; send one request per recipient or use Broadcasts with contact properties.
  • Mandrill's preserve_recipients and merge_language options have no equivalent.
  • If you used Mailchimp for marketing alongside Mandrill, you can move newsletters to emailr_ Broadcasts too, but it isn't required for the transactional switch.

Frequently asked questions

Do I need a Mailchimp account to use emailr_?

No. emailr_ is standalone. You need an emailr_ API key and a verified sending domain.

How do Mandrill merge tags translate?

Rewrite *|NAME|* as {{NAME}} in the template and pass the values in template_data. Contact properties such as first_name can also be resolved per recipient in broadcasts.

What replaces Mandrill subaccounts?

Domain workspaces. Each workspace has its own domain, templates, contacts and inboxes, and API keys are scoped to one workspace.

Can I schedule sends like send_at?

Yes. Pass scheduled_at with an ISO 8601 timestamp on the send request.

Ready to leave Mailchimp Transactional?

Create a free account, verify your domain and send your first email through emailr_ in minutes. No credit card required.