P vs MailKite logo
Alternative to Postal

The same parsed receive→reply loop — with no mail server to keep alive.

Postal is a full self-hosted mail server: it sends, it receives inbound over incoming routes, and its HTTP endpoint POSTs your agent a parsed payload with plain_body, html_body, and decoded attachments. The catch is operational, not technical — you run the box (Docker, MariaDB, Caddy, DNS, TLS, IP warmup, blocklist monitoring, patching). MailKite is that same loop delivered as one signed JSON webhook, with nothing to host.

A fair take on Postal

Postal is genuinely good software — MIT-licensed, mature, and it truly does parsed inbound, not just raw MIME. Self-hosting it buys you total control, data residency, and no per-message fee: at real scale you pay for a server and an IP, not for each email. Since v3 (2024) it even dropped RabbitMQ, so the stack is smaller than most guides remember.

How it works

Postal vs MailKite, in one diagram

P Postal (you self-host) sender Postal boxMX · MariaDB · Caddy HTTP endpointparsed fields, no auth block your agent You also operate the MX, MariaDB, Caddy and TLS, SPF/DKIM/DMARC, PTR/rDNS, IP warmup, and blocklist monitoring. The whole box is yours to keep alive. MailKite sender MX edgeparse + auth JSON webhookbody + auth inline your agent One signed event carries the parsed body, attachments, and an SPF/DKIM/DMARC verdict. Nothing to host.
Why teams switch

What MailKite does differently

The box is ours to run, not yours

Postal means standing up and keeping alive a full send+receive mail server: VPS with port 25, Docker, MariaDB, Caddy, DNS, TLS, PTR/rDNS, IP warmup, blocklist watch, and Postal + MariaDB upgrades. MailKite collapses every one of those stages into a signed webhook — the only box you run is your agent.

Auth verdict inline, not headers to parse

Postal's processed payload gives you spam_status, but the aligned SPF/DKIM/DMARC result an agent should weight before trusting a sender lives in the raw Authentication-Results headers — you parse them yourself. MailKite ships a structured auth block (spf/dkim/dmarc/spam) already resolved, so injection-trust is a field read, not a header parse.

Deliverability drift is handled, not your incident

On Postal the sending IP's reputation is yours to build and defend. If a fooled agent blasts a bad reply and lands the IP on a blocklist, that's your delisting to chase. MailKite sends over a monitored stream, so reputation and blocklist recovery aren't your on-call.

Managed loop, or we run the model too

Point a webhook and get parsed JSON, or set a route to action:'agent' with an agentPrompt and MailKite runs the receive→reply loop for you on a durable queue — capped and recorded as a transcript. Plus a hosted MCP server and Claude Code plugin, none of which you host.

Side by side

MailKite vs Postal

MailKite Postal
Parsed inbound to your endpoint One signed parsed JSON webhook Yes — parsed fields to your HTTP endpoint
Who runs the mail server Managed — nothing to host You (Docker, MariaDB, Caddy)
Start receiving DNS-verify a domain, add a webhook Provision box, DNS, TLS, then routes
Auth for injection safety auth block: spf/dkim/dmarc/spam inline spam_status; parse Authentication-Results yourself
Attachments Short-lived signed URLs Base64 inline in the payload
IP reputation & warmup Handled on a monitored stream Yours to build and defend
Upgrades & patching Handled You run Postal + MariaDB upgrades
Per-message cost Metered; free 3,000/mo in+out None — server + IP + your time

Competitor capabilities change — we re-audit these tables regularly. Spot something out of date? Tell us and we'll fix it.

Show the payload

What actually hits your webhook

Same job, but the auth verdict is a field (not a header parse), attachments are signed URLs (not base64 to decode), and the reply goes out over a monitored stream you don't host.

Postal incoming route → HTTP endpoint (processed payload)
http
// Postal must return 200 within 5s or it retries and eventually bounces.
import express from "express";
const app = express();
app.use(express.json({ limit: "50mb" })); // attachments arrive base64-inline

app.post("/postal/incoming", async (req, res) => {
const m = req.body; // { rcpt_to, mail_from, subject, plain_body, html_body,
// spam_status, attachments: [{ filename, content_type, data }] }

// No SPF/DKIM/DMARC verdict block — the aligned result lives in the raw
// Authentication-Results headers, so you parse it yourself.
const trusted = m.spam_status === "NotSpam"; // weaker than a real auth verdict

// Attachments are base64 inline — decode each one yourself.
const files = (m.attachments ?? []).map((a) => ({
filename: a.filename, buffer: Buffer.from(a.data, "base64"),
}));

const answer = await runAgent({ task: m.plain_body, from: m.mail_from, trusted });

// Reply via YOUR Postal box — you host the auth, the IP, and the server behind it.
await fetch("https://postal.yourco.dev/api/v1/send/message", {
method: "POST",
headers: { "X-Server-API-Key": process.env.POSTAL_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ to: [m.mail_from], from: m.rcpt_to, subject: `Re: ${m.subject}`, html_body: answer.html }),
});
res.sendStatus(200); // must be fast, or Postal retries
});
MailKite
json
POST /your-webhook   Content-Type: application/json
x-mailkite-signature: t=…,v1=… (HMAC-SHA256 — verify locally)

{
"id": "msg_2Hk9…",
"type": "email.received",
"from": { "address": "ada@example.com" },
"to": [{ "address": "support@myapp.ai" }],
"subject": "Re: invoice #1042",
"text": "Looks good — approved!",
"html": "<p>Looks good — approved!</p>",
"threadId": "<a1b2c3@mail.example.com>",
"auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass", "spam": "ham" },
"attachments": [
{ "filename": "po.pdf", "contentType": "application/pdf",
"size": 18213, "url": "https://api.mailkite.dev/att/2Hk9…/0?sig=…" }
]
}
The price advantage

Unlimited domains. One quota. Free to start.

MailKite

  • Free: 3,000 emails/mo (in + out)
  • Unlimited domains on every plan
  • Pro $20/mo → 50,000, graceful overage

Postal

  • MIT-licensed software, no hosted SaaS tier
  • No per-message fee — you pay for a server + sending IP
  • Real cost is your operational time: DNS, TLS, IP warmup, blocklist monitoring, MariaDB and Postal upgrades

Postal is free-as-in-software, not free-as-in-no-work. MailKite meters volume instead of billing per box or per domain, and there's no server, IP, or patching in the price.

See full MailKite pricing →

Switch in an afternoon

Moving from Postal

  1. 1

    DNS-verify a domain instead of provisioning a box

    Point MX at MailKite to receive (SPF + DKIM to send). No VPS, no port-25 fight, no Docker/MariaDB/Caddy bootstrap, no PTR/rDNS or TLS to stand up — the mail server is already running.

  2. 2

    Repoint your incoming-route handler at a webhook

    Keep the same shape: read event.text, event.from.address, event.subject. Drop the base64 attachment decode (use the signed URLs) and stop parsing Authentication-Results — read event.auth.spf/dkim/dmarc instead.

  3. 3

    Reply through the send API and retire the box

    Swap the fetch to your Postal /api/v1/send/message for mk.send({ from: event.to[0].address, inReplyTo: event.id, … }) — same account, threaded automatically, and no IP reputation or MariaDB backups left to keep alive.

Questions

Doesn't Postal already receive inbound and post parsed JSON?

Yes — this isn't a capability gap. Postal's incoming route can POST your agent a processed payload with plain_body, html_body, and decoded attachments. The difference is operational: with Postal you run the mail server, IP reputation, DNS, TLS, and upgrades doing it; with MailKite you receive a signed webhook and own none of that.

Isn't self-hosting Postal cheaper — no per-message fee?

At real volume, self-hosting can win on unit economics: you pay for a server and an IP, not per email. Weigh that against the operational cost MailKite absorbs — IP warmup, blocklist delisting, patching, MariaDB maintenance, and being on-call when deliverability drifts. Free-as-in-software, not free-as-in-no-work.

What do I lose leaving Postal for a managed inbox?

You give up total control and data residency — on Postal every message lives on hardware and in a database you chose, which matters for regulated mail. If control and residency outweigh the ops and you have a team to run a mail server, self-host. If you'd rather receive a parsed, authenticated webhook, hand off the box.

Go deeper

The Postal alternative for AI agents →

The long-form take on our blog — receipts, runnable code, and where we won't overclaim.

Try MailKite free — keep Postal running until you're ready.

Point a domain, drop in a webhook URL, receive your first email. Unlimited domains, no credit card.