Introduction
Email infrastructure is where product requirements meet internet plumbing. For full-stack developers working across frontend, backend, and infrastructure, it is a uniquely cross-disciplinary space. You need DNS skills for MX records, SMTP fluency for relays, a solid grasp of MIME to parse messages, and API know-how to deliver structured JSON into application backends. The reward is big: inbox-driven features like helpdesk ticketing, invoice ingestion, and automated workflows that start the moment a message lands on your domain.
This guide lays out a practical path to build scalable email-infrastructure with MX records, SMTP relays, and API gateways. It focuses on inbound email, parsing, and delivery patterns aligned with how modern full-stack teams ship code. You will find architecture advice, code-level patterns, and production-ready considerations like observability, retries, and compliance.
Email Infrastructure Fundamentals for Full-Stack Developers
MX records and message routing
Mail exchange (MX) records tell the world where to deliver mail for your domain. A typical configuration involves one or more MX records pointing to your inbound mail gateway. Priority values control fallback order. Production setups often use:
- Dedicated MX hosts per region or provider for resilience.
- Ephemeral or plus-addressed inboxes for multi-tenant routing, for example
tenant+ticket-123@yourdomain.com. - Subdomain scoping for different apps, such as
inbound.example.comfor testing andmail.example.comfor production.
Plan DNS TTLs to support rollouts. Lower values let you fail over faster during migrations. Document the relationship between MX providers and your SMTP ingress, since it affects TLS, IP reputation, and spam filtering behavior.
SMTP relay vs inbound SMTP vs API handoff
For inbound pipelines you have three broad choices:
- Run your own inbound SMTP listener on ports 25 or 587. This gives maximum control but requires handling SMTP sessions, TLS, and anti-abuse.
- Use a managed MX and inbound handoff that pushes accepted messages to your webhook or storage. This reduces operational overhead.
- Hybrid where you accept via SMTP then publish to an internal message bus. Helpful if you need custom filtering or PGP/S/MIME decryption at the edge.
Outbound SMTP relays are a separate concern, though they often live alongside inbound services. Keep the two responsibilities isolated in code and infrastructure to simplify security and capacity planning.
MIME parsing and structured JSON
Modern emails are MIME containers that can include multipart text, HTML, inline images, attachments, and nested messages. Your application rarely needs raw RFC 5322 text. It needs structured JSON, for example:
{
"from": {"address": "alice@example.com", "name": "Alice"},
"to": [{"address": "support@yourdomain.com"}],
"subject": "Issue with order #4129",
"text": "Plain text fallback...",
"html": "<p>Full details</p>",
"attachments": [
{"filename": "invoice.pdf", "contentType": "application/pdf", "size": 148321, "id": "att-01"},
{"filename": "screenshot.png", "contentType": "image/png", "size": 98321, "cid": "image001.png@01"}
],
"headers": {"Message-Id": "<m1@mx>", "In-Reply-To": "<prev@mx>"},
"messageId": "m1@mx",
"receivedAt": "2026-04-16T10:05:33Z"
}
Plan for encoding complexities: quoted-printable, base64, and non-UTF-8 charsets. Decide whether to inline small attachments in JSON or stream to object storage and reference by ID. For most apps, store the EML as an immutable artifact and keep normalized JSON for fast access.
Webhooks vs API polling
Webhooks offer push-based delivery with low latency. They require public endpoints, signature verification, retries, and idempotency. REST polling is pull-based and can be easier behind firewalls but increases latency and complexity around backfill logic. Many teams use webhooks for primary delivery and polling as backup during incidents. Whichever you choose, design the receiver to be stateless and idempotent, then use a queue to absorb spikes.
Trust boundaries and authentication
- SPF, DKIM, and DMARC: Evaluate sender authenticity to reduce spoofing. Do not rely solely on these for security decisions, but use them to guide scoring and filtering.
- DNSSEC and MTA-STS: Improve transport security and DNS integrity for your MX hosts.
- Webhook signatures: Verify HMAC signatures with key rotation. Reject unsigned or mismatched timestamps.
- Attachment scanning: Integrate malware and file-type checks before processing content.
Practical Implementation
Reference architecture
A robust inbound pipeline for full-stack developers looks like this:
- MX and Ingress: MX points to your inbound gateway. The gateway accepts mail via SMTP, validates size limits, and enforces TLS.
- Parser: Convert MIME to normalized JSON, store the raw EML and attachments in object storage, and emit an event with references.
- Delivery: Push JSON to a webhook or expose it via REST for polling. Include retry logic with exponential backoff and dead-letter queues.
- Application: Your webhook handler validates signatures, writes to a queue or log, and triggers domain logic like ticket creation or invoice extraction.
- Observability: Correlation IDs, structured logs, and metrics for latency, throughput, and failure rates.
Configuring DNS for inbound
Create MX records pointing to the provider or host handling inbound SMTP. If you use a subdomain like inbound.example.com to limit blast radius, add an MX record for that subdomain and provision TLS certificates accordingly. Keep SPF for outbound domains separate from inbound subdomains to avoid unintended sender policies.
Webhook handler pattern
Use a stateless handler that authenticates requests, stores data immutably, and emits an event for downstream processing. Example in Node.js using Express:
import crypto from "crypto";
import express from "express";
const app = express();
app.use(express.json({ limit: "10mb" }));
function verifySignature(req, secret) {
const signature = req.get("X-Signature");
const ts = req.get("X-Timestamp");
if (!signature || !ts) return false;
const payload = ts + "." + JSON.stringify(req.body);
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}
app.post("/webhooks/inbound-email", async (req, res) => {
if (!verifySignature(req, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("invalid signature");
}
// Idempotency key based on messageId + accountId
const key = req.body.messageId + ":" + req.body.accountId;
const exists = await hasProcessed(key); // implement via Redis or database unique constraint
if (exists) return res.status(200).send("ok");
await storeRaw(req.body.emlUrl); // download and archive EML
await storeJson(req.body); // persist normalized JSON
await enqueue("inbound-email", req.body); // push to queue for async processing
res.status(202).send("accepted");
});
app.listen(3000);
Key points:
- Enforce size limits on JSON bodies and attachment downloads.
- Use an idempotency key derived from stable identifiers like
messageIdand recipient address. - Decouple IO-heavy work with a queue and process downstream asynchronously.
Worker pattern
Workers consume events and implement domain logic. For example, turn emails into support tickets or extract invoice totals:
async function processInboundEmail(evt) {
const { html, text, attachments, headers } = evt;
// Heuristics: reply detection and threading
const threadId = headers["In-Reply-To"] || headers["References"] || evt.messageId;
// Simple content fallback
const body = html ? await sanitizeHtml(html) : text;
// Attachment routing
for (const att of attachments) {
if (att.contentType === "application/pdf") {
await scanForMalware(att.url);
await storeAttachment(att.url, "invoices/");
}
}
await upsertTicket({ threadId, from: evt.from, subject: evt.subject, body });
}
For long-running tasks like OCR on PDFs, hand off to background jobs with visibility timeouts and retries. Use dead-letter queues to capture poisoned messages and alert on them.
Tools and Libraries
Language and framework choices
- Node.js:
nodemailerfor SMTP,mailparserorpostal-mimefor parsing,expressorfastifyfor webhooks,bullmqfor queues. - Python: built-in
emailandmailboxmodules,aiosmtpdfor SMTP servers,fastapifor webhooks,celeryorrqfor workers. - Go:
github.com/emersion/go-messagefor MIME,github.com/emersion/go-smtpfor SMTP servers,fiberorchifor webhooks,segmentio/kafka-goorredisfor queues.
Infrastructure and observability
- Ingress: Postfix or OpenSMTPD if self-hosting, otherwise managed MX. Cloud email routing can forward to HTTP endpoints or storage buckets.
- Storage: Object storage for EML and attachments. Use content-addressed keys to deduplicate.
- Queues: SQS, Pub/Sub, Kafka, or NATS. Pick a system that your team already operates.
- Telemetry: OpenTelemetry for distributed traces and metrics, structured logs with correlation IDs per message.
Hosted parsing and delivery
If you prefer to avoid running SMTP servers and parsers, a hosted service like MailParse can provide instant inboxes, MIME-to-JSON parsing, and delivery via webhooks or REST polling. Evaluate signature verification, SLA, retry semantics, max message sizes, and how raw EML and attachments are exposed for auditing.
Common Mistakes Full-Stack Developers Make with Email Infrastructure
- Trusting headers without verification: Never trust
FromorReply-Toblindly. Validate SPF, DKIM, and DMARC and store results with the message. Use them as signals, not absolute truth. - Skipping idempotency: Retries happen. Use unique constraints or idempotency keys based on
messageIdplus recipient to avoid duplicate tickets or orders. - Inline processing in webhooks: Doing OCR or virus scanning in the HTTP request handler causes timeouts. Offload heavy work to workers and respond 202 Accepted quickly.
- Dropping non-UTF-8 content: Normalize charsets and handle quoted-printable and base64 correctly. Fallback gracefully when parts are malformed.
- Ignoring large attachments: Enforce size limits, stream downloads, and quarantine or reject too-large files with clear bounce messages.
- Insufficient monitoring: Without metrics on acceptance rate, parse errors, and webhook failures, issues linger. Add alerts on dead-letter growth and repeated retries.
- PII leakage in logs: Never log full bodies or attachments. Scrub or hash sensitive data and restrict access to raw EML.
Advanced Patterns for Production-Grade Email Processing
Address strategies for multi-tenancy
Use sub-addressing and subdomains to segment tenants and workflows:
- Plus addressing:
tenant+case-123@yourdomain.comencodes context in the mailbox and simplifies routing. - Per-tenant subdomains:
acme.inbound.yourdomain.commaps cleanly to tenant IDs and IAM policies in storage. - Ephemeral addresses for one-time captures like RFP submissions or job applications, garbage-collecting inactive inboxes on a schedule.
Backpressure, retries, and exactly-once semantics
Email is best-effort transport. Design for at-least-once delivery and embrace idempotency:
- Accept messages at the edge fast, then stream to a durable queue.
- Use exponential backoff with jitter and a max retry cap. Move permanently failing messages to a dead-letter queue for manual triage.
- Store EML and metadata immutably so you can replay processing without data loss.
- Keep webhook handlers stateless so they can scale horizontally behind an API gateway or load balancer.
Content extraction and enrichment
- Threading: Compute a conversation key from
Message-Id,In-Reply-To, andReferences. Fallback to subject normalization while avoiding "Re:" noise. - Text normalization: Strip quoted replies and signatures with heuristics to focus on the user's new content.
- Attachment pipelines: Offload PDF OCR and image processing to background jobs with GPU or vectorized libraries where appropriate.
- Compliance flags: Tag messages with jurisdictional markers derived from sender domains and IPs for downstream policy enforcement.
Security and integrity
- Encrypted storage: Encrypt EML and attachments at rest with KMS-managed keys and bucket policies scoped per tenant.
- PGP and S/MIME: Decrypt content at the edge only when keys are available and audit every decryption event.
- Webhook verification: Rotate HMAC secrets, enforce short timestamp windows, and use allowlists or mTLS where possible.
- MTA-STS and TLS-RPT: Publish policies to enforce TLS and receive reports on delivery problems.
Use-case specific pipelines
Tailor pipelines to your product domain:
- Inbound Email Processing for Helpdesk Ticketing | MailParse - Auto-create tickets, map replies to threads, and respect agent-only notes by parsing bounce and auto-reply headers.
- Email Parsing API for Compliance Monitoring | MailParse - Normalize messages, preserve tamper-evident EML, and feed compliance rules engines with structured headers and authentication results.
Each use case benefits from a shared core: verified ingestion, MIME-to-JSON parsing, immutable storage, and resilient delivery. Apply domain-specific transformations after this core to keep your architecture modular.
Conclusion
Email infrastructure blends internet standards with product pragmatism. As a full-stack developer, you already straddle frontend, backend, and ops. Use that advantage to design pipelines that start at MX records and end in domain events that your app understands. Keep ingestion simple and secure, parse MIME reliably, store everything immutably, and deliver structured JSON through webhooks or polling behind a queue. With a disciplined approach, email becomes a dependable event source rather than a support burden.
FAQ
How should I choose between webhooks and REST polling for inbound email?
Prefer webhooks for low latency and simpler flow control. They push messages as they arrive and scale horizontally behind an API gateway. Use REST polling if your environment cannot expose public endpoints or needs strict firewalling. Many teams implement webhooks as primary and short-interval polling as fallback during incidents.
What is the best way to store raw emails and attachments?
Store the original EML and attachments in object storage with content-addressed keys, then keep normalized JSON records referencing those blobs. This approach enables reliable reprocessing, auditing, and compliance checks. Avoid embedding large attachments directly in JSON payloads. Instead, stream them and enforce size limits.
How do I prevent duplicate processing?
Use idempotency keys derived from stable identifiers like messageId plus recipient. Enforce a unique constraint at the database level or maintain a short TTL cache in Redis. Also make your domain operations idempotent so that retries cannot double-create tickets or orders even if the idempotency cache is missed.
How can I improve deliverability and trust for inbound flows?
Publish correct MX records and ensure TLS with a modern cipher suite. Evaluate SPF, DKIM, and DMARC for each message and store their results. Implement MTA-STS to enforce TLS in transit and set up TLS-RPT to receive delivery reports. Apply malware scanning and restrict dangerous file types at the edge.
When is a hosted email parsing service a good fit?
When your team wants to focus on product features rather than SMTP operations and MIME edge cases. A hosted option like MailParse offers instant addresses, secure webhooks, and structured JSON delivery so you can concentrate on application logic. Validate signature schemes, retry policies, and how raw EML access is provided before committing.