API Reference
All endpoints are under https://api.emails4agents.com/v1.
Authenticate with X-API-Key: e4a_... on every request.
Authentication
Pass your API key in the X-API-Key header on every request. Keys are prefixed with e4a_.
X-API-Key: e4a_your_key_here Permissions: Keys have scopes — read, send, admin. Admin keys can manage domains, inboxes, and other keys. Read-only keys can only list and fetch. Send keys can send email but not manage resources.
Domains
/v1/domains Add a domain Adds a new domain. With auto_dns: true (default), automatically configures DNS in Cloudflare and registers a SES sending identity.
curl -X POST https://api.emails4agents.com/v1/domains \
-H "X-API-Key: e4a_..." \
-H "Content-Type: application/json" \
-d '{
"name": "yourdomain.com",
"auto_dns": true
}' {
"id": "dom_uuid",
"name": "yourdomain.com",
"mx_verified": false,
"spf_verified": false,
"dkim_verified": false,
"dmarc_verified": false,
"warmup_status": "new",
"daily_send_limit": 100,
"is_active": true,
"dns_records": { ... },
"dns_setup": {
"ses": "ok",
"cloudflare": "ok"
},
"created_at": "2025-04-16T10:00:00Z"
} /v1/domains List all domains curl https://api.emails4agents.com/v1/domains \
-H "X-API-Key: e4a_..." /v1/domains/{id}/verify Verify DNS records Checks MX, SPF, DKIM, and DMARC records are correctly configured.
{
"mx_verified": true,
"spf_verified": true,
"dkim_verified": true,
"dmarc_verified": true
} Inboxes
/v1/inboxes Create an inbox Provisions a new inbox. The address is {username}@{domain}.
curl -X POST https://api.emails4agents.com/v1/inboxes \
-H "X-API-Key: e4a_..." \
-d '{
"username": "agent",
"domain_id": "dom_uuid",
"display_name": "My Agent"
}' {
"id": "inb_uuid",
"domain_id": "dom_uuid",
"username": "agent",
"display_name": "My Agent",
"address": "[email protected]",
"is_active": true,
"created_at": "2025-04-16T10:00:00Z"
} /v1/inboxes List inboxes Optionally filter by domain_id.
curl "https://api.emails4agents.com/v1/inboxes?domain_id=dom_uuid" \
-H "X-API-Key: e4a_..." /v1/inboxes/{id} Get inbox by ID /v1/inboxes/{id} Deactivate inbox Soft-deletes the inbox (sets is_active: false). Message history is preserved.
Messages
/v1/messages/send Send an email Sends an email from an inbox via AWS SES. Provide at least text or html.
{
"from_inbox_id": "inb_uuid",
"to": "[email protected]",
"subject": "Hello from my agent",
"text": "Plain text body",
"html": "<p>HTML body</p>",
// Optional: reply in an existing thread
"reply_to_message_id": "msg_uuid",
// Optional: prevent duplicate sends
"idempotency_key": "unique-op-id"
} {
"id": "msg_uuid",
"inbox_id": "inb_uuid",
"thread_id": "thr_uuid",
"direction": "outbound",
"from_address": "[email protected]",
"to_address": "[email protected]",
"subject": "Hello from my agent",
"status": "queued",
"created_at": "2025-04-16T10:00:00Z"
} /v1/inboxes/{id}/messages List inbox messages Query params: direction (inbound|outbound), read (true|false), limit, offset.
curl "https://api.emails4agents.com/v1/inboxes/inb_uuid/messages?direction=inbound&read=false" \
-H "X-API-Key: e4a_..." /v1/messages/{id} Get a single message /v1/messages/{id}/reply Reply in-thread Sends a reply that preserves the In-Reply-To header chain, keeping email clients in the same conversation thread.
{
"text": "Thanks for reaching out. Here's what I found...",
"html": "<p>Thanks for reaching out. Here's what I found...</p>"
} /v1/threads/{thread_id} Get full thread Returns all messages in the thread ordered chronologically.
/v1/messages/search Search messages Full-text search across message content. Query params: q (search term), inbox_id, limit.
curl "https://api.emails4agents.com/v1/messages/search?q=invoice&inbox_id=inb_uuid" \
-H "X-API-Key: e4a_..." Webhooks
Webhooks deliver real-time event notifications to your endpoint. Register one or more URLs and specify which events to receive.
| Event | Trigger |
|---|---|
message.received | New inbound email stored in inbox |
message.sent | Outbound email accepted by SES |
message.delivered | SES confirms delivery |
message.bounced | Email bounced (hard or soft) |
message.classified | AI classification completed |
message.failed | Send failed after all retries |
domain.verified | DNS records verified successfully |
/v1/webhooks Register a webhook {
"url": "https://your-server.com/webhooks/email",
// null = receive events for all domains
"domain_id": null,
"events": [
"message.received",
"message.bounced"
]
} {
"event": "message.received",
"timestamp": "2025-04-16T10:23:45Z",
"data": {
"id": "msg_uuid",
"inbox_id": "inb_uuid",
"thread_id": "thr_uuid",
"direction": "inbound",
"from_address": "[email protected]",
"to_address": "[email protected]",
"subject": "Re: Your request",
"text_body": "Thanks, but can you also...",
"classification": "follow_up",
"classification_confidence": 0.94
}
} /v1/webhooks List webhooks /v1/webhooks/{id} Delete a webhook Retry policy: Failed deliveries are retried with exponential backoff: 30s → 2m → 10m → 1h → 6h. After all retries fail, the event is logged with status: failed.
API Keys
Manage programmatic access. Keys are scoped to permissions (read, send, admin) and optionally to a single domain.
/v1/api-keys Create an API key The raw key is only returned once at creation time — store it securely.
{
"name": "production-agent",
// null = global (all domains)
"domain_id": null,
// read | send | admin
"permissions": ["read", "send"]
} {
"id": "key_uuid",
"key": "e4a_abc123...", // only shown once
"key_prefix": "e4a_abc123",
"name": "production-agent",
"domain_id": null,
"permissions": ["read", "send"],
"rate_limit_per_minute": 60,
"is_active": true,
"created_at": "2025-04-16T10:00:00Z"
} /v1/api-keys List API keys Returns all active keys. Raw key values are never returned after creation.
/v1/api-keys/{id} Revoke an API key Agent Configuration
Configure an AI classifier on any inbox. When enabled, the agent runs via OpenRouter on every inbound message and attaches a classification label before the webhook fires.
/v1/inboxes/{id}/agent Configure agent for an inbox {
"system_prompt": "Classify this email as one of: sales_lead, support_request, spam, follow_up, other. Respond with JSON: {"classification": "...", "confidence": 0.0-1.0}",
// Any OpenRouter model ID
"model": "openai/gpt-4o-mini",
"max_iterations": 3,
"is_enabled": true,
// Optional knowledge base context
"kb_sources": []
} {
"id": "agc_uuid",
"inbox_id": "inb_uuid",
"is_enabled": true,
"system_prompt": "...",
"model": "openai/gpt-4o-mini",
"max_iterations": 3,
"kb_sources": [],
"tools_enabled": null,
"created_at": "2025-04-16T10:00:00Z",
"updated_at": "2025-04-16T10:00:00Z"
} /v1/inboxes/{id}/agent Get agent config /v1/inboxes/{id}/agent/runs Get agent run logs Returns classification run history including outcome, tokens used, and any errors.
{
"id": "run_uuid",
"message_id": "msg_uuid",
"inbox_id": "inb_uuid",
"outcome": "classified",
"tokens_used": 247,
"tools_called": [],
"context_used": {},
"error": null,
"created_at": "2025-04-16T10:00:00Z"
} MCP Server
The Emails4Agents API container ships a built-in Model Context Protocol server. Connect it to Claude Code, Cursor, or any MCP-compatible client to get native email tools.
Connect to Claude Code
{
"mcpServers": {
"emails4agents": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://api.emails4agents.com/mcp/sse",
"--header", "X-API-Key:${E4A_API_KEY}"
],
"env": {
"E4A_API_KEY": "e4a_your_key_here"
}
}
}
} Available tools
| Tool | Description |
|---|---|
list_domains | List all configured email domains |
create_domain | Add a new domain (auto DNS setup) |
verify_domain | Check DNS propagation status |
list_inboxes | List inboxes, optionally filter by domain |
create_inbox | Provision a new inbox |
send_email | Send an email from an inbox |
read_messages | Read messages from an inbox |
get_thread | Retrieve a full conversation thread |
reply_to_message | Reply in-thread preserving context |
search_messages | Full-text search across messages |
Error Codes
| Status | Meaning |
|---|---|
400 | Bad Request — missing or invalid fields |
401 | Unauthorized — invalid or missing API key |
403 | Forbidden — key lacks required permission scope |
404 | Not Found — resource does not exist |
409 | Conflict — resource already exists (e.g. duplicate inbox) |
422 | Unprocessable — validation error in request body |
429 | Rate Limited — exceeded requests per minute for this key |
500 | Internal Server Error — something went wrong on our side |
{
"detail": "Domain already exists"
}