SMTP2GO is a clean, drop-in SMTP relay and Email API for sending — genuinely one of the least fussy outbound options around. But it can't receive email at all, so the moment your agent needs to read a verification code or a reply you bolt on a second provider (an IMAP mailbox you poll). MailKite gives the agent a real inbox as parsed JSON, threads and authenticates it, and sends from the same API.
For pure outbound SMTP2GO is excellent: a drop-in relay at mail.smtp2go.com:587, a genuine 1,000/mo free tier, managed IPs and reputation you don't operate, and outbound delivery webhooks (delivered, open, click, bounce, spam). If your agent only ever emits mail, it's a fine, minimal choice and MailKite doesn't undercut it on relay simplicity.
SMTP2GO covers exactly one arrow of an agent's loop — the send. It has no path for incoming mail, so a verification code or reply can't come back through it. MailKite receives too, delivering each incoming message as a parsed email.received webhook.
To receive alongside SMTP2GO you stand up a separate mailbox with its own MX, poll it over IMAP (OAuth refresh, dedupe, keepalive), parse the MIME yourself, and re-derive SPF/DKIM/DMARC by hand. MailKite collapses all of that into one signed JSON webhook.
Read an inbound message and reply to it from the same account, SDK, and quota — with resolved threadId and inReplyTo so the reply threads. No two vendors and no glue between them.
Run every product, client, and agent alias from one account at no per-domain cost, on a combined in + out quota with graceful overage that never hard-cuts mid-flow.
| MailKite | SMTP2GO | |
|---|---|---|
| Outbound send | SMTP submission + Send SDK/API | SMTP relay + Email API |
| Inbound receive | Parsed JSON webhook | None (send-only) |
| Agent inbox (codes, replies, tasks) | Built in | Needs a 2nd provider (IMAP mailbox) |
| MIME parsing | Done at the edge | Yours, on the 2nd mailbox |
| SPF/DKIM/DMARC on inbound | In the auth block | Re-derive it yourself |
| Threading / reply-from | Resolved (threadId, inReplyTo) | Yours to track |
| Webhooks | email.received (an incoming message) | Outbound events only (delivered, open, bounce) |
| Run the agent for you | Route with action: 'agent' | No |
Competitor capabilities change — we re-audit these tables regularly. Spot something out of date? Tell us and we'll fix it.
SMTP2GO does the send arrow well; the receive arrow is a second vendor, a long-lived IMAP connection, MIME parsing, and auth you re-derive. MailKite makes inbound one signed JSON webhook.
// Outbound is easy — SMTP2GO is a drop-in relay.
import nodemailer from "nodemailer";
const relay = nodemailer.createTransport({
host: "mail.smtp2go.com", port: 587,
auth: { user: process.env.SMTP2GO_USER, pass: process.env.SMTP2GO_PASS },
});
await relay.sendMail({ from: "agent@myapp.ai", to, subject, html });
// Inbound has no SMTP2GO path. Stand up a SECOND mailbox and poll it:
import { ImapFlow } from "imapflow";
import { simpleParser } from "mailparser";
const imap = new ImapFlow({
host: "imap.fastmail.com", port: 993, secure: true,
auth: { user: process.env.IMAP_USER, pass: process.env.IMAP_PASS },
});
await imap.connect();
for await (const msg of imap.fetch({ seen: false }, { source: true })) {
const mail = await simpleParser(msg.source); // MIME parsing is yours
// SPF / DKIM / DMARC? re-derive them yourself before trusting the sender.
await runAgent({ task: mail.text, from: mail.from?.text });
} 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=…" }
]
} SMTP2GO's free tier counts outbound only; MailKite's 3,000/mo covers both directions, so the receiving half doesn't mean a second bill.
MailKite's inbound is a plain HTTPS webhook, so it drops in next to whatever you send with — point a domain at MailKite for the inbox and let SMTP2GO keep relaying, or move both onto MailKite.
Add the MailKite MX records so incoming mail actually has somewhere to land — or start on a managed subdomain while DNS propagates. No sandbox, no approval queue.
Verify x-mailkite-signature with the SDK's verifyWebhook helper, read event.from.address, event.text and event.auth, then reply through the same Send API — retiring the second mailbox and the IMAP poller.
No. SMTP2GO is an outbound SMTP relay and Email API; its webhooks report events for mail you sent (delivered, open, click, bounce, spam), and its own docs direct you to your mailbox provider's IMAP/POP for incoming mail. To receive you need a separate mailbox or an inbound-capable provider. MailKite delivers incoming mail as a parsed email.received JSON webhook.
It can send through SMTP2GO, but it can't receive — so it can't read verification codes, magic links, or replies through SMTP2GO alone. You'd bolt on a second provider (an IMAP mailbox you poll) for the receiving half. MailKite does both send and receive under one domain and one API.
No. Keep SMTP2GO as your outbound relay and use MailKite for the agent's inbox, or consolidate both onto MailKite later since sending shares the same quota. MailKite's inbound is a plain HTTPS webhook, so it slots in beside whatever you send with.
The SMTP2GO alternative for AI agents →
The long-form take on our blog — receipts, runnable code, and where we won't overclaim.
Point a domain, drop in a webhook URL, receive your first email. Unlimited domains, no credit card.