Why not your own inbox
With Pidgeon the address is one API call, and three inboxes are free.
The alternative most people try first is OAuth into their own Gmail or Outlook. It works for an assistant that triages your mail. It is the wrong shape for an agent that does its own work, for three reasons:
- It reads everything. A Gmail scope that lets an agent read mail lets it read all of it: your bank, your doctor, your password resets. An agent that only needs supplier replies still holds the rest.
- It speaks as you. Every email the agent sends is from you. When it gets something wrong, the person on the other end thinks you did.
- It does not multiply. Ten agents are ten people's inboxes, or ten paid seats. A product that gives each of its customers an agent needs an address per customer.
An address of its own fixes all three. The agent sees only mail sent to it, signs as itself, and a new one is an API call.
Create the address
Sign up at pidgeon.ai and you already have one address on the shared domain, pidgeon.email. Or let the agent sign itself up: two HTTP calls and a six-digit code from a person, described in skill.md.
A key with full access creates more:
curl https://pidgeon.ai/api/v1/identities \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"address": "scout@pidgeon.email",
"display_name": "Scout",
"type": "agent"
}'
The answer carries the address's id, which every later call names as identity_id. A domain of your own is free on every plan, and it is what makes the agent's mail come from scout@yourcompany.com; the domains guide lists the DNS records to add.
In TypeScript, with npm install @pidgeon-ai/sdk:
import { client, identitiesCreate } from '@pidgeon-ai/sdk';
client.setConfig({ auth: () => process.env.PIDGEON_API_KEY! });
const { data } = await identitiesCreate({
body: { address: 'scout@pidgeon.email', display_name: 'Scout', type: 'agent' },
throwOnError: true,
});
const identityId = data.data.id;
Receive mail
There are two ways, and most agents use both.
A webhook, for an agent that runs on its own. Register an endpoint for message.received and each email becomes a signed HTTP request to your code within seconds. The webhooks guide shows how to verify the signature.
A call that waits, for an agent in the middle of a task. An agent that has just emailed a supplier, or submitted a sign-up form, can wait for the answer in one request:
import { messagesAwait } from '@pidgeon-ai/sdk';
const { data: waited } = await messagesAwait({
query: { identity_id: identityId, body: 'clean', timeout: 120 },
throwOnError: true,
});
if (waited.data.message) {
console.log(waited.data.message.subject, waited.data.message.text);
}
body: 'clean' returns the text with anything a person could not see removed: hidden HTML, invisible characters. An email is text a stranger wrote, and the agent is about to read it. Prompt injection in email covers what else is checked.
Reply in the same thread
A reply names the message it answers. Pidgeon reads the recipients, the subject and the thread headers off it, so the other side's mail client files the answer under the same conversation:
import { messagesReply } from '@pidgeon-ai/sdk';
await messagesReply({
path: { id: waited.data.message!.id },
body: { text: 'Thanks. The order is confirmed for Thursday.' },
throwOnError: true,
});
When the supplier answers again, the next wait or webhook carries the same thread id, and the agent has the whole conversation to read before it replies.
From an agent, without code
If the agent is Claude Code, Cursor, Codex or anything else that speaks MCP, it does not need the SDK. Pidgeon's hosted MCP server is a URL:
claude mcp add --transport http pidgeon https://pidgeon.ai/api/mcp
The first call opens a sign-in where you choose what the agent may do: read, read and send, or full access, on one address or all of them. The agent then has create_address, await_message, send_message and reply_to_message as tools. Email for Claude Code and Cursor walks through it.
Keep a person in reach
An agent with its own address still answers to somebody, and the address can say how:
- A key that can only draft. A
draftkey reads and writes drafts and can never send; a person reviews and sends from the dashboard. - Approval for strangers. With
send_approval: "first_contact", a first email to somebody the address has never written to or heard from waits for a person. - A thread a person can take. Every conversation has an owner,
agentorhuman. When a person takes one over, the agent's sends in it are refused until it is handed back.
Other ways to do it
Pidgeon is one agent inbox API among several: AgentMail is the best known, and the roundup of email APIs for AI agents compares them. A Google Workspace user per agent works too, at a seat each; Pidgeon vs the Gmail API has the limits. And you can build it yourself on Amazon SES, if storage, threading, search and deliverability are work you want.
Whichever you pick, the rule is the same: the agent gets its own address, and yours stays yours.