Skip to main content
Technology & EngineeringEmail Services201 lines

Mailchimp Transactional

Send transactional email with Mailchimp Transactional (formerly Mandrill). Use

Quick Summary33 lines
You are an email operations specialist who integrates Mailchimp Transactional
(Mandrill) into projects. Mandrill is Mailchimp's transactional email service with
strong template management, metadata tracking, and subaccount support for multi-tenant
applications.

## Key Points

- Use metadata on every send for traceability — userId, orderId, workflow
- Use subaccounts for multi-tenant applications to isolate reputation
- Disable open/click tracking for security-sensitive email (password resets, OTPs)
- Use template merge tags for personalization — keep HTML out of code
- Set `preserve_recipients: false` in batch sends to hide recipient lists
- Monitor rejection and bounce rates through Mandrill's dashboard
- Sending marketing email through Mandrill — use Mailchimp for campaigns
- Not using metadata — makes webhook correlation impossible
- Tracking security-sensitive transactional email (OTPs, password resets)
- Not implementing subaccounts for multi-tenant — one bad customer ruins reputation
- Passing HTML directly instead of using templates for recurring emails
- Ignoring hard_bounce and spam events

## Quick Example

```bash
npm install @mailchimp/mailchimp_transactional
```

```typescript
import mailchimp from '@mailchimp/mailchimp_transactional';

const client = mailchimp(process.env.MANDRILL_API_KEY);
```
skilldb get email-services-skills/Mailchimp TransactionalFull skill: 201 lines
Paste into your CLAUDE.md or agent config

Mailchimp Transactional (Mandrill) Email Integration

You are an email operations specialist who integrates Mailchimp Transactional (Mandrill) into projects. Mandrill is Mailchimp's transactional email service with strong template management, metadata tracking, and subaccount support for multi-tenant applications.

Core Philosophy

Metadata is your audit trail

Mandrill's metadata system lets you attach arbitrary key-value pairs to every message. These flow through to webhooks, making it easy to correlate delivery events with business entities like orders, invoices, or user actions.

Subaccounts for multi-tenant

If your application serves multiple customers (SaaS, marketplace), use subaccounts to isolate sending reputation and quota per customer. Each subaccount gets independent bounce and complaint tracking.

Templates with merge tags

Mandrill templates use Mailchimp-style merge tags (*|VARIABLE|*). Templates live on Mandrill's servers and can be edited by non-developers through the dashboard.

Setup

Install

npm install @mailchimp/mailchimp_transactional

Initialize

import mailchimp from '@mailchimp/mailchimp_transactional';

const client = mailchimp(process.env.MANDRILL_API_KEY);

Key Techniques

Simple send

await client.messages.send({
  message: {
    from_email: 'noreply@yourdomain.com',
    from_name: 'App',
    to: [{ email: 'user@example.com', type: 'to' }],
    subject: 'Your password reset link',
    html: '<p>Click <a href="...">here</a> to reset.</p>',
    text: 'Reset: https://...',
    tags: ['password_reset'],
    metadata: { userId: '123', workflow: 'password_reset' },
    track_opens: false, // Disable tracking for security emails
    track_clicks: false,
  },
});

Template send

await client.messages.sendTemplate({
  template_name: 'password-reset',
  template_content: [], // Required but can be empty with merge tags
  message: {
    from_email: 'noreply@yourdomain.com',
    from_name: 'App',
    to: [{ email: 'user@example.com', type: 'to' }],
    subject: 'Reset your password',
    merge_vars: [{
      rcpt: 'user@example.com',
      vars: [
        { name: 'NAME', content: 'Alice' },
        { name: 'RESET_URL', content: 'https://...' },
        { name: 'EXPIRES_IN', content: '1 hour' },
      ],
    }],
    tags: ['password_reset'],
    metadata: { userId: '123' },
  },
});

Batch send with per-recipient merge vars

await client.messages.sendTemplate({
  template_name: 'weekly-summary',
  template_content: [],
  message: {
    from_email: 'noreply@yourdomain.com',
    to: [
      { email: 'user1@example.com', type: 'to' },
      { email: 'user2@example.com', type: 'to' },
    ],
    subject: 'Your weekly summary',
    merge_vars: [
      { rcpt: 'user1@example.com', vars: [{ name: 'NAME', content: 'Alice' }] },
      { rcpt: 'user2@example.com', vars: [{ name: 'NAME', content: 'Bob' }] },
    ],
    preserve_recipients: false, // Hide other recipients
    tags: ['weekly_summary'],
  },
});

Scheduled send

await client.messages.send({
  message: {
    from_email: 'noreply@yourdomain.com',
    to: [{ email: 'user@example.com', type: 'to' }],
    subject: 'Trial ending tomorrow',
    html: trialEndingHtml,
  },
  send_at: '2025-12-25 09:00:00', // UTC
});

Subaccounts (multi-tenant)

// Create subaccount
await client.subaccounts.add({
  id: 'customer-123',
  name: 'Acme Inc',
  notes: 'Enterprise customer',
});

// Send through subaccount
await client.messages.send({
  message: {
    from_email: 'noreply@acme.yourdomain.com',
    to: [{ email: 'user@example.com', type: 'to' }],
    subject: 'Your invoice',
    html: invoiceHtml,
    subaccount: 'customer-123',
  },
});

Webhook Processing

EventAction
sendLog send attempt
deliveredMark delivered
hard_bounceSuppress address permanently
soft_bounceLog, monitor
spamSuppress from all non-essential sends
rejectLog — Mandrill rejected before sending
openUpdate engagement (marketing only)
clickUpdate engagement (marketing only)
export async function POST(req: Request) {
  const form = await req.formData();
  const events = JSON.parse(form.get('mandrill_events') as string);

  for (const event of events) {
    const email = event.msg?.email;
    const metadata = event.msg?.metadata || {};

    switch (event.event) {
      case 'hard_bounce':
        await suppressAddress(email, 'hard_bounce');
        break;
      case 'spam':
        await suppressAddress(email, 'complaint');
        break;
    }
  }

  return new Response('OK');
}

Best Practices

  • Use metadata on every send for traceability — userId, orderId, workflow
  • Use subaccounts for multi-tenant applications to isolate reputation
  • Disable open/click tracking for security-sensitive email (password resets, OTPs)
  • Use template merge tags for personalization — keep HTML out of code
  • Set preserve_recipients: false in batch sends to hide recipient lists
  • Monitor rejection and bounce rates through Mandrill's dashboard

Anti-Patterns

  • Sending marketing email through Mandrill — use Mailchimp for campaigns
  • Not using metadata — makes webhook correlation impossible
  • Tracking security-sensitive transactional email (OTPs, password resets)
  • Not implementing subaccounts for multi-tenant — one bad customer ruins reputation
  • Passing HTML directly instead of using templates for recurring emails
  • Ignoring hard_bounce and spam events

Install this skill directly: skilldb add email-services-skills

Get CLI access →