cd /news/ai-agents/the-mailersend-alternative-for-ai-ag… · home topics ai-agents article
[ARTICLE · art-48671] src=mailkite.dev ↗ pub= topic=ai-agents verified=true sentiment=· neutral

The MailerSend alternative for AI agents

MailKite launches an alternative to MailerSend for AI agents, offering normalized authentication verdicts and a built-in receive-to-reply loop on custom domains. The service provides developers with a simpler way to give autonomous agents their own email inboxes, handling signature verification and trust decisions automatically.

read11 min views15 publishedJul 4, 2026
The MailerSend alternative for AI agents
Image: Mailkite (auto-discovered)

MailerSend (by MailerLite) is a developer-friendly transactional service, and its Inbound Routing genuinely receives mail as parsed JSON. For an autonomous agent the gaps are narrower than usual: you compose the trust decision yourself from spf_check and dkim_check (no DMARC, no spam verdict), and there's no built-in agent loop. MailKite, which we build, hands you a normalized auth verdict and a receive→reply loop on a domain you own. For developers giving an agent its own inbox.

Most “does it receive?” comparisons are lopsided, but this one isn’t, so the diagram earns its place up front: MailerSend really does deliver inbound email as parsed JSON, and the difference for an agent is subtler than “webhook vs no webhook.” It’s what arrives in that JSON, and what your loop has to decide before it acts. The rest of the post is the honest version of this picture, plus the ~25 lines that are the whole MailKite side (from a runnable demo repo).

Everything below is a repo you can run while you read. Clone demo-mailersend-ai-agent and

npm start

, or open it in StackBlitz(real Node in a browser tab, no account) where it runs on load. You don’t need a domain to see the loop run: a webhook normally needs a public URL, so

npm start

boots the server and self-fires a correctly signed

email.received

event at its own localhost — the whole receive→verify→think→reply loop runs in one command (the reply is a dry-run, and managed-route.mjs

dry-runs the hosted path too). It also ships the MailerSend path next to it in , so every contrast here is something you can diff and test, not take on faith. A domain comes in only when you want

mailersend-contrast/

realinbound email to arrive.

Here’s the whole MailKite side of an agent inbox: email in, verify the signature, hand the body to your model, reply through the same client. This is the heart of server.mjs

in that repo — wrapped in a dry-run harness and a stub agent so it runs with no key and no LLM, but the loop is exactly this, and it runs as pasted on Node 18+ (npm install mailkite express

):

import express from "express";
import { MailKite } from "mailkite";

const app = express();
const mk = new MailKite(process.env.MAILKITE_API_KEY);
const SECRET = process.env.MAILKITE_WEBHOOK_SECRET;

app.use("/hooks/agent", express.raw({ type: "application/json" }));

app.post("/hooks/agent", async (req, res) => {
  // signature check, replay window, constant-time compare — one call
  if (!MailKite.verifyWebhook(req.headers["x-mailkite-signature"], req.body, SECRET)) {
    return res.sendStatus(401);
  }
  res.sendStatus(200); // ack fast; run the agent out of band

  const event = JSON.parse(req.body);
  if (event.type !== "email.received") return;

  // The body is untrusted INPUT, never instructions. One block decides trust.
  const answer = await runAgent({
    task: event.text,
    from: event.from.address,
    trusted: event.auth.spf === "pass" && event.auth.dmarc === "pass",
  });

  await mk.send({
    from: event.to[0].address,   // reply from the address it was sent to
    to: event.from.address,
    subject: `Re: ${event.subject}`,
    inReplyTo: event.id,         // threads the reply
    html: answer.html,
  });
});

app.listen(3000);

That’s a fully autonomous email agent: it hears, thinks, and answers, with event.auth.dmarc

already resolved for the trust check. npm start

boots the server and self-fires the exact payload MailKite’s delivery worker sends, so in one command you watch the agent read the task, take its trust verdict straight off event.auth

, and dry-run the reply — no account or LLM required. The identical handler shape exists for Python, Ruby, Go, PHP, and Java; see the receiving docs and sending docs. Open the demo in StackBlitz to run it in your browser.

Where MailerSend wins for agents, honestly #

I want to be fair here, because MailerSend’s inbound is not a bolt-on. It’s a real feature and it’s pleasant to use.

If you’re already on MailerSend for outbound and you want an agent to read replies, the honest answer is: their inbound gets you most of the way there. This post isn’t “MailerSend can’t receive.” It’s about the two places an agent path still asks you to do the work.

What MailerSend asks of an agent builder #

The inbound payload nests the message under data

, and the two authentication signals it gives you are spf_check

(a { code, value }

object) and dkim_check

(a boolean). There is no DMARC result and no spam classification. So before your agent can weigh whether to trust an instruction, you compose the verdict yourself. Here’s the real MailerSend inbound handler ( mailersend-contrast/handler.mjs in the demo):

// MailerSend inbound: verify Signature header, then compose the trust decision yourself
import crypto from "node:crypto";
import { MailerSend, EmailParams, Sender, Recipient } from "mailersend";

const ms = new MailerSend({ apiKey: process.env.MAILERSEND_API_KEY });

export async function handleInbound(req, res) {
  const raw = req.rawBody; // keep the raw bytes for HMAC
  const expected = crypto.createHmac("sha256", process.env.MS_SIGNING_SECRET)
    .update(raw, "utf8").digest("hex");
  const got = req.headers["signature"] ?? "";
  if (!crypto.timingSafeEqual(Buffer.from(got, "hex"), Buffer.from(expected, "hex"))) {
    return res.sendStatus(401);
  }

  const { data } = JSON.parse(raw);          // message nested under data
  // spf_check is {code, value} (the code set isn't fully documented, so you normalize it);
  // dkim_check is a boolean. No DMARC alignment and no spam score are in the payload.
  const spfPass = data.spf_check?.value === "pass";
  const dkimPass = data.dkim_check === true;
  const trusted = spfPass && dkimPass;       // partial: no DMARC, no spam signal to weight

  const answer = await runAgent({ task: data.text, from: data.from?.email, trusted });

  const reply = new EmailParams()
    .setFrom(new Sender(data.recipients.rcptTo, "Agent"))
    .setTo([new Recipient(data.from.email)])
    .setSubject(`Re: ${data.subject}`)
    .setInReplyTo(data.headers["message-id"])
    .setHtml(answer.html);
  await ms.email.send(reply);
  res.sendStatus(200);
}

Nothing here is hard. But notice what the agent is left holding: it derives trusted

from two raw signals and then has to accept that DMARC alignment and a spam score simply aren’t in the payload. For a program that acts on the contents of email, that missing normalized verdict is the load-bearing gap, not the plumbing. The repo pins both costs — npm test

runs composeTrust()

and watches dmarc

and spam

come back null

(a partial verdict you maintain), then runs the handler end to end to show the reply is a second call to the send API, threaded by hand. On MailKite that decision is one field, event.auth

, which is the whole reason server.mjs never composes anything.

There’s a second gap, and it’s about who runs the loop. MailerSend delivers the message and stops; the model call, the retry policy, and the “did this run actually finish?” bookkeeping are your service to build and keep up. That’s a reasonable division of labor for a send-focused platform. It’s just work the agent path still owns.

MailerSend even ships an official MCP server, which is a genuinely nice touch for an agent stack. It’s worth being precise about what it does: it exposes sending, domain management, and analytics as tools, not inbox reading. So an agent can send through MailerSend’s MCP, but it can’t read its own mail through it; the inbound half is still the webhook you wire up.

The comparison, no adjective inflation #

MailerSend MailKite
Inbound delivery Parsed JSON webhook (data.* ) Parsed JSON webhook (email.received )
Auth signals spf_check + dkim_check auth: {spf, dkim, dmarc, spam}
DMARC / spam verdict Not in payload Included, normalized
Webhook signing HMAC-SHA256 Signature header HMAC + replay window, one-call verify
Built-in agent loop None (webhook only) Optional route action: 'agent'
Failed-delivery replay Retries Retries + one-click replay
Free tier 500 emails/mo, 1 domain 3,000 msgs/mo, no per-domain fee
Sending REST + SMTP + templates REST + SMTP submission edge

The through-line: MailerSend gives you a clean, signed, parsed inbound feed and leaves the trust verdict and the agent loop to you. MailKite resolves the verdict and will host the loop, on domains that don’t cost extra to add.

What actually hits your agent’s webhook #

Here’s the MailKite email.received

event. The auth

block is the whole point of the second diagram: SPF, DKIM, DMARC, and a spam classification, already resolved, so the agent reads one field instead of composing a decision:

{
  "id": "msg_2Hk9…",
  "type": "email.received",
  "from": { "address": "ada@example.com" },
  "to": [{ "address": "agent@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": [
    { "id": "msg_2Hk9…:0", "filename": "po.pdf", "contentType": "application/pdf",
      "size": 18213, "url": "https://api.mailkite.dev/att/2Hk9…/0?exp=…&sig=…" }
  ]
}

Don’t take the shape on faith — fire one and watch it come back, auth

block and all. Spoof the SPF/DKIM to fail

and see the verdict flip; that’s the field the MailerSend path makes you compose from spf_check

and dkim_check

:

Treat the body as data, never as instructions. The auth

block is what lets the agent decide whether to weight a sender before it reads a word of the message, and checking it is necessary but not sufficient: the real defense is bounding what a fooled agent can do. That’s the subject of the agent-security post, and it applies whichever provider parses your mail.

One thing MailKite does that a send-first provider doesn’t #

MailKite, which we build, was designed inbound-first, so the agent loop is a first-class option rather than something you assemble. Beyond the webhook lane above, a route whose action

is agent

runs the model loop for you:

await mk.createRoute({
  match: "agent@myapp.ai",
  action: "agent",
  agentPrompt: "Read incoming mail, answer billing questions from the docs, escalate the rest.",
});

The run executes on a Cloudflare Queue with its own budget, capped at a few tool rounds, reaped at five minutes so a slow model can’t wedge the pipeline, and recorded as a transcript you can drill into. Delivery failures are retried and one-click replayable from the dashboard, so a bad deploy on your side doesn’t drop an inbound message on the floor. To start, DNS-verify the domain (MX to receive, SPF + DKIM to send); there’s no sandbox or account-approval wait (MailerSend gates new accounts behind a trial-approval step that can take up to a day). The free tier is 3,000 messages a month, in and out, with no per-domain fee, so an agent can own as many scoped addresses as it needs.

Both ways to run the loop are in the demo repo: server.mjs is the bring-your-own-webhook version at the top of this post, and

dry-runs this hosted route. MailerSend has no equivalent to the second — you assemble the model and the send call yourself — and

managed-route.mjs

npm test

runs the MailKite side right next to the mailersend-contrast/handler.mjs

version so you can diff the two.## FAQ

Can MailerSend receive inbound email for an agent? Yes. MailerSend’s Inbound Routing points your MX at MailerSend, matches messages with catch-all or per-recipient filters, and POSTs the parsed email to your webhook as JSON. The message nests under data

with decoded text

, html

, subject

, from

, headers

, and attachments

, and the webhook is HMAC-SHA256 signed. It’s a real inbound feed; the agent-specific gaps are the trust verdict and the loop, not parsing.

Does MailerSend’s inbound payload include SPF, DKIM, and DMARC? It includes spf_check

(a { code, value }

object) and dkim_check

(a boolean). There is no DMARC alignment result and no spam classification in the payload, so an agent composes its trust decision from the two signals it does get. MailKite delivers a single normalized auth: {spf, dkim, dmarc, spam}

block.

Is MailerSend’s inbound routing free? Inbound routing is available on the free plan, but two details matter for an agent. As of December 2025 the free plan is 500 emails/month (100/day, card required) on one verified domain, and forwarded or posted inbound messages count against that same monthly quota; the former 3,000/month tier is now paid from $7/month. MailKite’s free tier is 3,000 messages a month across inbound and outbound with no per-domain fee.

How does an agent reply to an inbound email on MailerSend? Through the send API. The Node SDK builds a reply with EmailParams

, setInReplyTo

, and setReferences

to thread it, using the original message-id from data.headers

. It works; you’re just wiring the send half yourself. On MailKite, mk.send({ inReplyTo: event.id })

threads from the event you already have.

Do I have to move my sending off MailerSend to use MailKite for an agent? No. MailKite speaks plain HTTPS and REST, and offers an SMTP submission edge, so you can run the agent’s inbox on MailKite and keep sending wherever you already do. You’re replacing the inbound trust-and-loop layer, not your provider.

MailerSend’s inbound is one of the more developer-friendly ones out there, and if you’re already sending with it, its parsed webhook gets your agent most of the way. If the missing verdict block and the do-it-yourself loop are the parts you’d rather not own, clone the demo repo (or run it in your browser), then point a domain at MailKite and your next inbound email arrives with the verdict already resolved.

Related: the pillar on giving your agent an inbox and agent inbox security by design.

── more in #ai-agents 4 stories · sorted by recency
── more on @mailersend 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/the-mailersend-alter…] indexed:0 read:11min 2026-07-04 ·