API Documentation
Base URL: https://api.theagentmail.net
Getting started
AgentMail gives your AI agent its own email address. Here's how to set it up.
1. Sign in and create an org
Sign in with your personal email (Gmail, Outlook, ProtonMail, etc.). Then create an organization from the dashboard. You'll need an org admin token for the next step -- generate one from the Admin section.
2. Create an email account for your agent
curl -X POST https://api.theagentmail.net/v1/accounts \
-H "Authorization: Bearer am_org_..." \
-H "Content-Type: application/json" \
-d '{"address": "my-agent@theagentmail.net"}'3. Create a token scoped to that account
Use the account ID from the previous response. This token only has access to this one mailbox -- safe to give to your agent.
curl -X POST https://api.theagentmail.net/v1/accounts/ACCOUNT_ID/api-keys \
-H "Authorization: Bearer am_org_..." \
-H "Content-Type: application/json" \
-d '{"name": "my-agent-key"}'Save the key from the response. It's only shown once.
4. Give your agent the token and this docs page
Pass the account token and https://api.theagentmail.net/docs to your AI agent. It can read the API reference and start sending and receiving email on its own.
Authentication
All API endpoints require a Bearer token in the Authorization header.
Authorization: Bearer am_...There are two types of tokens:
Org tokens
Full access to all accounts in your organization. Create and delete accounts, send and receive email, manage webhooks, check karma. Created from the dashboard.
Account tokens
Scoped to a single email account. Can send and receive email, manage webhooks, and view account info. Cannot access other accounts or create new ones (returns 403). Created programmatically via POST /v1/accounts/:id/api-keys.
Account tokens can use shortcut routes that omit the account ID -- the server infers it from the token. For example, POST /v1/messages instead of POST /v1/accounts/:accountId/messages. See each endpoint section for the shortcut path.
Accounts
/v1/accounts-10 karmaCreate a new email account. Costs 10 karma.
Request body
{
"address": "my-agent@theagentmail.net",
"displayName": "My Agent" // optional
}Response (201)
{
"data": {
"id": "abc123",
"address": "my-agent@theagentmail.net",
"displayName": "My Agent",
"createdAt": 1709136000000
}
}Reserved addresses (uri, support) cannot be created.
/v1/accountsList all email accounts in your organization.
Response
{
"data": [
{
"id": "abc123",
"address": "my-agent@theagentmail.net",
"displayName": "My Agent",
"createdAt": 1709136000000
}
]
}/v1/accounts/:accountIdGet details for a specific account. With an account token: GET /v1/account
/v1/accounts/:accountId+5 karmaDelete an email account. Refunds 5 karma. With an account token: DELETE /v1/account
Messages are retained for reference. The address becomes available for reuse.
Messages
/v1/accounts/:accountId/messages-1 karmaSend an email from this account. Costs 1 karma. With an account token: POST /v1/messages
Request body
{
"to": ["human@example.com"],
"cc": ["other@example.com"], // optional
"bcc": ["hidden@example.com"], // optional
"subject": "Hello from my agent",
"text": "Plain text body",
"html": "<p>HTML body</p>", // optional
"inReplyTo": "<msgid@example>", // optional, for threading
"references": "<msgid@example>", // optional
"attachments": [ // optional
{
"filename": "report.pdf",
"contentType": "application/pdf",
"content": "base64-encoded-data"
}
]
}Response (201)
{
"data": {
"id": "msg_123",
"from": "my-agent@theagentmail.net",
"to": ["human@example.com"],
"subject": "Hello from my agent",
"direction": "outbound",
"status": "queued",
"timestamp": 1709136000000
}
}/v1/accounts/:accountId/messagesList all messages (inbound and outbound) for this account, sorted by most recent first. With an account token: GET /v1/messages
Response
{
"data": [
{
"id": "msg_123",
"from": "sender@gmail.com",
"to": ["my-agent@theagentmail.net"],
"subject": "Re: Hello",
"direction": "inbound",
"status": "received",
"timestamp": 1709137000000
}
]
}/v1/accounts/:accountId/messages/:messageIdGet a single message with full body content and attachment metadata. With an account token: GET /v1/messages/:messageId
Response
{
"data": {
"id": "msg_123",
"from": "sender@gmail.com",
"to": ["my-agent@theagentmail.net"],
"subject": "Re: Hello",
"text": "Full message body...",
"html": "<p>Full HTML body...</p>",
"direction": "inbound",
"status": "received",
"timestamp": 1709137000000,
"attachments": [
{
"id": "att_456",
"filename": "photo.jpg",
"contentType": "image/jpeg",
"size": 245000
}
]
}
}Attachments
/v1/accounts/:accountId/messages/:messageId/attachments/:attachmentIdGet a signed download URL for an attachment. The URL is valid for 1 hour. With an account token: GET /v1/messages/:messageId/attachments/:attachmentId
Response
{
"data": {
"url": "https://storage.googleapis.com/...",
"filename": "photo.jpg",
"contentType": "image/jpeg",
"expiresAt": 1709140600000
}
}Webhooks
Register a webhook URL to get notified when your account receives email. We'll POST to your URL within seconds of delivery.
/v1/accounts/:accountId/webhooksRegister a webhook for inbound email notifications. With an account token: POST /v1/webhooks
Request body
{
"url": "https://my-agent.example.com/inbox"
}Response (201)
{
"data": {
"id": "wh_789",
"url": "https://my-agent.example.com/inbox",
"secret": "whsec_abc123...",
"active": true,
"createdAt": 1709136000000
}
}Save the secret -- it's only returned on creation. Use it to verify webhook signatures.
/v1/accounts/:accountId/webhooksList all webhooks for this account. With an account token: GET /v1/webhooks
/v1/accounts/:accountId/webhooks/:webhookIdDelete a webhook. With an account token: DELETE /v1/webhooks/:webhookId
Webhook delivery format
When an email arrives, we POST a JSON payload to your webhook URL:
{
"messageId": "msg_123",
"accountId": "abc123",
"from": "sender@gmail.com",
"to": ["my-agent@theagentmail.net"],
"subject": "Hello",
"text": "Message body...",
"html": "<p>Message body...</p>",
"timestamp": 1709137000000
}Signature verification
Every webhook delivery includes two headers:
X-AgentMail-Signature— HMAC-SHA256 hex digest of the request bodyX-AgentMail-Timestamp— millisecond timestamp of when the delivery was sent
Verify the signature using your webhook secret, and reject requests with timestamps older than 5 minutes to prevent replay attacks:
import { createHmac } from "crypto";
const verifyWebhook = (body: string, signature: string, timestamp: string, secret: string) => {
const age = Date.now() - Number(timestamp);
if (age > 5 * 60 * 1000) return false;
const expected = createHmac("sha256", secret)
.update(body)
.digest("hex");
return signature === expected;
};
// In your webhook handler:
// const body = await req.text();
// const sig = req.headers.get("x-agentmail-signature");
// const ts = req.headers.get("x-agentmail-timestamp");
// if (!verifyWebhook(body, sig, ts, YOUR_WEBHOOK_SECRET)) {
// return new Response("Unauthorized", { status: 401 });
// }Karma
Karma is a credit system that keeps the shared domain clean. Sending costs karma. Receiving from real people earns it back. Agents that have genuine conversations sustain themselves. Spammers run out and get blocked.
You start with 100 karma when you pay. That's 100 sends, or 10 accounts, or some mix.
Purchase karma credits
Reply from a trusted domain (once per sender until you reply back)
Delete an email account (partial refund)
Send an email
Create a new email account
Trusted domains
Karma for email_received is only awarded when the sender uses a trusted email provider: Gmail, Outlook, Yahoo, iCloud, ProtonMail, Fastmail, Hey, and similar personal email services. Emails from throwaway or bulk domains don't earn karma.
Anti-gaming
You only earn karma once per sender until your agent replies back. If someone sends you 10 emails without you replying, you get +2 karma total, not 20.
/v1/karmaGet your organization's karma balance and event history.
Response
{
"data": {
"balance": 87,
"events": [
{
"id": "evt_1",
"type": "money_paid",
"amount": 100,
"timestamp": 1709136000000
},
{
"id": "evt_2",
"type": "account_created",
"amount": -10,
"timestamp": 1709136100000
}
]
}
}Organizations
Each organization has one billing user (the creator) and can have additional admins. Karma and accounts belong to the organization, not individual users.
Organizations are managed from the dashboard. By default, each user can create one organization. Contact support@theagentmail.net to create additional organizations.
Account API keys
/v1/accounts/:accountId/api-keysCreate a token scoped to a single account. Useful for giving an agent access to only its own mailbox without exposing org-level access.
Request body
{
"name": "My agent's key" // optional
}Response (201)
{
"data": {
"id": "key_123",
"key": "am_abc123...",
"prefix": "am_abc1",
"name": "My agent's key",
"createdAt": 1709136000000
}
}The full key is only returned once. Store it securely.
Errors
All errors return a JSON object with an error message and code:
{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE"
}UNAUTHORIZEDMissing or invalid API key / tokenKARMA_LOWInsufficient karma for this actionFORBIDDENAccount-scoped token accessing org-level endpointNOT_FOUNDResource not foundCONFLICTAddress already takenINTERNAL_ERRORSomething went wrong on our endTypeScript SDK
If you prefer typed method calls over raw HTTP, use the TypeScript SDK.
import { createClient } from "@agentmail/sdk";
// With an account-scoped token (no accountId needed)
const mail = createClient({ apiKey: "am_..." });
// Send an email
await mail.messages.send({
to: ["human@example.com"],
subject: "Hello from my agent",
text: "This email was sent by an AI agent.",
});
// Check inbox
const messages = await mail.messages.list();
// Register a webhook
await mail.webhooks.create({
url: "https://my-agent.example.com/inbox",
});
// Get account info
const account = await mail.accounts.get();
// With an org-level token (accountId required)
const orgMail = createClient({ apiKey: "am_org_..." });
await orgMail.messages.send(accountId, {
to: ["human@example.com"],
subject: "Hello",
text: "Sent via org token.",
});OpenCode skill
If your agent runs on OpenCode, install the AgentMail skill and it learns the full API automatically. No SDK needed.
opencode skill install agentmail