Email to JSON for Notification Routing | MailParse

How to use Email to JSON for Notification Routing. Practical guide with examples and best practices.

Introduction: From Email to JSON for Reliable Notification Routing

Email-to-JSON conversion turns unpredictable MIME messages into a clean, structured payload your application can trust. With a normalized JSON schema, you can evaluate headers, subject patterns, sender domains, and message bodies to drive notification routing decisions in Slack, Microsoft Teams, PagerDuty, or custom webhooks. Instead of scraping text with brittle regexes, you parse once, then route with confidence. The result is faster incident visibility, reduced noise, and fewer missed alerts.

Many teams still depend on email for critical signals: build failures, security advisories, cloud spend spikes, or customer escalation notices. Bridging these emails into chat and on-call systems is straightforward when each message arrives as a consistent JSON document. This guide explains how to implement an end-to-end pipeline that receives messages, transforms them into JSON, then routes notifications based on content and context.

Why Email to JSON Is Critical for Notification Routing

Routing by email content sounds simple until you meet the realities of email formatting. The email-to-JSON layer is what makes notification-routing robust for both technical and business outcomes.

Technical reasons

  • MIME complexity: Real-world email is multipart and messy. A message may include text/plain and text/html bodies, plus inline images and attachments. MIME parsing extracts a canonical body and explicit attachment metadata.
  • Unicode and encodings: Headers and bodies often include RFC 2047-encoded words and quoted-printable sections. A proper converter normalizes character encodings so your filters compare apples to apples.
  • Signal from headers: Keys like From, To, Subject, Message-ID, In-Reply-To, References, List-Id, and Auto-Submitted carry useful routing signals. JSON representation makes these fields easy to evaluate.
  • HTML consistency: Notification systems should ingest clean text, not HTML fragments. Email-to-JSON lets you prefer text bodies or sanitize HTML safely.
  • Idempotency: Re-delivery happens. A stable JSON payload with an idempotency key derived from Message-ID and content hashes enables safe deduplication.

Business reasons

  • Faster triage: High-severity alerts jump to incident rooms while low-severity updates post to lower-noise channels.
  • Lower MTTR: Clear routes and enriched context decrease the time to engage and resolve incidents.
  • Compliance and auditability: Structured JSON is easier to log, filter, and redact than raw email. You get traceable routing decisions.
  • Vendor flexibility: If an upstream tool only ships email, your system still integrates cleanly with your preferred chat and alerting stack.

Architecture Pattern: Combining Email-to-JSON With Notification Routing

A clean design separates message ingestion, parsing, and routing. Below is a common architecture pattern that scales from a single team to enterprise-wide usage.

Core components

  • Inbound addresses: Create one or more email addresses per source or team. For example, build-failures@alerts.example.com and security@alerts.example.com.
  • Email ingestion: Receive SMTP traffic and expose each message as a normalized JSON object via webhook or REST polling.
  • Parser: Convert MIME to JSON with fields for headers, body variants, attachments, and security signals like DKIM and SPF results.
  • Router: Apply rules to the JSON payload to choose destinations. For example, route [SEV1] subjects to a dedicated Slack channel and page the on-call rotation.
  • Delivery layer: Push notifications to Slack, Teams, or incident tools. Implement retries and dead-letter queues for failed posts.
  • Observability: Emit structured logs and metrics at each stage. Track end-to-end latency from email receipt to chat delivery.

Teams often start with a single address and a simple rules file, then scale to per-service addresses and infrastructure-as-code rules. MailParse fits into this pattern by issuing instant addresses, parsing MIME, and delivering structured JSON to your endpoint so your router stays focused on business logic.

Step-by-Step Implementation

1) Provision inbound email addresses

Use a predictable naming scheme so your rules can infer context by recipient. Map each source or severity to its own address. For example:

  • build@ci-alerts.example.com for CI notifications
  • infra-sev1@alerts.example.com for high-priority infrastructure alerts
  • customers@escalations.example.com for support escalations

Provisioning dedicated addresses isolates noise and simplifies rule evaluation. It also aids rate limiting and per-stream monitoring.

2) Configure a webhook endpoint

Expose an HTTPS endpoint to receive JSON payloads. Verify signatures and reject unknown sources. Keep the handler fast and idempotent. For a deeper dive on signing, retries, and backoff, see Webhook Integration: A Complete Guide | MailParse.

Recommended practices:

  • Require TLS and validate HMAC signatures on every request.
  • Respond with 2xx only after persisting the payload or enqueueing a job.
  • Return 4xx for validation errors and 5xx for transient failures to trigger retries.

3) Define parsing preferences and normalization rules

A robust email-to-JSON pipeline exposes options and consistent fields. Here are common rules to adopt:

  • Preferred body: Choose text/plain when available. If only HTML exists, sanitize and convert to text to avoid embedding scripts or style noise.
  • Attachment policy: Extract metadata only for non-inline attachments. Treat images with Content-Disposition: inline as inline media and omit them from routing decisions unless explicitly required.
  • Header normalization: Decode RFC 2047-encoded headers, lowercase domains, and trim whitespace.
  • Redaction: Remove or mask PII from bodies and headers before routing to broader channels.
  • Threading: Surface Message-ID, In-Reply-To, and References so replies can be grouped.

Typical incoming email for notification-routing might look like this:

Content-Type: multipart/alternative; boundary="XYZ"
From: CI Robot <ci@builds.example.org>
To: build@ci-alerts.example.com
Subject: [SEV1] Payment service compile failure on main
Message-ID: <20240401T120000Z-abc123@builds.example.org>
Auto-Submitted: auto-generated
Date: Mon, 01 Apr 2024 12:00:00 +0000

--XYZ
Content-Type: text/plain; charset="UTF-8"

Build 98321 failed in step "compile".
Service: payment
Commit: 4f82c19
Log: https://ci.example.org/build/98321
--XYZ
Content-Type: text/html; charset="UTF-8"

<p>Build 98321 failed in step "compile".</p>
<p>Service: payment</p>
<p>Commit: 4f82c19</p>
<p>Log: <a href="https://ci.example.org/build/98321">view logs</a></p>
--XYZ--

Converted into JSON, the key fields your router needs are explicit:

{
  "envelope": {
    "from": "ci@builds.example.org",
    "to": ["build@ci-alerts.example.com"]
  },
  "headers": {
    "subject": "[SEV1] Payment service compile failure on main",
    "message_id": "20240401T120000Z-abc123@builds.example.org",
    "auto_submitted": "auto-generated",
    "date": "2024-04-01T12:00:00Z"
  },
  "body": {
    "text": "Build 98321 failed in step \"compile\".\nService: payment\nCommit: 4f82c19\nLog: https://ci.example.org/build/98321\n",
    "html": "<p>Build 98321 failed...</p>"
  },
  "attachments": [],
  "spam": {
    "dkim": "pass",
    "spf": "pass",
    "dmarc": "pass"
  },
  "meta": {
    "source_ip": "203.0.113.10",
    "received_at": "2024-04-01T12:00:02Z"
  },
  "idempotency": {
    "key": "hash(message_id + body.text)"
  }
}

4) Implement routing rules

Maintain routing logic in a dedicated service or function. Keep rules data-driven so operators can adjust without redeploying code. Here is a concise approach:

  • Severity detection: Look for [SEV1], [SEV2], or vendor-specific tags in the subject.
  • Sender mapping: Route by domain. For example, anything from @builds.example.org goes to engineering-builds.
  • Recipient mapping: Interpret the to address as a namespace or team key.
  • Auto-generated filter: If auto_submitted is present, skip threaded reply rules.

Example pseudo-code for a Node.js router that posts to Slack or Teams:

// Express-style handler
app.post("/inbound", async (req, res) => {
  const msg = req.body;

  // Idempotency check
  const seen = await cache.get(msg.idempotency.key);
  if (seen) return res.status(200).end();
  await cache.set(msg.idempotency.key, "1", { ttl: 3600 });

  const subject = msg.headers.subject || "";
  const fromDomain = (msg.envelope.from || "").split("@")[1] || "";
  const channel = selectChannel(msg.envelope.to[0], fromDomain, subject);

  const text = extractText(msg.body);

  if (channel.type === "slack") {
    await postToSlack(channel.webhookUrl, {
      text: `🚨 ${subject}\n${text}`
    });
  } else if (channel.type === "teams") {
    await postToTeams(channel.webhookUrl, {
      text: `ALERT: ${subject}\n${text}`
    });
  } else {
    await postToWebhook(channel.url, { subject, text, msg });
  }

  res.status(200).end();
});

Make selectChannel table-driven so operators can match patterns like subject contains "[SEV1]" or from domain is builds.example.org. Keep message text compact to avoid exceeding chat platform limits. Include direct links to upstream dashboards when available.

5) Polling alternative to webhooks

If your environment restricts inbound webhooks, use REST polling to fetch messages in batches. Poll frequently for low latency and acknowledge messages only after successful routing. Whether you choose webhook delivery or polling depends on your network posture and reliability constraints. See MIME Parsing: A Complete Guide | MailParse for deeper details on how MIME parts turn into JSON fields your router can consume.

Testing Your Notification Routing Pipeline

Notification-routing is only as reliable as its tests. Combine unit tests, fixture replays, and end-to-end verifications to cover failures that occur in the wild.

Strategies and tools

  • Fixture-driven tests: Build a corpus of real-world MIME samples, including multipart messages, HTML-only notifications, and emails with large attachments. Replay these fixtures into your parser and router and assert on the final destination.
  • Boundary cases: Introduce subjects with encoded words, long lines, or unicode. Include malformed headers, nested multiparts, and inline images with odd content IDs.
  • Attachment edge cases: Ensure that screenshots and logs do not overflow chat limits. Test that attachment policies redact PII or strip executables.
  • Idempotency: Simulate retries by posting the same payload multiple times. Verify that only one chat message is created.
  • Rate limits and backpressure: Flood test the webhook endpoint and ensure worker pools scale without dropping notifications. Validate retry backoff and DLQ behavior for downstream failures.
  • Observability checks: Assert that metrics for received, parsed, routed, and failed are emitted with labels for source and destination.
  • End-to-end: Send test emails from controlled addresses with deterministic content. Confirm that they appear in the correct Slack or Teams channels with expected formatting.

Production Checklist: Monitoring, Error Handling, and Scaling

Monitoring and alerting

  • Latency SLOs: Track time from email receipt to chat delivery. Alert if median or p95 exceeds your threshold.
  • Success ratio: Monitor ratio of successfully routed messages to total received. Keep per-source breakdowns.
  • Retry depth: Measure webhook retry attempts and DLQ size. Investigate increases promptly.
  • Schema drift: Check for unexpected nulls or missing fields in JSON payloads. Add canary alerts for parsing regressions.

Error handling

  • Validation: Reject payloads missing required fields like headers.subject or envelope.from.
  • Quarantine and DLQs: When routing fails consistently, retain the message in a dead-letter queue with reason and context.
  • Auto-responses and loops: Detect Auto-Submitted, Precedence: bulk, and List-Id. Avoid sending loop-inducing replies or recursive notifications.
  • Oversized content: Truncate bodies to platform limits and attach a link to full logs. Strip or store oversized attachments outside chat.
  • Security scanning: Optionally scan attachments before further processing. Never forward malware to collaboration tools.

Scaling and reliability

  • Stateless workers: Keep routing stateless with external queues and caches for idempotency keys.
  • At-least-once semantics: Expect duplicates and implement de-duplication keyed by Message-ID plus content hash.
  • Ordering: If order matters for a stream, shard by to address or List-Id so messages process in sequence.
  • Configuration as code: Store routing rules in versioned config with staged rollout and quick rollback.
  • Graceful degradation: If chat APIs are down, buffer to persistent storage and notify operators via alternative channels.

Security and compliance

  • Webhook authentication: Verify signatures on every request and optionally IP allowlist known senders.
  • Least privilege: Restrict access to routing configs and secrets. Rotate chat webhooks regularly.
  • PII handling: Redact sensitive fields before posting to broad channels. Keep raw email data encrypted at rest.
  • Retention: Define how long to store raw MIME and derived JSON. Enforce deletion policies for privacy laws and internal standards.
  • Audit trails: Log routing decisions with keys like message_id, matched rules, and destinations for traceability.

Conclusion

Email remains a universal transport for alerts and updates, but routing decisions require structured data. Converting email to JSON removes ambiguity, surfaces the headers and bodies that matter, and makes routing predictable. By pairing a reliable parser with a rule-driven router and solid operational guardrails, teams deliver the right notifications to the right channels with low latency and high confidence. MailParse streamlines the ingestion and parsing layer so you can focus on the policy and outcomes that keep engineers informed and incidents short.

FAQ

How do I choose between text and HTML bodies when converting email to JSON?

Prefer text/plain for routing since it is easier to evaluate, shorter, and free of formatting hazards. If the message arrives HTML-only, sanitize and convert to text by stripping scripts and styles, then normalize whitespace. Store both variants in JSON so you can use the text for routing and optionally use HTML for rich-rendered notifications when the destination supports it.

What is the best way to handle attachments for notification-routing?

Do not forward attachments directly to chat channels unless required. Instead, extract attachment metadata and host large files on secure storage, then post a link. For inline images, skip them in routing logic because they rarely contain routing signals. Always set size limits and scan attachments for malware before further processing.

How do I group threaded replies or reduce noise from auto-replies?

Group messages by Message-ID, In-Reply-To, and References when building conversation threads or updating existing posts. To cut noise, detect Auto-Submitted headers and Precedence: bulk values and treat them as machine-generated. For list mail, List-Id helps route all messages from the same source consistently.

How do I prevent duplicates when webhooks retry or vendors resend alerts?

Derive an idempotency key from Message-ID plus a stable hash of the normalized body. Store the key with a TTL and skip processing if it exists. This approach tolerates both webhook retries and vendors that resend identical messages after a timeout.

Should I use webhooks or REST polling to receive JSON messages?

Choose webhooks for lower latency and simpler flow when your environment accepts inbound traffic. Use polling when networks are closed or when you want pull-based control. Both patterns work if you preserve idempotency and implement retries. MailParse supports webhook delivery and polling so you can fit the model to your infrastructure. For deeper API and MIME details, see Email Parsing API: A Complete Guide | MailParse.

Ready to get started?

Start parsing inbound emails with MailParse today.

Get Started Free