What is programmable email?
Programmable email is email you drive entirely from code: send with one API call, receive every message as JSON on a webhook, and give any app or AI agent its own inbox on a domain you control, with no mail server to run. The developer definition, the three primitives, and runnable code.
Programmable email is email you drive entirely from code. You send with one API call, receive every inbound message as structured JSON on a webhook, and give any app or AI agent its own inbox on a domain you control, with no mail server to run. Send, receive, and identity all become things you call, not infrastructure you operate.
That last part is what makes it programmable rather than just “an email API.” An email API sends. Programmable email closes the loop: mail flows out through code and back in through code, on the same domain, in the same shape.
Two things called “programmable email”
Search the term and you’ll hit two different meanings, so it’s worth separating them up front.
The first is the marketing one. HubSpot popularized “programmable email” for HubL-templated campaign personalization: dynamic content blocks, CRM merge fields, and conditional logic rendered into a marketing send. That’s a templating feature inside a marketing tool, aimed at marketers. It’s real, and it’s not what this post is about.
The second is the developer one, and it’s the one that matters if you write code: email as a programmable I/O primitive. Not a nicer template language for campaigns, but a way to treat an inbox the way you treat an HTTP endpoint or a database row: something your program reads from and writes to. When a developer says “give my app a programmable email address,” they mean send + receive + a real inbox they own, all reachable from an SDK. The rest of this post is that definition.
The three primitives
Programmable email, the developer kind, is three capabilities that compose:
1. Send — one POST, one API key. You call a single endpoint with a from, a to, a subject, and a body, and the message goes out over an authenticated, warmed domain. No SMTP socket to hold open, no queue to run.
2. Receive — inbound arrives as parsed JSON on a webhook. You point a domain’s MX record at the provider, and every message to that domain is decoded at the edge and POSTed to your endpoint as clean JSON: decoded text and html, a resolved thread ID, attachments, and an auth verdict. No IMAP poll, no MIME parser, no mail server.
3. Identity — an inbox per app or agent, on a domain you own. billing@yourco.dev, agent@yourco.dev, support+ticket42@yourco.dev are all addresses you spin up and route in code, not accounts a human logs into.
Compose the three and email stops being a channel you bolt on at the end and becomes a two-way interface your program speaks natively.
What it looks like in code
Sending is one call. Here it is in Node, though the same shape exists across the Node, Python, PHP, Java, Go, and Ruby SDKs:
import { MailKite } from "mailkite";
const mk = new MailKite(process.env.MAILKITE_API_KEY);
await mk.send({
from: "hello@yourco.dev",
to: "ada@example.com",
subject: "Your report is ready",
html: "<p>Download it any time in the next 7 days.</p>",
});
Receiving is one webhook handler. An inbound message lands as an email.received event, already decoded, with a signature you verify in one line:
import express from "express";
import { MailKite } from "mailkite";
const app = express();
app.use("/hooks/mail", express.raw({ type: "application/json" }));
app.post("/hooks/mail", (req, res) => {
if (!MailKite.verifyWebhook(req.headers["x-mailkite-signature"], req.body, process.env.MAILKITE_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
if (event.type === "email.received") {
console.log(`${event.from.address} said: ${event.text}`);
}
res.sendStatus(200);
});
app.listen(3000);
That’s the whole primitive. Everything hard — the MX server, the MIME decoding, SPF/DKIM/DMARC, TLS, retries — happens before the request reaches your code. The Node inbound walkthrough shows the exact payload shape field by field, and reply-by-email in your app closes the loop by threading a response back out.
Why “programmable email” beats “email API”
An email API is send-only by convention. It’s a one-way pipe: your app talks, the world listens. That covers transactional mail — receipts, resets, verification codes — and stops there.
Programmable email is bidirectional by definition. Because receiving is a first-class primitive, whole categories of feature open up that a send-only API can’t reach:
That last one is why the term is having a moment. An autonomous agent is useless if it’s deaf to everything that arrives by email, and email is still how the rest of the world sends verification codes, invoices, and replies. Give the agent a real inbox and it can participate. That’s the whole idea behind giving your AI agent its own inbox.
Who it’s for
Programmable email is for the developer who’d otherwise be standing up Postfix, wrangling mailparse, and babysitting an IP’s reputation just to let their app hear back from a user. It’s for the SaaS builder who ships several products and wants each on its own domain without running mail infrastructure per product. And increasingly it’s for the agent builder who needs to hand a model an inbox it can operate on its own.
The common thread: you want email to behave like the rest of your stack — an SDK call, a JSON payload, a signed webhook — instead of a category of ops you have to keep alive.
FAQ
What is programmable email, in one sentence? Email you drive entirely from code — send with one API call, receive every message as JSON on a webhook, and give any app or agent its own inbox on a domain you control, with no mail server to run.
How is it different from an email API? An email API is usually send-only. Programmable email adds receiving as a first-class primitive, so mail flows both directions through code on the same domain. That’s what enables reply-by-email, support inboxes, inbound parsing, and agent inboxes.
Do I need to run a mail server? No. You point a domain’s MX at the provider and call an API to send. The SMTP server, MIME parsing, and SPF/DKIM/DMARC are handled for you; your app only ever sees JSON and makes API calls.
Can an AI agent have its own programmable email inbox?
Yes. Give it a scoped address on your domain, and inbound mail arrives as an email.received event your agent reads; it replies with a send call. See give your AI agent an inbox.
Is this the same “programmable email” HubSpot talks about? No. HubSpot’s term is HubL-templated personalization inside marketing sends. This is the developer sense: email as programmable send/receive I/O.
Programmable email is just email that finally behaves like the rest of your code: a call to send, a webhook to receive, an inbox you own. MailKite is that primitive assembled — the MX edge, the parser, managed deliverability, and the Send API — across unlimited domains and mailboxes, free, so you can put a programmable address on every product you ship. See the programmable email overview, read the docs, or check the pricing.
Related: give your AI agent an inbox, parse inbound email to JSON in Node, and reply-by-email in your app.