MIME Parsing: MailParse vs Mandrill Inbound

Compare MailParse and Mandrill Inbound for MIME Parsing. Features, performance, and developer experience.

Why MIME Parsing Matters for Inbound Email Pipelines

MIME parsing is the backbone of reliable inbound email processing. Every email that reaches your application is a complex MIME-encoded document that may contain nested multipart structures, alternate bodies, attachments, inline images, calendar invites, and character sets from around the world. If your parser mishandles boundaries, transfer encodings, or headers, you risk losing data, corrupting attachments, or misinterpreting user content.

Choosing the right service for mime parsing is not just about receiving a webhook. It is about decoding MIME-encoded messages to a predictable, structured format you can trust under real-world load. In this comparison, we look at how MailParse and Mandrill Inbound approach the problem, how they represent parts and attachments, and what it means for developer experience, performance, and reliability.

How MailParse Handles MIME Parsing

MailParse focuses on delivering a complete, structured representation of the raw RFC 5322 message. The service receives mail on instant addresses you provision, parses the raw MIME stream, and delivers a normalized JSON payload via webhook or a REST polling API. The goal is to eliminate guesswork by providing a consistent parts tree, accurate header decoding, and safe attachment handling.

Parsing and normalization

  • Full MIME tree: Each part is represented with content type, disposition, charset, content-transfer-encoding, filename, and size. Nested multipart/alternative and multipart/related structures are preserved as a tree.
  • Transfer decoding: Base64, quoted-printable, and 7bit/8bit are decoded server-side. You can request raw or decoded bytes per part.
  • Charset and RFC 2231: Parameter decoding for filenames and charsets, including RFC 2231 encoded parameters and RFC 2047 header encodings for subjects and names.
  • Header fidelity: Multi-valued headers are preserved in order, with raw and decoded forms. DKIM, ARC, and Authentication-Results headers are retained.
  • Inline image mapping: Content-ID references are resolved so you can map cid: links in HTML to attachment payloads.

Webhook and REST payload design

Webhook deliveries contain a canonical JSON envelope with idempotency keys and a message object. REST polling returns the same schema so your ingestion code stays identical regardless of transport.

{
  "id": "evt_01HV3Q3JK7K8DN7C4M9Z",
  "type": "inbound.message.received",
  "timestamp": 1712689442,
  "message": {
    "envelope": {
      "from": "alice@example.com",
      "to": ["inbox+project-42@app.example.dev"],
      "helo": "mail.example.com",
      "remote_ip": "203.0.113.10"
    },
    "headers": [
      {"name": "Subject", "value": "Quarterly Report - v2"},
      {"name": "Date", "value": "Fri, 05 Apr 2024 12:04:22 +0000"},
      {"name": "Message-ID", "value": "<abc123@example.com>"}
    ],
    "parts": [
      {
        "content_type": "multipart/alternative",
        "children": [
          {"content_type": "text/plain; charset=utf-8", "text": "Hello team...", "size": 1234},
          {"content_type": "text/html; charset=utf-8", "html": "<p>Hello team...</p>", "size": 1670}
        ]
      },
      {
        "content_type": "application/pdf",
        "disposition": "attachment",
        "filename": "report.pdf",
        "size": 348210,
        "content_id": null,
        "storage": {
          "type": "inline_base64",
          "data": "JVBERi0xLjQKJeLjz9MKMiAwIG9iago8PC9M..."
        }
      }
    ],
    "meta": {
      "dkim_verified": true,
      "spam": {"score": 0.2}
    }
  }
}

Key points that simplify integration:

  • Parts tree is explicit. You always know where plaintext and HTML bodies live and can iterate attachments safely.
  • Options to request inline base64 attachment data or signed, time-limited file URLs for larger payloads.
  • Idempotency: Each delivery includes a stable message id and SHA-256 content hash for deduping.
  • Webhook retries with exponential backoff and signed HMAC headers. You can replay via REST using the event id.

How Mandrill Inbound Handles MIME Parsing

Mandrill Inbound, part of Mailchimp's transactional email product, delivers inbound email via webhooks configured on routes. It returns a JSON event that includes convenient fields like text, html, and attachments, and it also provides the raw MIME message for custom processing.

Inbound webhook structure

Your endpoint receives a POST with a form parameter named mandrill_events that is a JSON array. For inbound email, each event includes event: "inbound" and an msg payload:

[
  {
    "event": "inbound",
    "ts": 1712689442,
    "msg": {
      "raw_msg": "Return-Path: <alice@example.com>\r\nReceived: ...",
      "headers": {"Subject": "Quarterly Report - v2", "Date": "Fri, 05 Apr 2024 12:04:22 +0000"},
      "from_email": "alice@example.com",
      "from_name": "Alice",
      "to": [["inbox@app.example.dev", null]],
      "subject": "Quarterly Report - v2",
      "text": "Hello team...\n",
      "html": "<p>Hello team...</p>",
      "attachments": {
        "report.pdf": {"name": "report.pdf", "type": "application/pdf", "content": "JVBERi0xLjQ..."}
      },
      "images": {
        "logo.png": {"name": "logo.png", "type": "image/png", "content": "iVBORw0KGgo..."}
      },
      "spam_report": {"score": 0.2}
    }
  }
]

This design is approachable and works well if you primarily need flattened fields. Notable characteristics:

  • Convenience fields: text and html are extracted for you, so simple integrations can ignore MIME details.
  • Attachments and images: Delivered inline as base64-encoded content keyed by filename. Inline CID mapping is not explicitly linked, but you can correlate by filename if you control message creation.
  • Raw MIME: The raw_msg field contains the RFC 5322 message. If you need the full parts tree, you can re-parse with a MIME library.
  • Routing: You define routes based on recipient patterns. Inbound requires a Mailchimp account and leverages Mandrill's transactional infrastructure.

Side-by-Side Comparison of MIME Parsing Features

Capability MailParse Mandrill Inbound
Raw RFC 5322 access Available via metadata or export Available in raw_msg
Structured MIME parts tree First-class JSON representation of nested parts Flattened fields (text, html), attachments provided, re-parse raw_msg for full tree
Content-Transfer-Encoding decoding Server-side decoding for base64 and quoted-printable with raw access option Attachments and bodies returned decoded, raw available for custom parsing
Charset and RFC 2231 filename decoding Built-in normalization and UTF-8 output Common cases handled, advanced parameter decoding requires custom parsing of raw_msg
Inline image CID to HTML mapping Explicit mapping with content-id references Images provided, mapping by CID may require parsing raw_msg
TNEF/Winmail.dat extraction Built-in extraction to parts and files Not built-in - parse from raw_msg if needed
Header fidelity and multi-value preservation Ordered, multi-value headers preserved as array with decoded and raw Headers object provided, full fidelity available via raw_msg
Webhook retries and idempotency keys Idempotent event ids and content hashes, exponential backoff Retries supported by Mandrill's webhook system, dedupe logic is app responsibility
Standalone usage No external transactional account required Requires Mailchimp transactional (Mandrill) account

Code Examples: Developer Experience for MIME Parsing

Example 1 - Receiving a structured parts tree via webhook

This example shows how to handle a normalized parts tree, traverse attachments, and extract the preferred body.

// Node.js (Express)
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json({ limit: '20mb' }));

function verifySignature(req) {
  const signature = req.header('X-Signature');
  const secret = process.env.WEBHOOK_SECRET;
  const body = JSON.stringify(req.body);
  const digest = crypto.createHmac('sha256', secret).update(body).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(digest, 'hex'));
}

function pickPreferredBody(parts) {
  for (const p of parts) {
    if (p.content_type.startsWith('multipart/')) {
      const body = pickPreferredBody(p.children || []);
      if (body) return body;
    }
  }
  // simple preference: HTML over text
  const html = parts.find(p => (p.content_type || '').startsWith('text/html'));
  if (html) return { type: 'html', value: html.html };
  const text = parts.find(p => (p.content_type || '').startsWith('text/plain'));
  if (text) return { type: 'text', value: text.text };
  return null;
}

app.post('/webhooks/inbound', (req, res) => {
  if (!verifySignature(req)) return res.status(401).send('invalid signature');

  const { id, message } = req.body;
  const body = pickPreferredBody(message.parts || []);

  const attachments = [];
  function collectAttachments(parts) {
    for (const p of parts) {
      if ((p.disposition || '').toLowerCase() === 'attachment') attachments.push(p);
      if (p.children) collectAttachments(p.children);
    }
  }
  collectAttachments(message.parts);

  // Persist the event id to guard against duplicates
  // saveInboundEvent(id, message, body, attachments)

  res.status(200).json({ received: true, id, attachments: attachments.length, bodyType: body?.type });
});

app.listen(3000, () => console.log('listening on 3000'));

Example 2 - Mandrill Inbound: decoding attachments and optionally re-parsing raw MIME

Mandrill Inbound posts mandrill_events as a single form parameter. This example shows how to handle the flattened fields and when to use raw_msg for advanced parsing.

// Node.js (Express)
const express = require('express');
const multer = require('multer');
const { simpleParser } = require('mailparser'); // optional if you need full parts tree

const app = express();
const upload = multer();
app.post('/mandrill/inbound', upload.none(), async (req, res) => {
  const eventsJson = req.body['mandrill_events'];
  if (!eventsJson) return res.status(400).send('missing mandrill_events');

  const events = JSON.parse(eventsJson);
  for (const ev of events) {
    if (ev.event !== 'inbound') continue;
    const msg = ev.msg;

    // Use convenience fields
    const subject = msg.subject;
    const text = msg.text;
    const html = msg.html;

    // Decode attachments
    const attachments = [];
    if (msg.attachments) {
      for (const [filename, meta] of Object.entries(msg.attachments)) {
        const buffer = Buffer.from(meta.content, 'base64');
        attachments.push({ filename, contentType: meta.type, size: buffer.length, buffer });
      }
    }

    // If you need nested parts, re-parse the raw MIME
    let partsTree = null;
    if (msg.raw_msg) {
      const parsed = await simpleParser(msg.raw_msg);
      // parsed has text, html, attachments, headers, and can include alternatives
      partsTree = {
        text: parsed.text,
        html: parsed.html,
        attachments: parsed.attachments.map(a => ({ filename: a.filename, cid: a.cid, contentType: a.contentType, size: a.length }))
      };
    }

    // saveInbound(ev.ts, subject, text, html, attachments, partsTree)
  }

  res.status(200).send('ok');
});

app.listen(3001, () => console.log('mandrill inbound listening on 3001'));

Takeaways for Mandrill Inbound:

  • Use text and html for basic cases and quick wins.
  • When you need canonical MIME structure, parse raw_msg with a robust library, then map results to your schema.
  • Be mindful of payload size and request limits when processing inline attachments.

Performance and Reliability in Real-World MIME Parsing

Mime-parsing at scale is a streaming problem as much as a correctness problem. Large attachments, high concurrency, and malformed messages are the rule rather than the exception. Here are core considerations and how each platform addresses them.

Streaming and memory usage

  • Streaming decode: A good parser streams attachment bodies to storage instead of buffering entire files. This prevents memory spikes for 25 MB PDF files and 30 MB images.
  • Boundary repair: Messages from older MTAs often have malformed boundaries or mixed line endings. Parsers should be tolerant within RFC guidelines and fall back gracefully.
  • Line-by-line normalization: Correct handling of CRLF normalization avoids off-by-one boundary detection errors.

MailParse emphasizes streaming and boundary tolerance, exposing attachment content as signed URLs or inline base64 on request. This enables you to control memory footprint at the webhook level and fetch large files out-of-band when needed.

Mandrill Inbound typically inlines attachments in the webhook body as base64. This is convenient for small files but can increase webhook payload size. If you expect large attachments at volume, plan for higher request sizes, longer processing times at your endpoint, and stricter reverse proxy limits. When performance or granularity matters, use raw_msg and a streaming parser on your side.

Error handling and retries

  • Idempotency keys: Deduplicate deliveries via stable event ids and content hashes.
  • Exponential backoff: Webhooks should retry with backoff and signed requests so you can safely handle transient outages.
  • Partial failure resilience: If attachment storage fails, the system should mark the event as incomplete and support replay.

Mandrill Inbound uses Mandrill's webhook retry mechanism. Ensure your app is idempotent, particularly when attachments are large or your endpoint times out. Persist the original event or raw_msg reference so you can retry processing without data loss.

Edge cases to test

  • Nested multipart/alternative with related parts for inline images in HTML bodies.
  • TNEF winmail.dat extraction to expose calendar invites and embedded files.
  • Non-UTF-8 charsets and RFC 2231 filenames with multi-segment encodings.
  • Multiple duplicate headers like Received, repeated Reply-To, and folded long header values.
  • Messages with both Content-Disposition: inline and attachment anomalies.

Regardless of platform, build a corpus of edge-case emails and automate regression tests. For a broader view of your stack, check the Email Infrastructure Checklist for SaaS Platforms and consider ideas in Top Inbound Email Processing Ideas for SaaS Platforms.

Verdict: Which Is Better for MIME Parsing?

If your application needs a precise, developer-friendly representation of complex MIME structures, MailParse is the stronger choice for mime-parsing. You get a normalized parts tree, reliable charset and parameter decoding, and options for streamed or inline attachments without managing your own parsing pipeline.

Mandrill Inbound is a solid option if you already run on Mailchimp's transactional email and your use case is satisfied by flattened text, html, and attachment arrays. When you need deeper control, plan on re-parsing raw_msg with a MIME library and building your own mapping logic.

For teams designing long-lived email ingestion, prioritize accuracy and fidelity. A predictable parts tree reduces downstream complexity across ticketing, CRM ingestion, and workflow automations. To explore how parsing feeds product features, review Top Email Parsing API Ideas for SaaS Platforms.

FAQ

What is MIME parsing and why is it critical for inbound email?

MIME parsing is the process of decoding a MIME-encoded email into its structured components: headers, bodies, inline images, and attachments. Accurate parsing ensures you do not lose information in nested multipart structures, you decode non-UTF-8 charsets correctly, and you map inline Content-ID images into HTML. Without reliable mime parsing, automations break when messages deviate from happy paths.

Does Mandrill Inbound give me the full parts tree?

Mandrill Inbound provides convenient fields like text, html, and base64 attachments. It also includes raw_msg, the complete RFC 5322 message. If you need the full nested structure, you can re-parse raw_msg using a MIME library and then build your own normalized representation.

How should I handle large attachments safely?

Prefer streaming over buffering. If your provider inlines attachments as base64, increase request body limits and process in a streaming fashion where possible. Offload storage to object storage with signed URLs and process attachments asynchronously. Validate content type and size before committing to storage to prevent abuse.

What are common pitfalls in mime-parsing?

Common pitfalls include mishandling quoted-printable soft line breaks, failing to decode RFC 2231 filenames, collapsing multi-value headers into a single string, ignoring multipart/related relationships for cid-linked images, and treating 8bit bodies as ASCII which corrupts non-English content. Build tests for each case and track parser versions.

How do I validate deliverability and infrastructure around inbound parsing?

Parsing quality only matters if messages arrive reliably. Verify SPF, DKIM, and DMARC alignment, monitor MX health, and rate-limit or graylist as necessary. For a practical checklist that pairs well with mime-encoded message handling, see the Email Deliverability Checklist for SaaS Platforms.

Ready to get started?

Start parsing inbound emails with MailParse today.

Get Started Free