01
No identity
A message lands from support@. It could be a person, a cron job, or a model on its fourth retry. Nothing in the envelope says which, or whether the address was minted an hour ago.
Defeat gives every AI agent a real mailbox and a verified identity card. Recipients see who wrote, who operates it, and that the address is not a throwaway.
POST api.defeatemail.net/v1/messages
The gap · 01
Agents already send mail. They borrow a founder address, a shared no-reply, or a mailbox nobody owns. The recipient is left guessing and the sender is left with no record.
01
A message lands from support@. It could be a person, a cron job, or a model on its fourth retry. Nothing in the envelope says which, or whether the address was minted an hour ago.
02
Replies arrive where no agent is listening. The thread dies quietly, or a person inherits a conversation they never opened.
03
When an agent sends the wrong thing there is no signed trail from the message back to the run, the model, and the operator who answers for it.
How it works · 02
Defeat sits between the run and the recipient. It signs what leaves, resolves what arrives, and keeps a record either side can hold up later.
Create a mailbox from the API or the dashboard. Point a subdomain at Defeat, or borrow one of ours while you build.
Outbound mail is signed with the agent key and stamped with its operator, its model, and the task it is working.
Inbound mail arrives as structured JSON with the thread, the attachments, and the sender verdict already resolved.
Capabilities · 03
Every part of the mailbox is addressable from code, and every part of it leaves a trace.
Addresses you create, scope, and destroy from code. Wildcards, plus-addressing, and per-run inboxes that expire on their own.
POST /v1/mailboxesThe signed identity record carried by every message. Who ran it, on what model, for which task, and the key that proves all three.
X-Agent-FlagMatch on sender, flag state, header, or body. Forward, hold, quarantine, or hand the thread to a person.
rules.yamlConversation state stays intact when one agent passes the thread to another agent, or out to a human, and back again.
thread_idSigned delivery with retries and idempotency keys, or hold a stream open and read events the moment they land.
events.stream()An append-only record of every send, receipt, and verdict. Export it whenever you like and the signatures travel with it.
GET /v1/ledgerPer-agent rate caps, recipient allowlists, and a hard ceiling on volume. An agent stuck in a loop stops at the wall instead of the inbox.
policy.limitsRoute a draft to a person before it leaves. They approve in one click and the flag records exactly who did.
mode: reviewAttribution · 04
A flag is the agent’s identity card. It is signed, it rides in the message headers, it survives forwarding, and it resolves to a public page anyone can open without an account.
The inbox · 05
One mailbox, five messages, three verdicts. Pick any of them.
Nothing matches that filter
Quickstart · 06
No DNS on day one. Take a subdomain of defeatemail.net, ship the integration, and move to your own domain when you are ready.
# 1 · a mailbox that lives as long as the run curl -X POST https://api.defeatemail.net/v1/mailboxes \ -H "Authorization: Bearer $DEFEAT_KEY" \ -d '{"address":"triage@acme.defeatemail.net","flag":"agent_7Kq4x1","expires_in":"24h"}' # 2 · send it, signed curl -X POST https://api.defeatemail.net/v1/messages \ -H "Authorization: Bearer $DEFEAT_KEY" \ -d '{ "from": "triage@acme.defeatemail.net", "to": "jordan@acme.co", "subject": "Ticket 88213 needs a human", "text": "The refund is above my agent limit.", "flag": { "task": "run_88213", "model": "claude-opus-5" } }' # 3 · read the thread back curl https://api.defeatemail.net/v1/messages?thread=thr_4b21 \ -H "Authorization: Bearer $DEFEAT_KEY"
import { Defeat } from "@defeat/sdk"; const defeat = new Defeat({ apiKey: process.env.DEFEAT_KEY }); // one mailbox per run, gone when the run is const box = await defeat.mailboxes.create({ address: "triage@acme.defeatemail.net", flag: "agent_7Kq4x1", expiresIn: "24h", }); const sent = await defeat.messages.send({ from: box.address, to: "jordan@acme.co", subject: "Ticket 88213 needs a human", text: "The refund is above my agent limit.", flag: { task: "run_88213", model: "claude-opus-5" }, }); console.log(sent.flag.url); // defeatemail.net/f/agent_7Kq4x1 // inbound arrives already resolved defeat.on("message.received", (m) => { if (m.verdict !== "verified") return defeat.messages.hold(m.id); return handle(m.thread, m.text); });
import os from defeat import Defeat defeat = Defeat(api_key=os.environ["DEFEAT_KEY"]) # one mailbox per run, gone when the run is box = defeat.mailboxes.create( address="triage@acme.defeatemail.net", flag="agent_7Kq4x1", expires_in="24h", ) sent = defeat.messages.send( from_=box.address, to="jordan@acme.co", subject="Ticket 88213 needs a human", text="The refund is above my agent limit.", flag={"task": "run_88213", "model": "claude-opus-5"}, ) print(sent.flag.url) # defeatemail.net/f/agent_7Kq4x1 for m in defeat.events.stream("message.received"): if m.verdict != "verified": defeat.messages.hold(m.id) continue handle(m.thread, m.text)
Mailboxes, signed sends, webhooks, the flag record, verdicts, errors and rate limits.
Pricing · 07
Agents outnumber people. Charging by the human made sense for the last inbox, not this one.
· Solo
$0
One domain, three mailboxes, and the whole flag spec.
· Studio
$39 per month
For teams running agents against real people.
· Scale
Custom
Your keys, your region, your terms.
Pick a subdomain now and keep it when you move to your own.
No card needed. Keep the subdomain when you move to your own.