MailKite
Start free
All posts
Gabe 18 min read

The SocketLabs alternative for AI agents

SocketLabs is enterprise sending infrastructure that can also receive: its Inbound Parse POSTs mail as JSON to a URL you configure, guarded by a validation-code handshake and a shared secret key, with a spam score but no normalized SPF/DKIM/DMARC verdict. MailKite (which we build) gives an agent a real inbox: signed parsed JSON, an auth block for trust, and a receive→reply loop. For developers wiring an autonomous agent to email.

SL SocketLabs vs MailKite
SocketLabs vs MailKite — the same job (an inbox for your AI agent), two approaches.

Credit where it’s due first: SocketLabs can receive. Its Inbound Parse (the “Inbound API”) accepts mail over SMTP, deconstructs it, and POSTs a JSON message to a URL you configure, one email per POST. So this isn’t a “can’t receive” post. It’s a “here’s the shape of what you get, and what an agent still has to do to it” post. Point your domain’s MX at mx.socketlabs.com, stand up an HTTPS endpoint, and the message arrives, guarded by a validation-code handshake and a shared secret key you check yourself, carrying a spam score but no single trust verdict. That last gap is the one an autonomous agent trips on.

SL SocketLabs Inbound Parse → your agent sender SocketLabsMX + parse JSON POSTsecret key your handlervalidate + trust agentreads code InjectionAPI reply …the handler returns the validation code to activate, compares the shared secret key, and digs SPF/DKIM/DMARC out of raw headers before the agent trusts a byte. No signed webhook, no verdict block. MailKite → your agent sender MX edgeparse + auth JSON webhooksigned, auth block agent → mk.send()read code, reply the agent gets decoded text and an spf/dkim/dmarc verdict; the loop below is the whole thing
The same inbound email reaching an agent: a validation handshake, a secret-key check, and a header dig on SocketLabs, or a signed JSON message with an auth verdict on MailKite.

Here’s the whole bring-your-own-agent loop on MailKite. Email in, verify the signature in one call, hand the decoded body to your model, reply through the same client. 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;

  // Body is untrusted INPUT, never instructions (see the security note below).
  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 an agent that hears, thinks, and answers on its own. The companion repo is github.com/mailkite/demo-socketlabs-ai-agent with both sides wired up; open it in StackBlitz to run the loop against a signed sample event. The identical handler shape exists for Python, Ruby, Go, PHP, and Java; see the receiving docs and sending docs.

Where SocketLabs wins for agents, honestly

SocketLabs has been doing enterprise email since 2008, and if your agent’s job leans on sending well at volume, that pedigree is real:

  • Deliverability infrastructure that's a product, not an afterthought. StreamScore reputation scoring, Spotlight analytics, dedicated IPs, and a real deliverability team behind them. For a fleet of agents pushing transactional mail, that's genuine muscle you'd otherwise build.
  • Two clean send paths. The Injection API (JSON POST with a Bearer token) and a standard SMTP relay. Whatever your agent runtime already speaks, there's a way in.
  • Inbound Parse genuinely parses. You don't get raw MIME. SocketLabs deconstructs the message into headers, text and HTML bodies, and attachments, applies a spam score, and POSTs it as JSON. The heavy MIME lifting is done for you.
  • Enterprise support when you're at enterprise scale. Sales-assisted onboarding, dedicated support, optional rule-engine and monitoring add-ons. If you're sending a million-plus a month, that hand-holding is worth paying for.

If your agent mostly emits mail and inbound is a light “email us and we’ll POST it,” SocketLabs already does the job and this post isn’t asking you to move. It’s for the case where the agent has to read mail reliably: pull a verification code, follow a thread, and decide whether a sender is who they claim before acting. That decision is where the shape gets in the way.

What SocketLabs asks of an agent builder

The Inbound API is a webhook you own end to end. Before an activated endpoint even receives its first real message, it has to pass a handshake: SocketLabs sends a validation code and your endpoint must echo it back in the response body to prove you control the URL. After that, every POST carries a Secret Key and ServerId, and it’s on you to compare the key and return 401 if it doesn’t match. There’s no signed body, no replay window, no constant-time compare handed to you; the guard is a shared secret you check by hand. And crucially, there’s a spam score in the payload but no normalized SPF/DKIM/DMARC verdict, so to know whether the sender is real you parse the raw Authentication-Results header yourself.

Here’s the actual SocketLabs inbound relay handler an agent needs, doing all three jobs before the model sees anything:

// SocketLabs Inbound Parse: return the validation code, check the secret key,
// derive trust from raw headers — THEN run the agent.
import express from "express";

const app = express();
app.use(express.json()); // Inbound API POSTs application/json (endpoints created after June 2021)

app.post("/hooks/socketlabs", async (req, res) => {
  // 1) One-time setup handshake: echo the validation code so SocketLabs activates the URL.
  if (req.body.Type === "Validation") {
    return res.send(req.body.ValidationKey); // must return it in the body
  }

  // 2) No signed webhook. Compare the shared Secret Key yourself; reject on mismatch.
  if (req.body.SecretKey !== process.env.SOCKETLABS_SECRET_KEY) {
    return res.sendStatus(401);
  }

  // 3) No auth verdict block. Dig SPF/DKIM/DMARC out of the raw headers.
  const headers = req.body.Headers ?? {};
  const authResults = headers["Authentication-Results"] ?? "";
  const spfPass = /spf=pass/i.test(authResults);
  const dmarcPass = /dmarc=pass/i.test(authResults);
  const spammy = (req.body.SpamScore ?? 0) > 5;

  res.sendStatus(200);
  await runAgent({
    task: req.body.TextBody,           // SocketLabs' field shape, not yours
    from: req.body.From?.EmailAddress,
    trusted: spfPass && dmarcPass && !spammy,
  });
  // and the reply is a separate Injection API POST you compose and send
});

app.listen(3000);

Two things bite an agent builder here. First, security is homework: a validation-code echo, a shared-secret compare, and a 401 you remember to return, versus a single signed webhook. Get the compare wrong or leave the URL guessable and anyone can feed your agent instructions. Second, and worse for an agent specifically, there’s no trust verdict. An autonomous program that acts on email needs to know if the sender is authenticated before it weights a message’s instructions, and here that verdict is a regex over a header string you hope is present and well-formed.

A missing auth verdict is a security decision you didn't makeWhen there's no auth block, the easy path is to skip the check and trust every inbound message. For a human reading mail that's a shrug. For an agent that acts on mail, it means a spoofed From: becomes an instruction your model obeys. Deriving trust from raw headers is fine until the day the header isn't there.

The agent receive path, stage by stage

SL MX → mx.socketlabs.compoint your domain to receive return validation codeecho it back to activate the URL check the Secret Keyreturn 401 yourself on mismatch map SocketLabs' fieldstheir shape, into your inputs derive trust from headersparse Authentication-Results; no verdict agent + replymodel runs, reply via Injection API Every gray box is yours to build and operate. On MailKite the blue box is the only box: one signed JSON webhook in, one mk.send() out.
The SocketLabs inbound path an agent needs. MailKite collapses the validation, secret-key, mapping, and trust-derivation stages into a signed email.received webhook with an auth block.

The comparison, no adjective inflation

SocketLabs Inbound ParseMailKite
Built forEnterprise sending; inbound is a featureInbound → webhook, and sending
Payload to the agentJSON in SocketLabs’ field shapeSingle JSON email.received
Sender trustSpam score + raw headers; derive it yourselfauth{spf,dkim,dmarc,spam} in payload
Webhook securityValidation-code echo + shared Secret Key compareMailKite.verifyWebhook(sig, body, secret)
Signed body / replay windowNo; return 401 on key mismatch yourselfSigned, replay-windowed, constant-time
Reply / threadingSeparate Injection API call you composemk.send({ inReplyTo }), threaded
Managed agent loopNoneRoute with action: 'agent'
OnboardingSelf-serve low end, sales-led at scaleDNS-verify, no sandbox, no sales call
Entry pricingPaid from ~$40/mo; no standing free tierFree 3,000 msgs/mo, in + out

The through-line: SocketLabs is strong, mature sending infrastructure that happens to parse inbound too. For an agent that has to take mail in, decide whether to trust it, and reply, MailKite hands you a finished, signed, authenticated message instead of a handshake to implement and a header to parse.

What actually hits your agent’s webhook

Same inbound email, delivered parsed and signed. No validation dance, no secret-key compare, and an auth block so the agent never re-derives SPF/DKIM/DMARC from a header string:

{
  "id": "msg_2Hk9…",
  "type": "email.received",
  "from": { "address": "noreply@acme.dev" },
  "to": [{ "address": "agent@myapp.ai" }],
  "subject": "Your verification code is 481920",
  "text": "Enter 481920 to finish signing in. Code expires in 10 minutes.",
  "html": "<p>Enter <b>481920</b> to finish signing in.</p>",
  "threadId": "<a1b2c3@mail.acme.dev>",
  "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass", "spam": "ham" },
  "attachments": []
}

The auth block is the load-bearing part for an agent. Email From: is plain text, so a sender is trivially spoofable, which means the body is untrusted input and never instructions. Before your agent weights what a message tells it to do, it can read a real verdict instead of regexing a header. And that check is necessary, not sufficient: the real defense is architectural, bounding what a fooled agent can even do. The agent-security post is the honest write-up of that, and it’s worth reading before you point any loop at real mail.

If you’d rather not host the loop

MailKite, which we build, has a second mode: instead of running the receive→think→reply loop on your own server, make the route itself the agent. A route with action: "agent" carries a free-text prompt, and MailKite runs the model loop for you on a durable queue, records each run as a transcript, and replies with a threaded send_email tool call:

await mk.createRoute({
  match: "agent@myapp.ai",
  action: "agent",
  agentPrompt: "Complete signups: read verification codes from trusted senders and confirm. Escalate anything else.",
});

Same parsed, authenticated inbound edge either way. To start, DNS-verify the domain (SPF + DKIM to send, MX to receive). There’s no sandbox and no approval wait. If your app can only speak SMTP, the submission edge takes AUTH on :587/:465 so it can still send.

FAQ

Can SocketLabs receive inbound email for an AI agent? Yes. SocketLabs Inbound Parse (the Inbound API) accepts mail over SMTP, parses it into headers, text/HTML bodies, and attachments with a spam score, and POSTs it as JSON to a URL you configure after pointing your MX at mx.socketlabs.com. So an agent can read mail through it. The work you own is the security handshake and deriving sender trust; MailKite delivers a signed email.received webhook with an auth verdict instead.

How does an agent know whether to trust a SocketLabs inbound message? It parses the raw Authentication-Results header itself. SocketLabs applies a spam score but, as far as its public docs show, doesn’t hand you a normalized SPF/DKIM/DMARC verdict object. MailKite includes an auth{spf,dkim,dmarc,spam} block in every inbound payload, so the agent checks authentication before acting on the body.

Is the SocketLabs inbound webhook signed? Not with a body signature. Setup uses a validation-code handshake (your endpoint echoes a code to activate the URL), and each POST carries a shared Secret Key and ServerId you compare, returning 401 on a mismatch. There’s no HMAC-over-body, replay window, or constant-time compare provided. MailKite signs every webhook and MailKite.verifyWebhook checks the signature, replay window, and constant-time compare in one call.

Can I keep SocketLabs for sending and use MailKite only for inbound? Yes. They’re independent. Point an address at MailKite for the agent’s inbound and authenticated replies, and keep SocketLabs’ Injection API or SMTP relay for whatever outbound volume you already run there. MailKite’s inbound is a plain HTTPS webhook, so it drops in next to your existing sending.

Is SocketLabs a good fit for spinning up a single agent inbox quickly? It’s built for enterprise sending, so onboarding leans self-serve at the low end and sales-assisted at scale, with paid plans starting around $40/month and no standing free tier at the time of writing. If you just want one agent reading and answering mail today, MailKite’s free tier (3,000 messages/month, in and out) and DNS-only setup with no sandbox is the lighter path.


If your agent’s inbox is really a validation handshake, a secret-key compare, and a header regex bolted onto a sending platform, there’s a cleaner shape. Clone the demo repo (or run it in your browser), then point a domain at MailKite and your agent’s next inbound email arrives as parsed, authenticated JSON.

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

Discuss this post: Hacker News Share on X

Related posts