Appearance
Changelog
Fresh 🌱Release history for the AgentMail API, SDKs, and platform.
2026-6-8
Summary
You can now create inboxes on any subdomain of a verified domain without registering each subdomain separately. Enable subdomains_enabled on a domain, publish the single wildcard MX record it returns, and create inboxes on any subdomain on demand. Build agents that spin up addresses like agent@bot.example.com or support@team1.example.com the moment you need them.
What's new?
New features:
- Subdomains: Opt in per domain with
subdomains_enabled. When enabled, the domain's verification records include a wildcard MX record (*.<domain>) to publish on the top-level domain. Once it is published and verified, inboxes can be created on any subdomain of that domain.
Changes:
POST /v0/domainsaccepts an optionalsubdomains_enabledflag (defaults tofalse).PATCH /v0/domains/:domain_idnow acceptssubdomains_enabledand applies partial updates: send at least one offeedback_enabledorsubdomains_enabled, and omitted fields are left unchanged. Enabling subdomains on an already-verified domain returns it topendinguntil the new wildcard MX record is published; sending is not interrupted.- Domain responses now include the
subdomains_enabledfield. - Creating an inbox on a subdomain of a domain that does not have subdomains enabled returns a
422error.
Use cases
Build agents that:
- Provision a dedicated inbox per customer or workspace under one verified domain (
support@acme.example.com) - Separate agent traffic onto purpose-named subdomains (
billing.,outreach.,support.) without registering each one - Stand up short-lived inboxes on fresh subdomains for one-off tasks, then tear them down
- Keep all agent addresses under a single domain you verify and manage once
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### Enable subdomains on a verified domain
domain = client.domains.update("example.com", subdomains_enabled=True)
#### Publish the new wildcard MX record, then create inboxes on any subdomain
inbox = client.inboxes.create(username="agent", domain="bot.example.com")
print(inbox.inbox_id) # agent@bot.example.comtypescript
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "your-api-key" });
// Enable subdomains on a verified domain
const domain = await client.domains.update("example.com", { subdomainsEnabled: true });
// Publish the new wildcard MX record, then create inboxes on any subdomain
const inbox = await client.inboxes.create({ username: "agent", domain: "bot.example.com" });
console.log(inbox.inboxId); // agent@bot.example.comLearn more in the Setting Up Subdomains guide.
2026-6-3
Summary
You can now search messages and threads by keyword. Full-text search ranks results by relevance across the sender, recipients, subject, and message body, and works per-inbox or across your entire organization. List endpoints also gained substring filters, so you can narrow a list to a specific sender, recipient, or subject without paging through everything. Build agents that find the right conversation instead of scanning every thread.
What's new?
New endpoints:
GET /v0/inboxes/:inbox_id/messages/search- Full-text search of messages in an inbox, ranked by relevance.GET /v0/threads/search- Org-wide full-text search across threads in every inbox.GET /v0/inboxes/:inbox_id/threads/search- Full-text search of threads in a single inbox.GET /v0/pods/:pod_id/threads/search- Full-text search of threads in a pod.
New features:
- Full-text search: A
qquery matches against the sender, recipients, and subject (substring) and the message body (tokenized full text). Results are ordered by relevance. Spam, trash, blocked, and unauthenticated items are always excluded, andlimitis capped at 100. - Match highlights: Each search result includes an optional
highlightsobject with the matched fragments per field, with matched terms wrapped in**. A field appears only when it matched, so the present keys also tell you which fields produced the hit.
Changes:
GET /v0/inboxes/:inbox_id/messagesnow acceptsfrom,to, andsubjectsubstring filters.tomatches theto,cc, orbccfields.GET /v0/threads,GET /v0/inboxes/:inbox_id/threads, andGET /v0/pods/:pod_id/threadsnow acceptsenders,recipients, andsubjectsubstring filters.- Filtered list requests are served by search and cap
limitat 100; results keep the usual newest-first ordering.
Use cases
Build agents that:
- Pull up every thread mentioning an order number, invoice, or customer name across all of your inboxes
- Find the conversation a reply belongs to by searching the subject or body, instead of paging through history
- Narrow a list to a single sender or recipient before processing, using the new substring filters
- Surface the matched snippet to a human reviewer using per-field
highlights
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### org-wide full-text search across every inbox
results = client.threads.search(q="invoice overdue")
for thread in results.threads:
print(thread.thread_id, thread.subject)
# highlights tells you which fields matched
if thread.highlights:
print(thread.highlights)
#### scope a search to one inbox's messages
inbox_results = client.inboxes.messages.search(
inbox_id="support@agentmail.to",
q="refund requested",
)
#### or just filter a list by subject, no relevance ranking
filtered = client.inboxes.messages.list(
inbox_id="support@agentmail.to",
subject=["invoice"],
)typescript
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "your-api-key" });
// org-wide full-text search across every inbox
const results = await client.threads.search({ q: "invoice overdue" });
for (const thread of results.threads) {
console.log(thread.threadId, thread.subject);
// highlights tells you which fields matched
if (thread.highlights) console.log(thread.highlights);
}
// scope a search to one inbox's messages
const inboxResults = await client.inboxes.messages.search("support@agentmail.to", {
q: "refund requested",
});
// or just filter a list by subject, no relevance ranking
const filtered = await client.inboxes.messages.list("support@agentmail.to", {
subject: ["invoice"],
});Learn more in the Messages and Threads guides.
2026-5-28
Summary
Inboxes now support custom metadata: your own key-value data attached to any inbox. Link an inbox to records in your own system, such as a tenant ID, user ID, or feature flags, and read it back on every inbox response. Build agents that carry your application's context wherever an inbox goes.
What's new?
New features:
- Inbox metadata: Attach custom key-value pairs to an inbox. Values may be a string, number, or boolean, with up to 256 keys per inbox.
Changes:
- The
Inboxobject now includes an optionalmetadatafield, returned on get, list, and create responses. POST /v0/inboxesaccepts ametadatafield to set metadata at creation time.PATCH /v0/inboxes/:inbox_idaccepts ametadatafield. Updates merge into existing metadata: keys you include are added or overwritten, and keys you omit are preserved. Send a key with a null value to remove it, or setmetadatato null to clear everything. Each update must include at least one ofdisplay_nameormetadata.
Use cases
Build agents that:
- Tag each inbox with a tenant or customer ID so you can map inboxes back to your own data model
- Store per-inbox feature flags or routing hints that your agent reads at runtime
- Track lifecycle state, such as an onboarding step or campaign name, directly on the inbox
- Filter and organize a large fleet of inboxes by the attributes that matter to your application
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### attach metadata when creating an inbox
inbox = client.inboxes.create(
username="support-agent",
metadata={"tenant_id": "acme", "tier": "pro", "active": True},
)
#### merge in a change; omitted keys are preserved
client.inboxes.update(
inbox_id=inbox.inbox_id,
metadata={"tier": "enterprise"},
)typescript
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "your-api-key" });
// attach metadata when creating an inbox
const inbox = await client.inboxes.create({
username: "support-agent",
metadata: { tenant_id: "acme", tier: "pro", active: true },
});
// merge in a change; omitted keys are preserved
await client.inboxes.update(inbox.inboxId, {
metadata: { tier: "enterprise" },
});Learn more about attaching and updating inbox data in the Inboxes metadata guide.
2026-3-18
Summary
Inbox-scoped API keys let you generate credentials that are restricted to a single inbox. This gives agents and integrations the minimum access they need, reducing the blast radius if a key is compromised.
What's new?
New endpoints:
GET /v0/inboxes/:inbox_id/api-keys- List all API keys scoped to an inboxPOST /v0/inboxes/:inbox_id/api-keys- Create an API key scoped to an inboxDELETE /v0/inboxes/:inbox_id/api-keys/:api_key- Delete an inbox-scoped API key
Updated types:
ApiKeyandCreateApiKeyResponsenow include an optionalinbox_idfield when the key is scoped to an inbox
Use cases
Build agents that:
- Operate with least-privilege access to a single inbox rather than an entire pod or organization
- Issue short-lived, narrowly scoped keys to third-party integrations that only need access to one address
- Rotate credentials per inbox without affecting other inboxes or pods
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### create an api key scoped to a single inbox
key = client.inboxes.api_keys.create(
inbox_id="user@example.com",
name="integration-key"
)
print(key.api_key)typescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// create an api key scoped to a single inbox
const key = await client.inboxes.apiKeys.create("user@example.com", {
name: "integration-key",
});
console.log(key.apiKey);Learn more about API key scoping in the API Keys reference.
2025-8-13
Summary
We're excited to introduce Metrics Endpoints - two new powerful endpoints that give you deep insights into your email deliverability and agent performance. Track critical events like bounces, deliveries, rejections, and complaints with detailed timestamps to build smarter, self-optimizing email agents.
What's new?
New endpoints:
GET /metrics- Get comprehensive metrics across all your inboxesGET /inboxes/{inbox_id}/metrics- Get metrics for a specific inbox
Metrics tracked:
- Delivery events: sent, delivered, bounced, rejected
- Error tracking: complaints, spam reports
- Time-series data with detailed timestamps
Use cases
Build agents that:
- Monitor their own bounce rates in real-time
- Optimize send timing based on historical performance
- Automatically adjust behavior based on deliverability metrics
- Pause campaigns when performance drops below thresholds
- Implement intelligent retry strategies for better inbox placement
Ready to build smarter agents? Check out our Metrics API documentation to get started.
2025-7-20
Summary
Introducing WebSocket Streaming - receive email events in real-time as they happen. Build reactive agents that respond instantly to new messages, deliveries, and bounces without polling. Perfect for building interactive, event-driven email experiences.
What's new?
WebSocket endpoint:
wss://ws.agentmail.to/v0- Real-time event streaming
Events streamed:
message.received- New inbound email detectedmessage.sent- Outbound email sent successfullymessage.delivered- Delivery confirmed by recipient servermessage.bounced- Bounce detected (permanent or temporary)message.complained- Spam complaint received
Connection features:
- JWT-based authentication for secure connections
- Automatic reconnection with exponential backoff
- Event filtering by inbox for targeted subscriptions
- Low-latency delivery (typically under 100ms)
- Support for thousands of concurrent connections
Use cases
Build agents that:
- Respond to emails within seconds of receipt
- Monitor deliverability in real-time across all inboxes
- Trigger workflows instantly on specific events
- Build interactive conversational email experiences
- Scale to handle high-volume email operations
- React to bounces and complaints immediately
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### subscribe to events for an inbox
async with client.websockets.subscribe(
inbox_id="support@example.com"
) as ws:
async for event in ws:
if event.type == "message.received":
print(f"New email from: {event.data.from_}")
response = await generate_response(event.data.text)
await client.messages.reply(
message_id=event.data.message_id,
text=response
)typescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// subscribe to events for an inbox
for await (const event of client.websockets.subscribe("support@example.com")) {
if (event.type === "message.received") {
console.log("New email from:", event.data.from);
const response = await generateResponse(event.data.text);
await client.messages.reply(event.data.messageId, response);
}
}Get started with WebSocket Streaming to build real-time email agents.
2025-6-15
Summary
Introducing Pods - team collaboration spaces for AgentMail. Share inboxes, domains, and resources across your organization while maintaining granular control. Perfect for teams building multi-agent email systems that need organized resource management.
What's new?
New endpoints:
POST /pods- Create a new pod (team workspace)GET /pods- List all pods in your organizationGET /pods/{pod_id}- Get pod detailsDELETE /pods/{pod_id}- Delete a podPOST /pods/{pod_id}/inboxes- Create inbox within a podPOST /pods/{pod_id}/domains- Add custom domain to a podGET /pods/{pod_id}/threads- List threads within a podGET /pods/{pod_id}/metrics- Get metrics for a pod
Pod features:
- Shared inbox access across team members
- Per-pod domain configuration
- Isolated metrics and analytics per pod
- Organized resource hierarchy
Use cases
Build systems where:
- Multiple agents share email infrastructure
- Different teams manage their own inboxes independently
- Resources are organized by department or project
- Analytics are tracked per team workspace
- Billing and usage can be attributed to specific teams
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### create a pod for your sales team
pod = client.pods.create(
name="Sales Team",
description="Shared resources for sales agents"
)
#### create an inbox in the pod
inbox = client.pods.inboxes.create(
pod_id=pod.pod_id,
inbox_id="sales@example.com"
)
#### list all pods
pods = client.pods.list()
for pod in pods.pods:
print(f"Pod: {pod.name} ({len(pod.inbox_ids)} inboxes)")typescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// create a pod for your sales team
const pod = await client.pods.create({
name: "Sales Team",
description: "Shared resources for sales agents",
});
// create an inbox in the pod
await client.pods.inboxes.create(pod.podId, "sales@example.com");
// list all pods
const { pods } = await client.pods.list();
for (const p of pods) {
console.log(`Pod: ${p.name} (${p.inboxIds?.length ?? 0} inboxes)`);
}Learn more about organizing teams with Pods in our documentation.
2025-12-22
Summary
Webhooks & Events – receive email and domain events via HTTP callbacks. Subscribe to message lifecycle events (received, sent, delivered, bounced, complained, rejected) and domain verification. Use Svix headers for verification and filter by inbox or pod. Perfect for agents that need reliable, async notifications without keeping a WebSocket open.
What's new?
Webhook events:
message.received- New inbound emailmessage.sent- Outbound message sentmessage.delivered- Delivery confirmedmessage.bounced- Bounce (with type and recipients)message.complained- Spam complaintmessage.rejected- Rejection (e.g. validation)domain.verified- Domain verification succeeded
Delivery & verification:
- Svix-style headers:
svix-id,svix-signature,svix-timestampfor verification - Filter by inbox or pod (up to 10 per webhook)
- Payloads include inbox_id, thread_id, message_id, timestamps, and event-specific data
Use cases
Build agents that:
- React to new emails, bounces, and complaints via HTTP
- Sync email state to your database or queue
- Trigger workflows on domain verification
- Verify webhook signatures for security
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### in your webhook handler: verify signature and handle event
#### (use Svix or the raw headers for verification)
def handle_webhook(request):
event_id = request.headers.get("svix-id")
signature = request.headers.get("svix-signature")
payload = request.json()
if payload.get("event_type") == "message.received":
message = payload.get("message")
# process new email
elif payload.get("event_type") == "domain.verified":
domain = payload.get("domain")
# domain is verifiedtypescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// in your webhook handler: verify signature and handle event
// (use Svix or the raw headers for verification)
function handleWebhook(request: Request) {
const eventId = request.headers.get("svix-id");
const signature = request.headers.get("svix-signature");
const payload = request.json();
if (payload.event_type === "message.received") {
const message = payload.message;
// process new email
} else if (payload.event_type === "domain.verified") {
const domain = payload.domain;
// domain is verified
}
}Set up and verify webhooks in our Webhooks documentation.
2025-10-28
Summary
Introducing Custom Domains – add and verify your own domains for sending and receiving email. Use DNS verification (TXT, CNAME, MX), export zone files for easy DNS setup, and control feedback (bounce and complaint) delivery. Perfect for agents that need to send from your brand's domain with full control over deliverability.
What's new?
New endpoints:
GET /domains- List all domainsGET /domains/{domain_id}- Get domain details and verification recordsPOST /domains- Create (add) a domainDELETE /domains/{domain_id}- Remove a domainGET /domains/{domain_id}/zone-file- Download zone file for DNS setupPOST /domains/{domain_id}/verify- Trigger domain verification
Domain features:
- DNS verification with TXT, CNAME, and MX records
- Verification status: NOT_STARTED, PENDING, VERIFYING, VERIFIED, FAILED, INVALID
- Per-record status (MISSING, INVALID, VALID) for targeted fixes
- Zone file export for quick import at your DNS provider
- Optional feedback (bounce/complaint) delivery per domain
Use cases
Build systems where:
- Agents send from your verified custom domain
- You manage DNS in one place and sync via zone file
- Verification status drives onboarding or monitoring
- Bounce and complaint handling is configured per domain
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### create a domain
domain = client.domains.create(
domain="mail.example.com",
feedback_enabled=True
)
#### get verification records and status
domain = client.domains.get(domain_id=domain.domain_id)
for record in domain.records:
print(f"{record.type} {record.name}: {record.status}")
#### trigger verification after updating DNS
client.domains.verify(domain_id=domain.domain_id)typescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// create a domain
const domain = await client.domains.create({
domain: "mail.example.com",
feedbackEnabled: true,
});
// get verification records and status
const domainDetails = await client.domains.get(domain.domainId);
for (const record of domainDetails.records) {
console.log(`${record.type} ${record.name}: ${record.status}`);
}
// trigger verification after updating DNS
await client.domains.verify(domain.domainId);Learn more in our Custom Domains and Managing Domains guides.
2025-10-25
Summary
Introducing the Drafts API – compose and manage email drafts before sending. Create drafts, update them over time, schedule send times, and send when ready. Perfect for agents that need to build messages incrementally, support reply threading, or queue emails for later delivery.
What's new?
New endpoints:
GET /drafts- List all drafts (with optional filters)GET /drafts/{draft_id}- Get a draftPOST /inboxes/{inbox_id}/drafts- Create a draft in an inboxPATCH /inboxes/{inbox_id}/drafts/{draft_id}- Update a draftPOST /inboxes/{inbox_id}/drafts/{draft_id}/send- Send a draftDELETE /inboxes/{inbox_id}/drafts/{draft_id}- Delete a draft
Draft features:
- Compose with to, cc, bcc, subject, plain text, and HTML body
- Reply threading via
in_reply_toandreferences - Schedule send with
send_atfor delayed delivery - Attachments and labels
- List and filter drafts by inbox, labels, or time range
Use cases
Build agents that:
- Compose multi-step replies before sending
- Schedule follow-up emails for optimal delivery
- Queue outbound messages and send in batches
- Edit drafts based on new context or user feedback
- Maintain proper email threads with
in_reply_to
python
from agentmail import AgentMail
client = AgentMail(api_key="your-api-key")
#### create a draft in an inbox
draft = client.inboxes.drafts.create(
inbox_id="support@example.com",
to=["user@example.com"],
subject="Re: Your request",
text="We're looking into it.",
in_reply_to="<message-id@example.com>"
)
#### update the draft
client.inboxes.drafts.update(
inbox_id="support@example.com",
draft_id=draft.draft_id,
text="We've resolved your request."
)
#### send the draft
client.inboxes.drafts.send(
inbox_id="support@example.com",
draft_id=draft.draft_id
)typescript
import { AgentMail } from "agentmail";
const client = new AgentMail({ apiKey: "your-api-key" });
// create a draft in an inbox
const draft = await client.inboxes.drafts.create("support@example.com", {
to: ["user@example.com"],
subject: "Re: Your request",
text: "We're looking into it.",
inReplyTo: "<message-id@example.com>",
});
// update the draft
await client.inboxes.drafts.update(
"support@example.com",
draft.draftId,
{ text: "We've resolved your request." }
);
// send the draft
await client.inboxes.drafts.send("support@example.com", draft.draftId);Learn more about composing and sending in our Drafts documentation.