Setting up inbound email routing and parsing rules in Mailgun

If you’ve ever needed to process emails automatically—think contact forms, help desk tickets, or custom notifications—you know how messy inbound email routing can get. This guide is for developers, sysadmins, or anyone who wants to wrangle incoming email with Mailgun and actually have it do what you want. We'll walk through routing, parsing, and some real-world caveats Mailgun’s marketing doesn’t talk about.

No-nonsense. No hand-waving. Just what works.


Why Use Mailgun for Inbound Email?

Mailgun isn’t just for blasting newsletters. It’s one of the few email APIs that handles inbound mail, parses it, and can forward the results via webhooks. That means you can build stuff like:

  • A support mailbox that auto-creates tickets
  • Contact forms that don’t depend on sketchy PHP scripts
  • Custom notification pipelines

Sure, you could run your own mail server, but unless you enjoy pain, don’t. Mailgun handles the gnarly parts—SPF, DKIM, spam filtering, and parsing raw MIME messages—so you don’t have to.

Step 1: Set Up a Domain for Receiving

First, you need a domain or subdomain to receive mail. Don’t use your main domain for inbound mail parsing unless you really trust your code. A subdomain like inbound.yourdomain.com is safer and keeps things separate.

How to add an inbound domain:

  1. Go to Domains in Mailgun:
    In the dashboard, hit “Domains” and click “Add New Domain.”

  2. Pick a subdomain:
    For inbound, use something like inbound.yoursite.com. This keeps inbound and outbound mail separate.

  3. Update DNS records:
    Mailgun will give you MX, TXT, and maybe CNAME records.

  4. The MX record is critical; it tells the world to send mail to Mailgun.
  5. The TXT (SPF/DKIM) records help with authentication and spam filtering.
  6. DNS changes can take a while. If something isn’t working, double-check DNS propagation.

Pro tip:
Don’t skip DKIM/SPF. If you do, expect random failures and mail ending up in spam. Mailgun will still accept mail without them, but deliverability and security take a hit.

Step 2: Enable Inbound Email

You have to explicitly enable inbound routing for your domain.

  • In your domain settings, look for the “Receiving” section.
  • Flip the toggle or checkbox for “Enable Receiving.”

Mailgun doesn’t process inbound mail unless this is on. Easy to forget.

Step 3: Set Up Routes

Routes are the heart of inbound processing in Mailgun. They let you filter, forward, or parse emails based on conditions (like recipient, subject, etc).

How Routing Works

  • Every route has filters (when to trigger) and actions (what to do).
  • Filters are based on things like recipient address, subject, or headers.
  • Actions include forwarding to an email, sending to a webhook, storing, or stopping further processing.

Creating Your First Route

Let’s say you want to parse all emails sent to support@inbound.yoursite.com and POST them to your app.

  1. Go to Routes:
    In the Mailgun dashboard, head to “Receiving > Routes.”

  2. Add Route:
    Click “Create Route” (or similar—it moves around).

  3. Set the Filter:

  4. Expression Type: Most folks use “Match Recipient.”
  5. Recipient: support@inbound.yoursite.com

  6. Set the Action:

  7. Choose “Forward” and enter your webhook endpoint (e.g., https://yourapp.com/mailgun/inbound).
  8. You can add multiple actions—like storing a copy and forwarding to a backup email.

  9. Priority:
    Lower numbers run first. If you have overlapping routes, this matters. Default is usually fine unless you get fancy.

  10. Activate:
    Save/activate the route.

What works:
- Webhook forwarding is reliable and fast. - Filters are flexible—match by recipient, pattern, or headers. - You can chain actions for more complex flows.

What’s iffy:
- The UI is...functional, but not fun. Some settings are buried. - Debugging failed webhooks can be a pain—logs are there, but not exhaustive. - Mailgun sometimes lags on processing if you’re on a free plan.

Step 4: Parse Incoming Emails

When Mailgun forwards an inbound email to your webhook, it POSTs a payload with a bunch of fields. You get:

  • The plain text and HTML body
  • Attachments (as files or URLs)
  • Metadata (headers, subject, sender, etc.)

Payload Structure

Here’s what to expect in your POST handler:

json { "recipient": "support@inbound.yoursite.com", "sender": "bob@example.com", "subject": "Help needed", "body-plain": "This is the plain text body", "body-html": "

This is the HTML body

", "attachments": ["attachment-1", "attachment-2"], // ...plus raw MIME, headers, etc. }

Mailgun sends attachments as multipart files. Names like attachment-1, attachment-2, etc.

Common Gotchas

  • Attachments:
    You get files, but you have to save or process them yourself. They don’t stick around forever—grab them when you get the webhook.

  • Subject/Body Encoding:
    Watch out for weird encodings or character sets if you get lots of international mail.

  • Spam:
    Mailgun will forward obvious spam unless you add your own filters. Use X-Mailgun-Sflag or similar headers to spot flagged messages.

Ignore:
- The “Store and Notify” action unless you need a full mailbox archive (most don’t). - Parsing the raw MIME unless you have a very niche need.

Step 5: Handle the Webhook in Your App

Your endpoint needs to accept POST requests with multipart/form-data. Most frameworks handle this fine. Here’s a basic example in Python (Flask):

python from flask import Flask, request

app = Flask(name)

@app.route('/mailgun/inbound', methods=['POST']) def mailgun_inbound(): sender = request.form.get('sender') subject = request.form.get('subject') body = request.form.get('body-plain') # Handle attachments files = request.files for filename in files: file = files[filename] # Save or process file # Do something with the data return '', 200

Tips:

  • Return a 200 status quickly. Mailgun doesn’t retry failed webhooks forever.
  • Log everything, especially early on. Email formats are wild.
  • Secure your endpoint—don’t let random folks POST to it. Check the signature Mailgun sends, or whitelist IPs.

Step 6: Test, Debug, and Maintain

Don’t trust that it “just works.” Send test emails from different clients (Gmail, Outlook, etc.), with and without attachments. Look for:

  • Weird formatting
  • Broken parsing
  • Missing attachments
  • Spam slipping through

Debugging tips:

  • Use Mailgun’s logs to see what was received and how it was routed.
  • If you’re not getting webhooks, check your SSL certificates and firewall rules.
  • If you’re getting duplicate or missing emails, check route priorities and overlaps.

What to ignore:
- Over-optimizing for rare edge cases until you see them in real email traffic. - Relying on Mailgun’s spam filtering to be perfect. It isn’t.

When Mailgun Isn’t Enough

Mailgun is solid, but not perfect. Here’s where people run into trouble:

  • Strict webhook timeouts:
    If your handler is slow, Mailgun may give up before your code finishes.

  • Attachment size limits:
    If people send big files, you’ll hit limits (usually 25 MB per message).

  • Weird email clients:
    Some emails (especially from enterprise systems) are formatted in ways Mailgun’s parser doesn’t handle gracefully.

If you need more control, you may need to supplement Mailgun with your own parsing or filtering logic. But for 95% of use cases, it’s good enough.

Keep It Simple and Iterate

Setting up inbound email routing and parsing in Mailgun isn’t rocket science, but it does have sharp edges if you’re not careful. Start with the basics—route by recipient, parse the payload, and handle attachments. Once that’s working, layer in spam filtering, error handling, and whatever else you need.

Don’t overthink it. Get something working, then tweak as real emails come in. You’ll thank yourself later.