Get your API key
AI & agents

Connect your agent

Give your agent a real email address it can send from, receive to, and act on. Point any MCP client at the hosted remote MCP serverhttps://mcp.mailkite.dev/mcp — and sign in over OAuth in the browser, no key to copy. In Claude Code it's two lines with the MailKite plugin. Prefer a static key or offline use? Run the server locally. Works with Claude Code, Cursor, VS Code, Windsurf, Codex, Gemini, and any MCP client. Prefer code? It's the same REST API as everything else.

Why give an agent an inbox

  • Email is your agent's identity. Sign-ups, verification, and 2FA codes land in an inbox the agent owns and reads — no OAuth, no human in the loop.
  • Inbound it can act on. Received mail is parsed to clean JSON and kept in a queryable store, so the agent reasons over the full message and replies in-thread.
  • One inbox per agent. Unlimited domains and addresses — a fleet of agents means a fleet of inboxes, with no per-domain tax.

Drop-in AI skill

Prefer a coding agent? The MailKite agent skill wires email into your agent with one command — then it drives the whole lifecycle (domains, DNS, webhooks, sending, inbound) over MCP, the CLI, any SDK, or REST. Works in Claude Code, Cursor, Codex, Amp, and any agent that reads Agent Skills:

terminal
npx skills add mailkite/agent-skills

View on GitHub Install guide →

How much email should the agent control?

You decide the agent's reach — the whole account, one domain, or a single mailbox — and each grant is revocable on its own:

Account key

*

Every domain, every mailbox

One key runs all your email: the agent can create domains, set webhooks, send and read everywhere — unlimited domains and mailboxes. For your own trusted automation.

Domain-scoped key

*@agents.yourapp.com

Every mailbox on one domain

The agent sends, reads, and replies on that domain and nothing else — the rest of your account is out of reach. Mint one per agent; revoke one without disturbing the others.

Agent route

support@yourapp.com

One mailbox

A route hands one address's mail to your agent — that inbox is all it sees and all it replies from. The narrowest grant: the agent never holds a key at all.

The first two grants are API keys — step 1 below. The third needs no key on the agent's side: a route with action agent hands one address's inbound to a built-in inbox agent that reads and replies in-thread there, and nowhere else.

Step 1 — Get your API key

MailKite has two kinds of key, both mk_live_… tokens — the first two rungs of the ladder above:

  • Account key — one per account, unrestricted across every domain: it can create & register domains, verify DNS, set webhooks, send, and read inbound. Great for getting started and for your own trusted code.
  • Domain-scoped key — tied to a single domain: it can send from it, manage it, and read its mail and routes, and nothing else. It can't touch your other domains, mint keys, or change account-wide settings. Recommended for agents — and the right choice for anything in production or multi-tenant, where each agent (or tenant) should be confined to its own domain and revocable on its own.

Open the dashboard. The account key is shown on your first screen and under Settings → API key. To mint a domain-scoped key, go to API keys, pick a domain, give it a label (e.g. support agent), and create it. Hand whichever you chose to your agent as MAILKITE_API_KEY — the rest of this guide works the same either way:

.env
MAILKITE_API_KEY=mk_live_3a9f…
Treat the account key like a superadmin password — it's here for convenience, to get you up and running immediately. For production, mint a domain-scoped key per agent so each one is confined to its own domain and revocable on its own, without disrupting everything else. Either way, keep it in an env var or secrets vault, never commit it to a repo, and rotate the account key or revoke any scoped key anytime from the dashboard (it stops working at once).

Step 2 — Connect your agent to MailKite Remote MCP

MailKite's hosted remote MCP server exposes email as tools — including reading inbound, which most email MCPs skip. Any MCP client that speaks Streamable HTTP connects to one URL:

MailKite remote MCP — Streamable HTTP
https://mcp.mailkite.dev/mcp

Pick your agent below and copy the snippet. Every path signs you in over OAuth in the browser — no key to paste — and if you're already in the dashboard it connects with no extra login. The server is open source at github.com/mailkite/mailkite-mcp.

Using a chat-based agent (claude.ai, ChatGPT, your own assistant)? Paste this into the prompt — it adds the server and walks you through browser sign-in.

paste into your agent
Connect to the MailKite MCP server (Streamable HTTP) at
https://mcp.mailkite.dev/mcp and authenticate in the browser when prompted.
Then use the mailkite_* tools to send, read inbound, and reply to email.

On VS Code, Codex, or an offline/local server? Those are just below. Whichever path you pick, the same mailkite_* tools appear.

Run the server locally — static key, offline, or a custom base URL

Prefer a static key, offline use, or a custom base URL? Run @mailkite/mcp locally over stdio via npx (no install; needs Node 18+). The key from step 1 goes in as MAILKITE_API_KEY. Unlike the remote server it runs fully offline — mailkite_verify_webhook needs no network at all — and it honors MAILKITE_BASE_URL to target a staging API.

One command registers it — add -s user to enable it across every project.

terminal — Claude Code
claude mcp add mailkite \
-e MAILKITE_API_KEY=mk_live_3a9f… \
-- npx -y @mailkite/mcp

Already use the CLI? mailkite mcp launches the same local server with your saved token.

Whichever path you pick, the 23 mailkite_* tools below appear — the five inbox-agent tools are rolling out in preview. The server is a thin layer over the MailKite SDK and the same contract it uses: one tool per endpoint, with inputs validated against the shared JSON Schemas before any request is made — so a tool can't put a malformed call on the wire. A couple of tools (like mailkite_verify_webhook) run locally, with no API call at all.

What your agent can do

ToolWhat it does
Sending
mailkite_sendSend a message over a verified domain — HTML, text, cc/bcc, attachments, and in-thread replies via inReplyTo.
mailkite_send_batchSend one personalized message per recipient (up to 50) in a single call — per-recipient templateData/headers merged over the shared base; per-recipient results.
mailkite_upload_attachmentUpload a file and get a secure, time-limited URL to attach in send (filename + url) or link inline — instead of base64-inlining large files.
Inbound messages
mailkite_list_messagesList stored messages, newest first.
mailkite_get_messageFetch one message in full — body, headers, deliveries, and attachment links.
mailkite_retry_deliveryRe-deliver a stored message to its webhook.
mailkite_create_realtime_tokenMint a short-lived, single-use token for one Realtime API connection (GET /v1/realtime). The stream itself is not an MCP tool — a tool call cannot hold a subscription open.
Domains
mailkite_registerCreate an account from just an email — returns the API key; sending stays blocked until the address is verified.
mailkite_meThe account behind this credential: email, verification state, plan.
mailkite_register_oauth_clientRegister an OAuth client (RFC 7591) to link a user's existing MailKite account.
mailkite_exchange_oauth_tokenExchange an authorization code for an access token, or rotate a refresh token.
mailkite_get_api_keyGet the account's API key (read-or-create).
mailkite_rotate_api_keyRotate the account API key; the old one stops working immediately.
mailkite_list_scoped_keysList domain-scoped API keys (one per site/CI job).
mailkite_create_scoped_keyCreate a key limited to one domain.
mailkite_delete_scoped_keyRevoke a domain-scoped key.
mailkite_list_app_passwordsList app passwords — mailbox credentials scoped to a domain + address pattern.
mailkite_create_app_passwordCreate an app password for one domain and address pattern (imap and/or api).
mailkite_update_app_passwordChange an app password's label, address pattern or protocols. Its domain is fixed for life.
mailkite_rotate_app_passwordReplace an app password's secret, keeping its scope. The old secret stops working immediately.
mailkite_delete_app_passwordRevoke an app password; IMAP sessions and API calls using it stop immediately.
mailkite_list_mailbox_messagesList a mailbox's messages, newest first — an agent reading its own inbox over HTTPS.
mailkite_get_mailbox_message_rawFetch one message's raw RFC822 bytes from a mailbox.
mailkite_set_mailbox_message_flagsReplace a message's IMAP flags (e.g. mark it Seen).
mailkite_get_usageThis period's email/AI usage vs the plan's included bucket.
mailkite_list_suppressionsList addresses this account will not send to.
mailkite_add_suppressionSuppress an address (unsubscribe, bounce, complaint, or manual).
mailkite_remove_suppressionRemove an address from the suppression list.
mailkite_list_domainsList your domains, each with its webhook URL.
mailkite_create_domainAdd a domain; returns the DNS records to set.
mailkite_suggest_subdomainSuggest a free, unclaimed subdomain label to prefill an input with.
mailkite_check_subdomainCheck whether a free subdomain label can be claimed.
mailkite_claim_subdomainClaim a free MailKite subdomain (<label>.<base>) — verified instantly, no DNS to publish.
mailkite_get_domainGet one domain with DNS records + webhook.
mailkite_verify_domainRe-check DNS and update verification status.
mailkite_delete_domainRemove a domain.
mailkite_check_domain_availabilityCheck whether a domain is available to register, with pricing.
mailkite_register_domainRegister a domain through MailKite and auto-provision its DNS.
Webhooks
mailkite_set_webhookSet or replace the domain's catch-all webhook.
mailkite_delete_webhookRemove the domain's webhook.
mailkite_set_webhook_eventsOpt the domain's inbound webhook into engagement events — one webhook, all events; pass "all" or a list of email.* types.
mailkite_delete_webhook_eventsOpt back out of engagement events on the inbound webhook (email.received only — the default).
mailkite_set_tracking_webhookSplit-endpoint override: send engagement events to a dedicated URL instead of the inbound webhook.
mailkite_delete_tracking_webhookRemove the domain's dedicated tracking-event webhook.
mailkite_test_webhookSend a signed test event to the webhook.
mailkite_verify_webhookVerify an x-mailkite-signature header on an inbound delivery — runs locally, no API call.
Routes
mailkite_list_routesList inbound routing rules.
mailkite_create_routeCreate a route (match, action, destination) — action is webhook, forward, store, drop, or agent (hand inbound to an inbox agent).
mailkite_delete_routeDelete an inbound routing rule by id — pair with create_route to register and tear down a webhook destination (e.g. an automation subscribing on enable, cleaning up on disable).
mailkite_agentSend a message to one of your inbox agents and get its reply — defaults to the account's default agent; routeId/address pick a specific one, model overrides the model.
mailkite_routeRoute a message to one of your registered routes (by routeId or address), running that route's action — agent, webhook, or forward.
Templates
mailkite_list_templatesList your saved email templates.
mailkite_list_base_templatesList the built-in base templates you can start from.
mailkite_get_templateFetch one template (subject + rendered HTML/text).
mailkite_create_templateSave a new email template, then send it with templateId + templateData.
Contact lists
mailkite_list_listsList your contact lists (static, curated broadcast audiences), each with its member count.
mailkite_create_listCreate a contact list; add contacts with add_list_contacts.
mailkite_get_listGet one contact list with its member count.
mailkite_update_listRename a contact list.
mailkite_delete_listDelete a contact list — the list is removed, the contacts themselves are kept.
mailkite_list_list_contactsList the contacts that are members of a list.
mailkite_add_list_contactsAdd contacts (by id, ctr_…) to a list; returns how many were newly added.
mailkite_remove_list_contactRemove one contact from a list (the contact itself is kept).
Broadcasts
mailkite_list_broadcastsList your broadcasts (one-to-many sends) with status and send stats.
mailkite_create_broadcastCreate a broadcast draft — from is required; set audience to { type: "all" } or a list.
mailkite_get_broadcastGet one broadcast with its status and recipient summary.
mailkite_update_broadcastEdit a draft broadcast (from/subject/audience/html/…). Drafts only.
mailkite_delete_broadcastDelete a broadcast draft.
mailkite_send_broadcastSend a broadcast now, or pass an ISO 8601 scheduledAt to schedule it — a one-click unsubscribe is always added.
Documentation
mailkite_semantic_searchSemantic search over the MailKite documentation — the most relevant doc sections for a natural-language query.
Inbox agents (preview)
mailkite_create_agentCreate a built-in inbox agent — instructions, reply mode, spam, tags, escalation. Preview.
mailkite_list_agentsList your inbox agents. Preview.
mailkite_get_agentFetch one inbox agent's config. Preview.
mailkite_update_agentUpdate an inbox agent's config. Preview.
mailkite_delete_agentRemove an inbox agent. Preview.

Run your own agent loop

If you run your own agent loop, route the inbound webhook straight into it, then take the agent's response and hand it to the SDK's send with inReplyTo (the Send API under the hood) to respond in-thread:

Don't want to host a loop at all? A built-in inbox agent can triage, reply, filter spam, tag, and escalate in MailKite's pipeline — no server to run. It runs on Claude at a flat $0.10 per action, or free on your own Anthropic key. Running your own agent over the webhook is free too — you just pay your model provider.

agent hook
import { MailKite } from "mailkite";

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

// Hand inbound mail to your agent loop, then send its reply.
app.post("/hooks/mailkite", async (req, res) => {
res.sendStatus(200); // ack fast
const event = req.body;
if (event.type !== "email.received") return;

// 1. Let the agent reason over the message and produce a reply.
const reply = await agent.run({
role: "user",
content: `New email from ${event.from.address}: ${event.subject}\n\n${event.text}`,
});

// 2. Feed the agent's response straight to the Send API, in-thread.
await mk.send({
from: event.to[0].address, // the agent's own address
to: event.from.address,
subject: `Re: ${event.subject}`,
text: reply.content,
inReplyTo: event.threadId, // keeps it in the same conversation
});
});
Install Docs →
Give each agent its own address — a per-address route (mailkite_create_route) or its own domain (mailkite_create_domain) — so its mail, context, and replies stay cleanly separated.

Ready to wire one up? Create an agent inbox or read the API reference.