All posts
Gabe 6 min read

Email parser API: parse inbound email to JSON with a webhook

An email parser API that turns inbound email into structured JSON — no templates, no IMAP, no MIME parsing. Point your MX at MailKite and get a signed webhook with the full message payload.

An email parser API takes incoming email and returns structured data your app can use. The traditional approach — Mailparser, Parseur, Parsio — gives you a template editor: you highlight a field, name it, and hope future senders match the layout. The API approach is different: point your domain’s MX records at a parsing service, and every inbound message arrives at your endpoint as clean JSON — headers split out, body decoded, attachments extracted — without you ever writing a regex or maintaining a template.

MailKite is that API. You add a domain, register a webhook, and every email to *@yourdomain.com is parsed and POSTed to your endpoint as a signed JSON payload. No mail server, no IMAP polling, no mailparse in your dependency tree. See the inbound email API for the full product picture, or the complete email-to-webhook guide for a multi-language deep dive. Here’s how it works with real code.

How it works

senderany mail client MailKite MXparse + SPF/DKIM signed POSTapplication/json your endpointverify → 200
The MX edge handles SMTP, MIME parsing, and authentication. Your endpoint only sees a signed JSON request.

Three steps:

  1. Add your domain to MailKite. You get an MX record to publish in your DNS.
  2. Register a webhook — the URL where parsed email lands.
  3. Handle the POST. Verify the HMAC signature, read the JSON body, return 2xx.

Every email to any address on that domain is parsed and delivered the same way. No per-sender templates, no mailbox connections, no polling intervals.

Set up the domain

Add your domain via the dashboard or CLI:

npx @mailkite/cli domains add mail.yourapp.com --json

MailKite returns the DNS records to publish:

Type: MX   Host: @   Priority: 10   Value: mx.mailkite.dev
Type: TXT  Host: @   Value: "v=spf1 include:mailkite.dev ~all"
Type: TXT  Host: mailkite._domainkey   Value: "v=DKIM1; k=rsa; p=..."
Type: TXT  Host: _dmarc   Value: "v=DMARC1; p=none;"

Add these to your DNS provider. Once the MX record propagates (usually under 5 minutes), MailKite activates the domain and starts receiving.

Register your webhook

npx @mailkite/cli webhook set <domainId> https://yourapp.com/hooks/mailkite --json

MailKite returns a signing secret. Store it — you’ll need it to verify incoming webhooks.

Parse email in Node.js

import express from "express";
import { MailKite } from "@mailkite/sdk";

const app = express();
const PORT = process.env.PORT || 3000;
const WEBHOOK_SECRET = process.env.MAILKITE_WEBHOOK_SECRET;

// CRITICAL: use express.raw() — not express.json().
// The HMAC covers the raw bytes; JSON parsing first would break verification.
app.use("/inbound", express.raw({ type: "application/json" }));

app.post("/inbound", (req, res) => {
  // 1. Verify the signature
  const sig = req.headers["x-mailkite-signature"] || "";
  if (!MailKite.verifyWebhook(sig, req.body, WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  // 2. Parse the payload
  const email = JSON.parse(req.body);

  console.log("From:", email.from);
  console.log("To:", email.to);
  console.log("Subject:", email.subject);
  console.log("Text:", email.text);
  console.log("HTML:", email.html);
  console.log("Attachments:", email.attachments?.length ?? 0);

  // 3. Do your work — store it, forward it, trigger a workflow
  res.status(200).send("OK");
});

app.listen(PORT, () => console.log(`Listening on :${PORT}`));

The payload you get

Every inbound email arrives as a JSON object with these fields:

{
  "id": "msg_abc123",
  "from": "sender@example.com",
  "to": ["hello@mail.yourapp.com"],
  "cc": [],
  "bcc": [],
  "subject": "Project update — Q3 timeline",
  "text": "Hi team,\n\nHere's the Q3 update...",
  "html": "<p>Hi team,</p><p>Here's the Q3 update...</p>",
  "headers": {
    "message-id": ["<abc123@example.com>"],
    "date": ["Thu, 10 Jul 2026 14:30:00 +0000"],
    "received": ["from mail.example.com by mx.mailkite.dev ..."]
  },
  "attachments": [
    {
      "filename": "report.pdf",
      "contentType": "application/pdf",
      "size": 48200,
      "url": "https://api.mailkite.dev/v1/attachments/msg_abc123/report.pdf"
    }
  ],
  "date": "2026-07-10T14:30:00.000Z"
}

No template mapping. No field extraction rules. The entire message — headers, text, HTML, and attachments — arrives in one object. If you need to pull a tracking number from the subject or a PO number from the body, that’s a string operation in your code, not a template you maintain.

Parse email in Python

from flask import Flask, request, abort
from mailkite import MailKite

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["MAILKITE_WEBHOOK_SECRET"]

@app.post("/inbound")
def handle_inbound():
    sig = request.headers.get("X-MailKite-Signature", "")
    if not MailKite.verify_webhook(sig, request.get_data(), WEBHOOK_SECRET):
        abort(401)

    email = request.get_json()

    # Extract what you need
    subject = email["subject"]
    sender = email["from"]
    body = email["text"]

    # Process: store in DB, trigger workflow, etc.
    return "OK", 200

Why not just use a template-based parser?

Template-based email parsers (Mailparser, Parseur, Parsio) work like this: you forward emails to their inbox, open the template editor, highlight the fields you want, name them, and save. When a matching email arrives, the parser extracts those fields and sends them to your app via webhook or integration.

That works until it doesn’t. Here’s where the model breaks down:

ProblemTemplate-basedAPI-first (MailKite)
New sender formatBuild a new templateNo change — JSON payload is format-agnostic
Field location changesEdit the templateYour code handles the new layout
Volume pricingPer-email credits ($30–300/mo)Unlimited free domains, usage-based
Attachment parsingLimited (PDFs only on paid plans)Every attachment type, URL-accessible
Template maintenanceOngoing — every sender is a templateZero — the API is the parser
LatencyForward → parse → deliver (seconds)MX edge → webhook (sub-second)

The template model is designed for non-technical users who want to extract data from emails without writing code. The API model is for developers who want to parse email as part of a program — the parsing is a step in a pipeline, not an end in itself.

When an API-first parser makes sense

  • Multi-product teams — you ship several products, each with its own domain. A template per sender per domain doesn’t scale. One webhook per domain does.
  • AI agents — agents need structured email data in real time, not a SaaS dashboard. The webhook lands in their handler directly.
  • Transactional workflows — order confirmations, support tickets, notification routing. The email IS the trigger; you need the data in code, not in a spreadsheet.
  • Attachment processing — PDFs, images, CSVs attached to inbound email. The API gives you a URL to fetch; no template needed for each document type.

FAQ

What’s the difference between an email parser API and an inbound email API?

They’re the same thing from different angles. “Email parser API” focuses on the extraction — turning raw email into structured data. “Inbound email API” focuses on the delivery — getting email from the internet to your app. MailKite does both: it receives email over SMTP, parses it to JSON, and delivers it to your webhook. The API is the parser.

Do I need to run a mail server?

No. You publish an MX record pointing to MailKite’s edge. MailKite handles the SMTP connection, MIME parsing, and authentication. Your app only sees HTTP.

How does MailKite handle attachments?

Every attachment — PDF, image, CSV, ZIP — is extracted from the MIME structure and stored. The payload includes a URL you can fetch with your API key. No size limit beyond the sender’s SMTP constraints.

Can I filter which emails get parsed?

MailKite parses every email to every address on your domain. Filtering happens in your webhook handler — check the to field, the from field, the subject, or any header. Routes (address-level rules) can also forward specific addresses to different endpoints.

What about email authentication?

MailKite checks SPF and DKIM on every inbound message and includes the results in the payload headers. You can enforce authentication in your handler — reject messages that fail SPF, for example — or trust MailKite’s edge to filter spoofed mail.

Is there a free tier?

Yes. Unlimited domains are free. Sending and receiving are free within generous limits. No credit card required to start — sign up at app.mailkite.dev.

Discuss this post: Hacker News Share on X

Related posts