Webhook Integration: MailParse vs Postmark Inbound

Compare MailParse and Postmark Inbound for Webhook Integration. Features, performance, and developer experience.

Why Webhook Integration Matters for Inbound Email

Real-time webhook integration is the backbone of modern inbound email processing. When a user replies to a support ticket or forwards a document into your app, you want that message parsed, normalized, and delivered to your backend immediately. A robust webhook-integration strategy ensures predictable delivery, strong security, straightforward retries, and clear payloads your code can trust. If a webhook fails due to a transient outage or a slow endpoint, the provider should keep trying, sign requests for verification, and give you the tools to replay or poll for messages when needed.

In this guide, we compare webhook-integration capabilities for inbound email between two platforms: a developer-focused service that provides instant addresses, MIME-to-JSON parsing, and webhook delivery with a REST polling option, and Postmark Inbound - the inbound email processing webhook from Postmark. The focus is on real-time email delivery via webhooks, including retry logic, payload signing, and how each handles edge cases. If you are building SaaS, support tooling, or workflow automation, the differences here will shape your integration experience and long-term reliability.

Related reading for teams planning their stack:

How MailParse Handles Webhook Integration

This platform is designed for developers who need fast setup, predictable payloads, and flexible delivery. It provides real-time webhooks, a REST polling API, and instant email addresses that route inbound mail into structured JSON. Below is a technical breakdown of the webhook-integration features that matter in production.

Webhook delivery and event model

  • Event type: inbound.received with headers like X-Event-Type for filtering and routing.
  • Immediate delivery in real-time with sub-second processing for typical messages.
  • Attachments parsed with metadata and base64 content, inline images detected with content IDs so you can reconstruct HTML safely.

Payload format

Webhook JSON is normalized for easy consumption. Example shape:

{
  "id": "evt_01HV...abc",
  "timestamp": "2026-04-24T12:05:43.120Z",
  "message": {
    "from": {"address": "alice@example.com", "name": "Alice"},
    "to": [{"address": "support@yourapp.io", "name": ""}],
    "cc": [],
    "bcc": [],
    "subject": "Re: Ticket #12345",
    "text": "Hi team, following up...",
    "html": "<p>Hi team, following up...</p>",
    "headers": [{"name": "Message-Id", "value": "<abc@mx>"}],
    "in_reply_to": "<abc@mx>",
    "references": ["<abc@mx>"],
    "raw_size": 42816,
    "attachments": [{
      "filename": "screenshot.png",
      "content_type": "image/png",
      "content_base64": "iVBORw0KGgoAAA...",
      "size": 120394,
      "content_id": "image001.png@01DAF..."
    }]
  }
}

Security and signature verification

  • HMAC SHA-256 signing on the raw request body using your webhook secret.
  • Headers:
    • X-Signature - hex signature of the raw body
    • X-Timestamp - seconds since epoch to prevent replay
  • Recommended policy: reject if the timestamp is older than 5 minutes and verify signatures in constant time.

Retries and idempotency

  • Automatic retries when your endpoint returns a non-2xx response or times out.
  • Backoff strategy: quick retry in 30 seconds, then 2 minutes, 10 minutes, and periodic attempts for up to 24 hours.
  • Headers:
    • Idempotency-Key - stable key for the message to deduplicate
    • X-Delivery-Attempt - 1 on first try, increments thereafter
  • Recommended integration: acknowledge with 202 Accepted as soon as the payload is queued internally, then process asynchronously to keep SLAs tight.

REST polling fallback

Not every environment can accept inbound webhooks due to firewalls, maintenance windows, or zero-downtime deploys. This provider exposes a REST polling API that lists inbound messages that have not been acknowledged yet. Use it to recover during incidents or as a belt-and-suspenders strategy alongside webhooks.

  • List endpoint supports cursors and time filters, so you can backfill a specific window.
  • Mark-as-processed mutation prevents double delivery when you confirm ingestion.

How Postmark Inbound Handles Webhook Integration

Postmark Inbound focuses on delivering parsed inbound email to your webhook endpoint. It is mature, widely used, and well documented. Here is how its webhook-integration capability typically works:

  • Webhook POSTs an inbound JSON payload to your configured URL in real time.
  • Optional HMAC signing via X-Postmark-Signature. When enabled, Postmark computes an HMAC SHA-256 using your webhook token and the raw request body. You verify the header against your computed signature in constant time.
  • Payload includes canonical fields such as FromFull, ToFull, CcFull, Subject, TextBody, HtmlBody, Headers, Attachments, and optionally RawEmail.
  • Attachments are delivered as base64 in the Attachments array with metadata like Name and ContentType.
  • Retries occur on non-2xx responses with backoff. Postmark-Inbound is generally reliable at reattempting transient failures.

The platform does not provide a REST polling option for inbound messages. If your endpoint is unavailable for an extended period, you depend on retries and any manual replay tools provided in their UI or via support. For many teams this is sufficient, but if you need a pull-based contingency during maintenance windows, you will need to plan around that limitation.

Side-by-Side Webhook-Integration Features

Feature MailParse Postmark Inbound
Real-time webhook delivery Yes, sub-second processing for typical messages Yes, fast inbound delivery
Payload signing HMAC SHA-256 with X-Signature and X-Timestamp HMAC SHA-256 with X-Postmark-Signature when configured
Retry logic Exponential backoff with multi-hour persistence, delivery attempt header Backoff based on non-2xx responses, automatic retries
Idempotency Idempotency-Key header plus stable event id Stable message identifiers in payload, deduplicate using MessageID or equivalent
REST polling fallback Yes, list and ack pattern to recover during outages No REST polling option
Attachments Base64 with metadata, content IDs for inline images Base64 with metadata in Attachments array
Raw email access Exposed via attachment or API for debugging Optional RawEmail field when enabled
Timeout guidance Recommend 2 to 5 second server-side timeout, accept 202 quickly Respond with 2xx promptly to avoid redelivery

Code Examples: Verifying Webhook Signatures and Handling Retries

Node.js example - verifying signatures and acknowledging quickly

// npm i express raw-body crypto
const express = require('express');
const getRawBody = require('raw-body');
const crypto = require('crypto');

const app = express();

// We need raw body to verify HMAC accurately
app.post('/webhooks/inbound', async (req, res) => {
  const raw = await getRawBody(req);
  const signatureHeader = req.headers['x-signature']; // Provider-specific
  const timestamp = req.headers['x-timestamp'];       // Provider-specific

  // 1) Check timestamp window to mitigate replay
  const now = Math.floor(Date.now() / 1000);
  if (!timestamp || Math.abs(now - Number(timestamp)) > 300) {
    return res.status(400).send('Stale timestamp');
  }

  // 2) Compute HMAC on raw body hex digest
  const secret = process.env.WEBHOOK_SECRET;
  const hmac = crypto.createHmac('sha256', secret).update(raw).digest('hex');

  // Constant-time comparison
  function safeEqual(a, b) {
    const ba = Buffer.from(a, 'utf8');
    const bb = Buffer.from(b, 'utf8');
    if (ba.length !== bb.length) return false;
    return crypto.timingSafeEqual(ba, bb);
  }

  if (!signatureHeader || !safeEqual(hmac, String(signatureHeader))) {
    return res.status(400).send('Invalid signature');
  }

  // 3) Idempotency - deduplicate on event id or Idempotency-Key
  const event = JSON.parse(raw.toString('utf8'));
  const idempotencyKey = req.headers['idempotency-key'] || event.id;
  const seen = await hasSeen(idempotencyKey); // implement in your store

  if (seen) {
    // Already processed - acknowledge to stop retries
    return res.status(200).send('OK');
  }

  // 4) Acknowledge early to keep latency low
  res.status(202).send('Accepted');

  // 5) Process asynchronously
  await recordSeen(idempotencyKey);
  await handleInbound(event.message); // your business logic
});

app.listen(3000, () => console.log('listening on 3000'));

// Example dedupe store, replace with Redis or database
const memory = new Set();
async function hasSeen(key) { return memory.has(key); }
async function recordSeen(key) { memory.add(key); }

async function handleInbound(msg) {
  // Route by mailbox, save attachments, extract ticket ids, etc.
}

Python example - Postmark-Inbound HMAC verification

# pip install flask
import hmac, hashlib, time
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_TOKEN = b'your-postmark-webhook-token'  # set via environment in production

def safe_compare(a: str, b: str) -> bool:
    return hmac.compare_digest(a, b)

@app.post("/webhooks/postmark-inbound")
def postmark_inbound():
    raw = request.get_data()
    sig = request.headers.get('X-Postmark-Signature', '')
    # Postmark computes HMAC SHA-256 on the raw body with your token
    computed = hmac.new(WEBHOOK_TOKEN, raw, hashlib.sha256).hexdigest()

    if not sig or not safe_compare(sig, computed):
        abort(400, "Invalid signature")

    # Acknowledge quickly
    # Use event identifiers in body to dedupe if needed
    return ("OK", 200)

Idempotency and retries

Whether you integrate with this provider or with Postmark-Inbound, treat webhook processing as at-least-once. Always deduplicate using a stable key, for example:

  • The webhook Idempotency-Key header or a top-level event id.
  • For Postmark's inbound, a stable MessageID or a hash of the MessageID and Date header.

Store seen keys in Redis with a TTL that matches your business needs, usually 24 to 72 hours, so replays during incidents do not create duplicates in your database.

Performance and Reliability in Production

Timeouts and response codes

  • Always return a 2xx once you have safely queued the message internally. Do not block on downstream services like virus scanning or ticket creation.
  • Set a low server-side timeout, typically 2 to 5 seconds, for webhook requests. If processing overruns, respond 202 then continue asynchronously.
  • Use circuit breakers and queues to decouple your webhook endpoint from the rest of your system.

Attachment handling and large messages

  • Attachments can push payloads into multi-megabyte territory. Keep body size limits generous on the webhook route or use chunking proxies where supported.
  • Decode base64 streams to disk or object storage, not memory. Validate content_type and size to avoid abuse.
  • For inline images, stitch content by content_id to render HTML safely.

Schema changes and forward compatibility

  • Parse defensively. Ignore unknown fields and treat missing noncritical fields as empty. This keeps your integration resilient to minor provider updates.
  • Log X-Delivery-Attempt or provider-specific retry counters to help SREs debug transient failures.

When your endpoint is down

  • With a polling fallback, you can temporarily disable webhooks, continue collecting inbound mail, then backfill with a cursor-based API when systems recover.
  • With Postmark Inbound, rely on automatic retries and be prepared to contact support or use UI tools for replays if required. Make sure your retry window aligns with your RTO.

If you are designing for high availability, combine rate limiting, idempotent handlers, safe queues, and observability. For guidance on the surrounding stack, see the Email Deliverability Checklist for SaaS Platforms.

Verdict: Which Is Better for Webhook Integration?

Both platforms deliver solid real-time webhook-integration for inbound email. Postmark's inbound webhook is well engineered and easy to adopt, with optional HMAC signing and an intuitive JSON schema. If your workloads only need push delivery and your team is comfortable with the retry behavior, it is an excellent choice.

MailParse edges ahead for teams that prioritize operational flexibility. If you want the option to poll during maintenance, rehydrate events after deploys, or build a cold-path ingestion pipeline, the combination of webhook delivery plus a REST polling API is compelling. The idempotency key, delivery attempt counters, and consistent payload shape also make it straightforward to design exactly-once semantics on top of an at-least-once transport. For fast moving SaaS products and customer support workflows, that flexibility translates directly into fewer incidents and faster recovery.

FAQ

How do I verify webhook signatures in constant time?

Compute the HMAC SHA-256 digest over the raw HTTP body using your provider's secret, hex encode it, then compare using a constant-time function like crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python. Also validate a timestamp header within a small window to mitigate replay attacks.

What response code should my webhook return?

Return a 2xx status as soon as you have persisted the payload to a durable queue or database. Responding 202 is a good pattern if additional processing is asynchronous. Non-2xx responses trigger retries, so do not use them for application-level errors unless you truly want redelivery.

How do I handle duplicate deliveries safely?

Treat all inbound webhooks as at-least-once. Deduplicate using a stable key like Idempotency-Key or a message identifier in the payload. Persist processed keys with a TTL and make handlers idempotent so replays do not create duplicate tickets or tasks.

What if my webhook endpoint is occasionally offline?

Design for graceful degradation. Use low-timeout acknowledgements, retries on your side, and if available, a REST polling fallback to backfill events. Without a polling option, ensure the provider's retry window covers your maintenance windows and use their replay tools when necessary.

Can I inspect the raw MIME when debugging parsing issues?

Yes in most cases. Postmark Inbound can include a RawEmail field when enabled. This provider exposes raw MIME via an attachment or API endpoint for debugging and audits. Store raw messages in a secure bucket with tags that link to your processed records.

Ready to get started?

Start parsing inbound emails with MailParse today.

Get Started Free