Why Email Authentication Matters for Inbound Parsing
Email authentication is more than an outbound deliverability topic. If your app ingests email - support ticketing, user-generated content, automated workflows, or SaaS integrations - you need to know who actually sent a message before you trust it. Attackers spoof domains and users, mailing lists rewrite headers, and forwarders can break SPF alignment. A parsing service that does not evaluate SPF, DKIM, and DMARC leaves you to build and maintain that critical logic yourself.
Inbound email-authentication protects data integrity, prevents spoofing, and lets you make policy decisions like rejecting untrusted messages, flagging them for review, or gating automations. The most useful implementation validates SPF,, DKIM,, and DMARC on receipt, interprets alignment rules, understands ARC for forwarded mail, then exposes structured results in your webhook or API so you can decide how to act per message.
If your product roadmap includes automated routing, approvals, or content publishing from email, review your provider's authentication stack alongside MIME parsing. For a broader operational checklist, see the Email Infrastructure Checklist for SaaS Platforms and Top Inbound Email Processing Ideas for SaaS Platforms.
How MailParse Handles Email Authentication
MailParse evaluates authentication as a first-class step in the inbound pipeline and exposes machine-friendly results in both webhooks and REST API. The goal is to make decisions trivial: label a message as trusted, quarantined, or rejected without screen-scraping headers.
SPF evaluation
- Captures the connecting IP, SMTP HELO, and envelope-from (mfrom) used to deliver the message.
- Evaluates SPF per RFC 7208 and records the mechanism that matched. Supports recursive lookups with safe limits and DNS caching.
- Exposes scope and alignment against the visible From domain to support DMARC decisions.
DKIM verification
- Verifies all DKIM signatures on the message, not just the first valid one. This matters for forwarders and multi-signer messages.
- Respects canonicalization and l= body length tags, with strict body hashing. Publishes per-signature results, selectors, and covered headers.
- DNS fetches are cached and time-limited to prevent latency spikes.
DMARC policy and alignment
- Computes DMARC alignment using the RFC 7489 algorithm with support for organizational domain discovery.
- Parses DMARC policy records including p, sp, pct, and evaluates results for each message. Even though DMARC reporting is outbound, a correct inbound verdict is central to trust.
- Exposes both pass or fail and whether the pass was via SPF alignment, DKIM alignment, or both.
ARC chain handling for forwarded mail
- If a message has an ARC chain, verifies the ARC-SEAL and ARC-Message-Signature across the set.
- Exposes chain depth and final verdict so you can decide whether to trust an intermediary's assessment when SPF fails due to forwarding.
Webhook and API shape
Authentication data is included in webhook payloads and REST responses under a concise schema that stays stable across versions. You do not need to parse raw headers to make decisions.
{
"id": "evt_9f2e...b1",
"timestamp": "2026-04-24T12:03:45.284Z",
"from": {
"header": "Alice <alice@example.com>",
"address": "alice@example.com",
"domain": "example.com"
},
"envelope": {
"mail_from": "alice@example.com",
"helo": "mx.example.com",
"rcpt_to": ["ingest@yourapp.io"],
"client_ip": "203.0.113.54"
},
"auth": {
"spf": {
"result": "pass",
"domain": "example.com",
"scope": "mfrom",
"ip": "203.0.113.54",
"aligned": true,
"mechanism": "ip4:203.0.113.0/24"
},
"dkim": [
{
"domain": "example.com",
"selector": "s1",
"result": "pass",
"aligned": true,
"canonicalization": "relaxed/relaxed",
"covered_headers": ["from","to","subject","date"]
}
],
"dmarc": {
"result": "pass",
"aligned": true,
"policy": "reject",
"org_domain": "example.com",
"reason": "dkim_aligned"
},
"arc": {
"result": "none",
"chain": 0
}
},
"subject": "Quarterly report",
"text": "Hi team,...",
"html": "<p>Hi team,...</p>",
"attachments": [],
"raw_mime_url": "https://api.your-inbox.com/messages/msg_123/raw"
}
Webhook requests are signed with HMAC SHA-256 using your signing secret. Each payload carries an idempotency key and timestamp so you can verify authenticity and deduplicate.
How Mailgun Inbound Routing Handles Email Authentication
Mailgun Inbound Routing focuses on flexible routing and posting message content to your HTTP endpoint. The route payload includes parsed fields and the original MIME if you enable storage. For authentication, the service does not compute DMARC alignment for you and does not include explicit SPF or DKIM verdicts in the standard route parameters. If you need those, you have two options:
- Request the raw MIME and parse the
Authentication-Resultsheader added by upstream MTAs, then perform your own DKIM, SPF, and DMARC checks if that header is absent or untrusted. - Run your own verification against the raw message using libraries like dkimpy or OpenDKIM.
Typical route payload fields include sender, recipient, message-headers, body-plain, timestamp, token, and signature. Webhooks are signed and you can verify integrity using your API key. This design is flexible but puts the burden of inbound authentication on your application.
If your use case demands structured authentication outcomes, you will end up parsing headers or validating signatures yourself on top of mailgun inbound routing (also written as mailgun-inbound-routing in some docs). This is workable and common, especially if you already standardize on Mailgun's stack for outbound, but it increases implementation time and CPU cost within your app.
Side-by-Side Comparison of Email Authentication Features
| Capability | MailParse | Mailgun Inbound Routing |
|---|---|---|
| SPF validation on inbound | Built-in result and alignment exposed in JSON | Not included in route fields - manual or parse headers |
| DKIM verification on inbound | Built-in for all signatures with per-sig details | Not included - verify via raw MIME in your app |
| DMARC evaluation and alignment | Built-in with org-domain discovery and policy | Not included - implement evaluation yourself |
| ARC chain verification | Built-in with chain depth and verdict | Not included |
Structured auth.* fields in webhook |
Yes | No |
| Authentication-Results header preserved | Yes - parsed and available in raw headers | Yes - available if you fetch raw MIME |
| Webhook signature scheme | HMAC SHA-256 with timestamp and idempotency key | HMAC SHA-256 with timestamp and token |
| Edge-case handling for forwarded mail | ARC aware and DMARC-friendly decisions exposed | Depends on your custom logic |
| Scaling cost of auth logic | Included in service | Runs in your app - more compute at scale |
Code Examples
Example: Verify webhook and use authentication verdicts from MailParse
This Node.js snippet shows how to verify the request signature and branch logic on DMARC and DKIM alignment. The payload structure mirrors the earlier example.
import crypto from 'crypto';
import express from 'express';
const app = express();
app.use(express.json({ limit: '25mb' }));
const SIGNING_SECRET = process.env.SIGNING_SECRET;
function verifySignature(req) {
const timestamp = req.header('X-Timestamp');
const sig = req.header('X-Signature');
const body = JSON.stringify(req.body);
const h = crypto.createHmac('sha256', SIGNING_SECRET);
h.update(timestamp + '.' + body);
const digest = 'sha256=' + h.digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig || ''), Buffer.from(digest));
}
app.post('/inbound', (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send('invalid signature');
}
const msg = req.body;
// Simple policy: require DMARC pass via DKIM alignment or accept ARC-backed forwarded mail
const dmarc = msg.auth?.dmarc?.result;
const arcOk = msg.auth?.arc?.result === 'pass';
const dkimAligned = (msg.auth?.dkim || []).some(s => s.result === 'pass' && s.aligned);
if (dmarc === 'pass' && dkimAligned) {
// trusted path
processTrusted(msg);
} else if (arcOk) {
processForwarded(msg);
} else {
flagForReview(msg);
}
res.send('ok');
});
app.listen(3000);
Example: Verify Mailgun route signature and compute DKIM/DMARC yourself
First verify Mailgun's route signature, then validate DKIM using dkimpy on the raw MIME. Once you have DKIM and SPF results, compute DMARC alignment in your code.
# Flask + dkimpy example
from flask import Flask, request, abort
import hmac, hashlib, dkim
API_KEY = b'your-mailgun-api-key'
app = Flask(__name__)
def valid_signature(timestamp, token, signature):
mac = hmac.new(API_KEY, (timestamp + token).encode('utf-8'), hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, signature)
@app.route('/mg-inbound', methods=['POST'])
def mg_inbound():
ts = request.form.get('timestamp','')
token = request.form.get('token','')
sig = request.form.get('signature','')
if not valid_signature(ts, token, sig):
abort(401)
# If you enabled MIME storage, get the URL or raw content
raw_mime = request.files.get('message').read()
# DKIM verification
dkim_result = dkim.verify(raw_mime)
# Compute SPF separately using your SMTP logs or headers
# Then evaluate DMARC alignment with your own function
# Example decision
if dkim_result:
pass_message(raw_mime)
else:
quarantine(raw_mime)
return 'ok'
If you choose to keep Mailgun Inbound Routing for flexibility, encapsulate your SPF, DKIM, and DMARC logic into a module so it is testable and fast. Cache DNS lookups aggressively and set timeouts to avoid backpressure.
Performance and Reliability
Latency and throughput
Authentication checks are DNS heavy. Cache layers and concurrency matter. The Mailgun route delivers a payload quickly because it does not perform DMARC evaluation for inbound by default. That shifts work to your app. If you roll your own checks, add a DNS cache and keep-per-query timeouts short to avoid latency spikes.
MailParse performs SPF, DKIM, DMARC, and optional ARC verification before completing the parse, then publishes a unified result. This keeps your webhooks lean and predictable because you do not need to fetch additional data or call out to DNS per request. Results stream with the rest of the parsed JSON, and the raw MIME is still available for audit.
Webhook delivery and retries
Both platforms sign webhooks and retry on non-2xx responses. If you are at scale, design for at-least-once processing. Use the provided event id or construct your own idempotency key to avoid duplicate work. Some teams report that Mailgun route deliveries can bunch during regional incidents or high volume, which may produce short bursts of retries. Size your worker pool accordingly and apply backpressure policies.
Edge cases that impact authentication
- Forwarded mail: SPF will usually fail. ARC verification can rescue trust if the forwarder is trusted. If you rely on mailgun inbound routing, you will need to evaluate ARC yourself. With a structured ARC verdict, you can accept forwarded mail with confidence.
- Mailing lists and rewriters: DKIM may fail if headers are modified. Look for multiple signatures and accept aligned ones where possible. Keep detailed per-signature results to avoid false negatives.
- Subdomain policy and org domain: DMARC sp records differ from p at the organizational domain. Evaluate both correctly or you will misclassify mail from subdomains.
- Header folding and MIME quirks: Canonicalization differences can break DKIM unexpectedly. Prefer relaxed canonicalization support and expose which headers were covered by the signature.
Scaling cost matters too. On Mailgun you are responsible for computing SPF, DKIM, DMARC at ingest time, so your CPU, DNS, and maintenance costs grow with inbound volume. On the other side, the structured approach centralizes the heavy lifting and simplifies your app logic.
For a broader operational perspective on reliability, see the Email Deliverability Checklist for SaaS Platforms.
Verdict: Which Is Better for Email Authentication?
If your primary criterion is robust, structured inbound email authentication that you can use immediately in code, MailParse is the better fit. You get SPF, DKIM, DMARC, and ARC results alongside parsed MIME, with alignment already computed. This shortens time to production and reduces the surface area of custom code you have to own.
If you already use Mailgun for outbound and need simple inbound routing with raw MIME, mailgun inbound routing remains a solid option. Just budget engineering time for dkimpy or OpenDKIM integration, SPF evaluation, and a correct DMARC implementation. At high volume, also account for the compute and DNS load that inbound authentication introduces when you run it yourself.
Most teams that need to take automated actions from email - creating issues, provisioning resources, or publishing content - benefit from a provider that handles authentication at the platform layer and exposes verdicts as JSON. That makes policy decisions easy, auditable, and consistent.