ZeptoMail is Zoho's transactional sending service — cheap, disciplined, and genuinely good at outbound. But it never receives mail, so the moment an agent or app has to read a login code or a reply you bolt on a Zoho Mail mailbox and poll it over IMAP: two providers, two credentials, MIME parsing on your side. MailKite (which we build) does both legs in one API, delivering inbound as parsed JSON.
For pure outbound, ZeptoMail is a strong, cheap leg: prepaid credits with no monthly floor, a deliberately transactional-only shared IP pool that keeps one-to-one mail landing, and both a REST endpoint and an SMTP relay. If your agent only ever speaks and never listens, it's a good choice.
ZeptoMail is send-only, so to let an agent read anything you add a Zoho Mail mailbox and poll it over IMAP — a second product, a second login. MailKite receives and sends under one account with a shared quota.
No IMAP poll loop to babysit, no simpleParser, no \Seen flag to set atomically. MailKite decodes at its MX edge and POSTs one JSON object: text, HTML, resolved threadId, and signed attachment URLs.
ZeptoMail's inbound path (Zoho Mail over IMAP) hands you raw headers to verify yourself. MailKite includes an auth block — spf, dkim, dmarc, spam — on every received event, which matters when an email is untrusted agent input.
Run every product, client, and alias from one account at no per-domain cost — and the free tier is ongoing (3,000 msgs/mo in + out), not a short-validity trial credit.
| MailKite | ZeptoMail | |
|---|---|---|
| Send (API + SMTP) | Send API + SMTP submission edge | REST + SMTP relay, transactional-only |
| Inbound / receive | Yes — parsed JSON webhook | None — send-only service |
| Read a verification code / reply | It's in the email.received event | Bolt on Zoho Mail, poll IMAP |
| Providers to run an agent inbox | One | Two (ZeptoMail out + Zoho Mail in) |
| Inbound trust signal | Structured auth block in payload | Derive SPF/DKIM/DMARC yourself |
| Threaded reply from sent address | inReplyTo, reply from event.to[0] | Set headers yourself |
| Per-domain fees | None — unlimited domains | None (credits by volume) |
| MCP server + Claude Code plugin | Yes | None |
Competitor capabilities change — we re-audit these tables regularly. Spot something out of date? Tell us and we'll fix it.
Two providers, two credentials, and the whole decode-and-verify step on your side — versus one signed JSON POST with body, threading, and auth already resolved.
// OUT — ZeptoMail send. Zoho-enczapikey token, nested to[].email_address.
await fetch("https://api.zeptomail.com/v1.1/email", {
method: "POST",
headers: { Authorization: `Zoho-enczapikey ${ZEPTO_SEND_TOKEN}` },
body: JSON.stringify({
from: { address: "agent@yourco.dev" },
to: [{ email_address: { address: replyTo } }],
subject: `Re: ${subject}`,
htmlbody: answer.html, // no inReplyTo/threading helper
}),
});
// IN — ZeptoMail can't receive. Bolt on Zoho Mail, poll over IMAP:
const imap = new ImapFlow({
host: "imap.zoho.com", port: 993, secure: true,
auth: { user: "agent@yourco.dev", pass: ZOHO_APP_PASSWORD },
});
await imap.connect();
await imap.mailboxOpen("INBOX");
for await (const msg of imap.fetch({ seen: false }, { source: true })) {
const mail = await simpleParser(msg.source); // MIME → fields is yours
// dedupe, mark \Seen atomically, verify SPF/DKIM/DMARC: also yours
} 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=…" }
]
} For pure outbound at volume ZeptoMail's credits are very cheap. But a receive-and-reply agent means also running Zoho Mail plus your own IMAP pipeline, so total cost and setup usually favor one provider.
Point your domain's MX at MailKite to receive, and verify SPF + DKIM to send. You can retire the Zoho Mail mailbox and IMAP poller you bolted on just to read replies.
Drop ImapFlow, simpleParser, the \Seen bookkeeping, and the SPF/DKIM checks. Set your webhook URL and read event.from.address, event.subject, event.text, event.auth.
Swap the ZeptoMail send call for mk.send() — one account, one token. Use inReplyTo and reply from event.to[0] so the thread stays intact.
No. ZeptoMail is Zoho's transactional sending service — no inbound webhook, no inbound parse, no IMAP. Its webhooks report events on mail you already sent (bounces, opens, clicks, complaints), not messages arriving. To read mail you add a separate product, Zoho Mail, over IMAP.
ZeptoMail sends transactional email (API + SMTP) and never receives. Zoho Mail is the actual mailbox product with IMAP/POP/SMTP. An agent that must both send and read on Zoho ends up running both, with two logins and two credentials. MailKite does both halves under one account.
For pure outbound at volume, ZeptoMail's prepaid credits (one credit sends 10,000 emails, no monthly floor) are very cheap. But an agent inbox on ZeptoMail means also paying for and running Zoho Mail plus your own IMAP pipeline. MailKite starts free at 3,000 messages/mo in and out with no per-domain fee, so a receive-and-reply workload usually favors one provider.
The ZeptoMail 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.