Why Email to JSON Decides Developer Velocity
Email-to-JSON is a small phrase with a big impact. When inbound email messages arrive, your application needs predictable, clean JSON that handles every quirk of MIME: multipart bodies, weird character encodings, inline images with Content-ID references, threaded replies, and the long tail of headers vendors and clients add. If the conversion is inconsistent, your workflows for ticketing, lead capture, user-generated content, and automation slow down, produce edge case bugs, and require one-off patches.
A well designed email to JSON pipeline should do three things: convert raw email into a normalized JSON envelope that suits application code, deliver that JSON reliably at scale, and make it easy to reprocess or re-fetch when something goes wrong. The details matter: body selection, attachment presentation, header fidelity, signature verification, and idempotency determine how quickly a developer ships and how safely systems operate in production.
This comparison focuses only on converting email to JSON and delivering the structured result to your app. It highlights how two different approaches - webhook only versus webhook plus REST polling - shape the developer experience and operational reliability.
How MailParse Handles Email to JSON
MailParse converts raw MIME into a normalized JSON document purpose built for application consumption. The service provisions instant inbound addresses, accepts messages, parses them into structured fields, and delivers them through a signed webhook or a REST polling API. The goal is to keep your code simple: parse once, consume everywhere.
JSON shape and normalization
- Envelope and identifiers:
id,received_at(ISO 8601 UTC),directionset to inbound, and a stableevent_idfor idempotency. - Addresses: Canonicalized
from,to,cc,bcc, andreply_to, each presented as objects withnameandemail, plusaddress_stringfor the original raw value. - Headers: A
headersobject preserves all header fields with unfolded lines and canonical keys, plusheaders_rawas an array of {name, value} for exact fidelity. - Bodies:
textandhtmlare decoded and normalized to UTF-8, with quoted-printable and Base64 decoding, and automatic dominant body selection when one is missing. - Attachments:
attachmentsarray withfilename,content_type,size,inline,content_id, asha256checksum, and a time-limiteddownload_url. Large items can be streamed from storage without loading into memory. - Inline image resolution: A
cid_mapmakes mappingcid:references inhtmlstraightforward. - Original MIME: Either
raw_mime_urlfor pull-on-demand, orraw_mime_base64when you require the complete source inline. - Content safety: Optional HTML defanging, and attachment content-type validation with easy allow-listing.
Delivery options and reliability
- Signed webhooks: POSTs carry an
X-SignatureHMAC SHA-256 header keyed by your secret. Non-2xx responses trigger exponential backoff retries with jitter, and a dead-letter queue when a threshold is exceeded. - REST polling API: Pull messages at your pace using
GET /v1/messages?status=ready&limit=100, retrieve bodies withGET /v1/messages/{id}, acknowledge usingPOST /v1/messages/{id}/ack, and optionally requeue with/retryif downstream work fails. - Idempotency: Each event contains a stable
event_id, enabling safe de-duplication in your application store. - Character encodings: All bodies are normalized to UTF-8 with correct decoding of Shift-JIS, ISO-2022-JP, and other legacy charsets. Long header folding and RFC 2231 filename parameters are respected.
The result is a consistent email-to-JSON experience that lets your ingestion, automation, or support pipelines focus on business logic rather than MIME edge cases.
How Postmark Inbound Handles Email to JSON
Postmark Inbound focuses on a straightforward webhook that posts a parsed JSON payload to your endpoint whenever a message arrives. It is a durable approach that integrates well with queue-backed workers and serverless endpoints where webhooks are the native pattern.
What the inbound JSON typically includes
- Addresses and envelope:
Fromplus a richerFromFullobject,ToandToFullarrays,CcandCcFullwhen present, and identifiers such asMessageIDandDate. - Bodies:
TextBodyandHtmlBodyas strings already decoded for typical consumption. - Attachments: An
Attachmentsarray with Base64 content, filename, content type, length, and an optionalContentIDfor inline use. - Headers: A collection of original headers, useful for custom routing, threading, and domain-specific integrations.
Delivery model
- Webhook only: Postmark's inbound posts to your configured URL. Your service returns a 200-level status to acknowledge, otherwise Postmark retries delivery. Your code is responsible for idempotency, storage, and any replay you require.
- Operational notes: If your endpoint is offline, you rely on Postmark's retry behavior until the message is either delivered or dropped according to their policies. There is no REST polling option for pulling messages on demand.
For many applications already built around webhooks, this design is perfectly serviceable. Teams that prefer pull-based control need to build their own storage layer and worker pollers.
Side-by-Side Comparison of Email to JSON Features
| Capability | MailParse | Postmark Inbound |
|---|---|---|
| Delivery methods for parsed JSON | Webhook and REST polling | Webhook only |
| Webhook authentication | HMAC signature header with secret rotation | Webhook to your URL, you implement verification as needed |
| Idempotency token | Stable event_id, documented idempotent retries |
Recommended via MessageID in your app |
| Attachments presentation | Signed download URLs or direct Base64, plus checksum | Base64 in payload with metadata |
| Inline image mapping | Explicit cid_map for easy HTML rewriting |
Use ContentID fields to resolve cid: references |
| Character encoding normalization | All text normalized to UTF-8 | Text and HTML provided as parsed strings |
| Original MIME access | Optional raw_mime_url or inline Base64 |
Parsed payload focus, store raw MIME yourself if needed |
| Reprocessing | Requeue via API, fetch again anytime | Replay by re-sending to your own systems, or manual tools |
| Error handling | Retry with exponential backoff, dead-letter queue, metrics | Automatic retries on non-2xx, governed by Postmark policies |
Code Examples: Developer Experience for Email to JSON
Webhook verification and consumption with a pull fallback
This example shows receiving a signed webhook, verifying the HMAC, and acknowledging. If your endpoint is down, use the REST polling API to fetch pending messages until the webhook resumes.
// Node.js (Express) - verify HMAC signature and process message
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.WEBHOOK_SECRET;
app.use(express.json({ limit: '20mb' }));
app.post('/webhooks/inbound', (req, res) => {
const signature = req.get('X-Signature') || '';
const payload = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', SECRET)
.update(payload, 'utf8')
.digest('hex');
if (signature !== expected) {
return res.status(401).send('invalid signature');
}
const msg = req.body;
// Example fields
// msg.id, msg.received_at, msg.from.email, msg.subject, msg.text, msg.html
// msg.attachments[i].download_url for streaming
// Idempotently store by msg.event_id or msg.id
// enqueue work, then acknowledge
res.status(204).send();
});
app.listen(3000);
# REST polling loop to drain messages during maintenance
# Fetch ready messages
curl -s -H "Authorization: Bearer $API_TOKEN" \
"/v1/messages?status=ready&limit=50" | jq '.items[].id' | while read id; do
id=$(echo $id | tr -d '"')
# Retrieve full payload
curl -s -H "Authorization: Bearer $API_TOKEN" "/v1/messages/$id" > "/tmp/$id.json"
# ...process...
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" "/v1/messages/$id/ack" > /dev/null
done
Consuming Postmark Inbound webhooks
Postmark Inbound delivers a JSON payload to your endpoint. A 200-level response acknowledges successful processing. Your code should treat the payload as untrusted input, validate sizes, and perform idempotent storage keyed by MessageID.
// Node.js (Express) - Postmark Inbound webhook
import express from 'express';
import { Buffer } from 'node:buffer';
const app = express();
app.use(express.json({ limit: '20mb' }));
app.post('/webhooks/postmark-inbound', async (req, res) => {
const evt = req.body;
// Basic fields
const from = evt.FromFull?.Email || evt.From;
const subject = evt.Subject || '';
const text = evt.TextBody || '';
const html = evt.HtmlBody || '';
// Attachments
const attachments = (evt.Attachments || []).map(a => ({
filename: a.Name,
contentType: a.ContentType,
length: a.ContentLength,
contentId: a.ContentID,
// Decode on demand to avoid memory pressure
toBuffer: () => Buffer.from(a.Content || '', 'base64')
}));
// Idempotent store keyed by evt.MessageID
// If processing is successful:
res.status(200).send('ok');
});
app.listen(3001);
# Example of decoding a single attachment from Postmark Inbound using Python
import base64, json, sys
evt = json.load(sys.stdin)
for att in evt.get('Attachments', []):
raw = base64.b64decode(att.get('Content', ''))
with open(att.get('Name', 'attachment.bin'), 'wb') as f:
f.write(raw)
Performance and Reliability for Email-to-JSON Pipelines
Edge cases that matter in production
- Malformed MIME boundaries: Real world senders sometimes violate RFCs. A robust parser recovers missing or duplicated boundaries instead of dropping parts.
- Character sets and encodings: Messages from legacy systems use ISO-2022-JP, Shift-JIS, or Windows-1252. Automatic, correct decoding into UTF-8 prevents mojibake in your database and search index.
- Large attachments and streaming: Inline Base64 is convenient, but unbounded buffering in your app can cause memory spikes. Prefer download URLs or streamed processing, and set explicit size limits in your handlers.
- Inline images with
cid:references: Clean mapping between Content-ID headers and attachment records avoids broken images in rendered HTML. - Reply trimming: When building threaded discussions, choose a consistent strategy for detecting quoted replies and signature delimiters, and fall back to full bodies when confidence is low.
Webhook only versus webhook plus polling
Webhook only systems are elegant when everything is online. When a maintenance window or an upstream incident takes your ingestion offline, you have two choices: temporarily buffer with wider retries or drop payloads into a dead-letter queue and re-inject later. A REST polling option simplifies these scenarios. Your workers can pull a controlled batch size, process at steady-state capacity, and acknowledge only after persistence succeeds. This reduces the blast radius of outages and simplifies backfills after schema changes.
Both patterns can be made reliable, but the operational burden shifts to your team if you need pull semantics. If you prefer preventing webhook exposure from isolated networks, polling from inside your VPC while leaving email acceptance external is also attractive.
For deeper planning around endpoints, retries, and message flow, see Email Infrastructure Checklist for SaaS Platforms and Top Inbound Email Processing Ideas for SaaS Platforms.
Verdict: Which Is Better for Email to JSON?
If your team is comfortable with a webhook only design and you need a concise inbound payload with parsed bodies and attachments, Postmark Inbound delivers that with minimal ceremony. It aligns well with applications that already operate a durable webhook ingestion tier.
If you want both push and pull control, consistent normalization with optional raw MIME access, and built in idempotency plus reprocessing via API, MailParse is the stronger fit. The email-to-JSON conversion is comprehensive, and the REST polling option eliminates a class of operational headaches for teams that prefer controlled batch consumption.
In short, match the delivery model to your architecture. When in doubt, favor the approach that lets you test, replay, and scale without adding custom plumbing. If you are planning a broader foundation for inbound and outbound, the Email Deliverability Checklist for SaaS Platforms is a helpful companion.
FAQ
What does Email to JSON include by default?
At minimum you want normalized sender and recipient objects, subject, decoded plain text and HTML bodies, a complete header set, and an attachments list with filename, content type, size, inline flag, and a reference you can use without storing the entire file in memory. A stable identifier and idempotency token are important for safe retries. Access to the original MIME source is valuable for audits, legal archiving, and manual debugging.
How should I handle attachments safely in my application?
Stream attachments to storage rather than decoding Base64 into memory when files are large. Enforce an allow-list of content types, validate that claimed types match magic bytes where possible, and consider virus scanning before further processing. For inline images referenced by cid:, rewrite HTML to point at your own safe URLs. Avoid persisting untrusted HTML directly into customer visible surfaces without sanitization.
Can I process replies and preserve threading reliably?
Use Message-ID, In-Reply-To, and References headers to associate messages to threads. Heuristically trim quoted replies by matching common delimiters, but always keep the full untrimmed body for auditing. Decide whether to present the trimmed reply or the full text based on confidence in the heuristic and your product's UX.
What if my inbound endpoint is behind a firewall or temporarily offline?
Two common patterns work well. First, allow webhooks only from known IPs to a small ingress tier and place a queue directly behind it. Second, pull new messages from a REST polling API on a schedule and acknowledge only when storage succeeds. MailParse supports both models, which makes migrations and maintenance windows much simpler to manage.
How do I prototype an inbound flow without exposing a public URL?
Use a REST polling workflow or a tunneling tool like ngrok during early development. Polling lets you iterate locally without opening ports. When you are ready to go to production, switch to webhooks for push-latency or keep polling if it matches your control and security requirements. For more ideas on building end to end flows, explore Top Email Parsing API Ideas for SaaS Platforms.