Get your API key
AI & agents

Inbox agents Beta

MailKite's job is the plumbing — inbound email becomes clean JSON at your webhook, and one API sends the reply. Inbox agents are the optional AI layer on top: they read each message, decide what it means, and act — replying in-thread or calling any MailKite tool (send, open a route, look up past mail…) the way your prompt tells them to. It's the same agent that powers the assistant in your dashboard, pointed at inbound mail. Switch one on per address, or leave a domain as a plain email→webhook pipe — email stays the product; AI is a capability you opt into.

Two ways to put AI on inbound

Both are first-class. Pick per address.

ApproachWho runs the agentStatus
Bring your own agent You do — inbound webhook → your model/loop → the Send API to reply in-thread. See Agent inboxes & MCP. Available today
Built-in inbox agent MailKite does — point a route at action: "agent" with a prompt; it runs in our pipeline, replies, and can call any tool. No server, no loop to host. Beta
This is the inverse of Agent inboxes & MCP, which gives your agent an email identity. Inbox agents are about MailKite acting on the mail that arrives — and the two compose.

Configure one

There's no separate object to manage: an inbox agent is a route whose action is agent, carrying the agentPrompt that programs it. Point an address at it and you're done — no webhook endpoint to host.

route an address to an AI agent
import { MailKite } from "mailkite";

const mk = new MailKite(process.env.MAILKITE_API_KEY);

// One route turns an address into an AI inbox agent. The prompt is the program; the
// agent can reply to the sender or forward to an address you approve (agentForwardTo).
await mk.createRoute({
match: "support@myapp.ai",
action: "agent",
agentPrompt:
"Answer billing and account questions for myapp.ai. Reply in-thread. " +
"Escalate anything you can't resolve to humans@myapp.ai.",
agentForwardTo: ["humans@myapp.ai"],
});
Install Docs →

When mail arrives at support@myapp.ai, MailKite runs the agent with your prompt and the parsed message. It reasons, calls whatever tools it needs, and (per the prompt) replies in-thread from the receiving address. The run happens in the background, so inbound stays fast.

What the agent can do

The sender of an inbound email is untrusted — anyone can email your address — so an inbox agent is deliberately locked down. It has exactly two actions, both constrained so a hostile email can't turn the agent against your account:

  • Reply to the person who wrote in — in-thread, from the address that received the mail. It chooses the words; it cannot change who the reply goes to or send from another domain.
  • Forward the email to an address you control — your account email, any address on a domain you own, or an address you pre-approve on the route (agentForwardTo). Any other destination is refused. This is how you let it escalate or hand off.

That's it. An inbox agent cannot read your other messages, list or change your domains, routes, or webhooks, or send to an arbitrary address — those are not tools it is given. (Your dashboard assistant / MCP server can do all of that, because they're authenticated as you, not driven by an inbound stranger.) So “answer billing questions; escalate refunds to refunds@myapp.ai” becomes a reply and a forward — with refunds@myapp.ai in agentForwardTo.

Guardrails. The email body is treated as untrusted input (instructions inside it are data, not commands); the agent won't reply to no-reply/automated senders, acts at most once per message, and can only reply to the sender or forward to an address you control. It can never read or change the rest of your account.

The dashboard assistant

The same agent runs as a chat in your dashboard (the Assistant tab). Ask it to add a domain, show DNS, wire a webhook, set up an inbox-agent route, or find a message — it calls the same tools and streams its work. It's the fastest way to try a prompt before you put it on a route.

Planned: structured spam, tags & escalation

Today everything is expressed through the free-text agentPrompt plus tools, which already covers triage, replies, and escalation. First-class, typed configuration is the next step:

FieldValuesPurpose
agentPromptstring · availableThe agent's instructions — its job, tone, and rules.
agentForwardTostring[] · availableExtra addresses the agent may forward to (beyond your account email and your own domains). Your escalation/hand-off inboxes.
reply.modeauto · draft · off · plannedSend the reply, hold it for human approval, or never reply.
spam.actionquarantine · tag · plannedDrop junk before the model runs, or just label it.
tagsstring[] · plannedA first-class label vocabulary, emitted on the webhook.

When that lands, an agent route can also forward the inbound event to your own endpoint, enriched with the spam verdict, tags, and what the agent did — so your code stays in the loop. The planned shape:

email.received (planned fields)
{
"type": "email.received",
"from": { "address": "ada@example.com" },
"to": [{ "address": "support@myapp.ai" }],
"subject": "Can't update my card",
"text": "Hi — my payment keeps failing…",
// ↓ planned: added when first-class spam/tagging runs in front of the agent
"spam": { "verdict": "ham", "score": 0.03 },
"tags": ["billing"],
"agent": { "id": "agt_…", "action": "replied", "replyId": "msg_…" }
}

Bring your own instead

Already have an agent? Skip the built-in one: route the address to a webhook and run your own loop — read the parsed JSON, reason over it, and reply with the SDK's send + inReplyTo. The full pattern, in seven languages, is in Agent inboxes & MCP.

Here's the whole loop in one handler — verify the signature with the MailKite SDK, hand the message to Claude, then reply in-thread with send + inReplyTo. Node and Python drive Claude with the Claude Agent SDK; the other SDKs have no Agent SDK, so they call Claude through the Anthropic Messages API. Set ANTHROPIC_API_KEY and MAILKITE_WEBHOOK_SECRET in the environment. The handler runs inside a try/catch: on success it returns 200; if the agent or send throws, it returns a non-2xx and MailKite redelivers the event, retrying with exponential backoff.

agent reply
import express from "express";
import { MailKite } from "mailkite";
import { query } from "@anthropic-ai/claude-agent-sdk"; // reads ANTHROPIC_API_KEY

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

// Verify the exact bytes — capture the RAW body, not a re-serialized object.
app.use("/hooks/mailkite", express.raw({ type: "application/json" }));

app.post("/hooks/mailkite", async (req, res) => {
// 1. Verify the signature with the SDK before trusting anything.
if (!mk.verifyWebhook(req.headers["x-mailkite-signature"], req.body, SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
if (event.type !== "email.received") return res.sendStatus(200);

try {
// 2. Hand the message to Claude via the Claude Agent SDK; collect its reply.
const prompt = `New email from ${event.from.address}: ${event.subject}\n\n${event.text}`;
const stream = query({ prompt, options: { model: "claude-opus-4-8" } });
let reply = "";
for await (const msg of stream) {
if (msg.type === "result" && msg.subtype === "success") reply = msg.result;
}

// 3. Send Claude's reply back in-thread with the MailKite SDK.
await mk.send({
from: event.to[0].address, // the agent's own address
to: event.from.address,
subject: `Re: ${event.subject}`,
text: reply,
inReplyTo: event.threadId, // keeps it in the same conversation
});
res.sendStatus(200);
} catch (err) {
// Non-2xx → MailKite redelivers, retrying with exponential backoff.
console.error(err);
res.sendStatus(500);
}
});
Install Docs →

What it costs

The built-in inbox agent runs on Claude, and pricing is simple and pay-as-you-go: $0.10 per AI action — one agent run over one inbound email — the same on every paid plan, with no bundles or token math. Prefer to run on your own key? Add your Anthropic API key in the dashboard and AI is free — we never bill it. Bringing your own agent over a plain webhook is free too; you just pay your own model provider. See Pricing for the full breakdown.

Next: Agent inboxes & MCP for the bring-your-own pattern, or Inbound webhooks for the raw event your agent reasons over.