HermesAgentMail API
Agent inbox infrastructure over a simple REST API. Create inboxes on hermesagentmail.com, receive inbound email through signed webhooks, and send replies with human approval built in. Everything lives at https://hermesagentmail.com — no separate API subdomain.
Overview
HermesAgentMail gives every agent workflow its own address on hermesagentmail.com. Inbound mail is verified, queued, stored encrypted, and pushed to your endpoints. Outbound mail goes through an approval model so a human signs off before an agent sends, unless you explicitly opt an inbox into auto mode.
Inbound flow
- 1Email arrives at
your-inbox@hermesagentmail.com. - 2Resend receives the message and delivers a verified webhook to us.
- 3The event lands in a durable queue for reliable processing.
- 4A worker stores the message encrypted (AES-256-GCM at the application layer).
- 5An HMAC-signed webhook is POSTed to the endpoints you configured in the dashboard under Webhooks.
REST API
JSON over HTTPS at hermesagentmail.com/api/v1. Bearer token auth. Predictable errors.
Webhook-first
Inbound email is pushed to you, HMAC-signed and verifiable.
Approvals
Replies wait for human approval unless the inbox is set to auto.
Authentication
Authenticate every request with an API key in the Authorization header. Keys are created in the dashboard, look like hm_live_…, are stored as SHA-256 hashes at rest, and are scoped — a key can only call endpoints its scopes allow. Revoke a key at any time from the dashboard.
curl https://hermesagentmail.com/api/v1/inboxes \
-H "Authorization: Bearer hm_live_..."| Scope | Grants |
|---|---|
email:read | List inboxes, list messages, and read full message content. |
email:draft | Draft replies to inbound messages. |
email:send_request | Request that a drafted reply is sent. |
email:write | Full email write access, including direct sends where allowed. |
inbox:write | Create and manage inboxes. |
Quickstart
The full loop with curl: create an inbox, list inbound messages, read one, draft a reply, then request that it is sent. You need a key with the inbox:write, email:read, email:draft, and email:send_request scopes.
curl -X POST https://hermesagentmail.com/api/v1/inboxes \
-H "Authorization: Bearer $HERMES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"local_part": "research",
"agent_name": "Research Agent"
}'curl "https://hermesagentmail.com/api/v1/messages?limit=50" \
-H "Authorization: Bearer $HERMES_API_KEY"curl https://hermesagentmail.com/api/v1/messages/MESSAGE_ID \
-H "Authorization: Bearer $HERMES_API_KEY"curl -X POST https://hermesagentmail.com/api/v1/messages/MESSAGE_ID/reply \
-H "Authorization: Bearer $HERMES_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Thanks for reaching out — here is the summary you asked for." }'curl -X POST https://hermesagentmail.com/api/v1/outbound/OUTBOUND_MESSAGE_ID/request-send \
-H "Authorization: Bearer $HERMES_API_KEY"If the inbox is in required approval mode, step 5 returns approval_required and the message waits in the dashboard approval queue. In auto mode it returns sent.
Inboxes API
An inbox is an address plus an agent identity and an approval mode. Reserved local parts (support, billing, abuse, postmaster, admin, noreply, and other system names) cannot be claimed.
curl https://hermesagentmail.com/api/v1/inboxes \
-H "Authorization: Bearer $HERMES_API_KEY"
# Requires scope: email:read.
{
"data": [
{
"id": "8f4c1a2e-...",
"address": "research@hermesagentmail.com",
"agent": "Research Agent",
"status": "active",
"approval_mode": "required",
"received": 128,
"sent": 31,
"created_at": "2026-06-30T09:12:00Z"
}
]
}curl -X POST https://hermesagentmail.com/api/v1/inboxes \
-H "Authorization: Bearer $HERMES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"local_part": "research",
"agent_name": "Research Agent"
}'
# Requires scope: inbox:write. Returns 201.
{
"data": {
"id": "8f4c1a2e-...",
"address": "research@hermesagentmail.com",
"agent": "Research Agent",
"status": "active",
"approval_mode": "required",
"created_at": "2026-07-05T02:41:00Z"
}
}Errors: 400 invalid_local_part, 400 reserved_address, 400 address_taken, and 402 quota_exceeded when your plan has no inbox slots left. Every new inbox starts as approval_mode: "required" — a human approves every send. On paid plans, an inbox earns auto-send in the dashboard after 10 approved sends with no bounces or complaints, and loses it automatically if either occurs later.
Messages API
List inbound messages across your inboxes, or filter to a single thread. The list endpoint returns metadata and a snippet only; fetch a message by id for the full decrypted body.
curl "https://hermesagentmail.com/api/v1/messages?limit=50&thread_id=THREAD_ID" \
-H "Authorization: Bearer $HERMES_API_KEY"
# Requires scope: email:read. Metadata + snippet only.
{
"data": [
{
"id": "3b9d7c10-...",
"thread_id": "5a2e8f44-...",
"address": "research@hermesagentmail.com",
"from": "customer@example.com",
"subject": "Invoice question",
"snippet": "Can you resend my latest invoice?",
"created_at": "2026-07-04T12:00:00Z"
}
]
}curl https://hermesagentmail.com/api/v1/messages/MESSAGE_ID \
-H "Authorization: Bearer $HERMES_API_KEY"
# Requires scope: email:read. Full message.
{
"data": {
"id": "3b9d7c10-...",
"thread_id": "5a2e8f44-...",
"from": "customer@example.com",
"subject": "Invoice question",
"text": "Can you resend my latest invoice?",
"html": "<p>Can you resend my latest invoice?</p>",
"headers": { "message-id": "<abc@example.com>" },
"attachments": [
{ "filename": "invoice.pdf", "content_type": "application/pdf", "size": 48213 }
],
"created_at": "2026-07-04T12:00:00Z"
}
}Message bodies are stored encrypted and decrypted on read. Attachment entries are metadata only — filenames, content types, and sizes.
Replies & sending
Sending is a two-step flow: draft a reply, then request that it is sent. Human approval before send is the default everywhere. A draft never leaves the platform until it is either approved in the dashboard or the inbox is in auto approval mode.
curl -X POST https://hermesagentmail.com/api/v1/messages/MESSAGE_ID/reply \
-H "Authorization: Bearer $HERMES_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "plain text reply" }'
# Requires scope: email:draft. Returns 201.
{
"data": {
"outbound_message_id": "c71f0b3d-...",
"approval_required": true,
"status": "pending_approval"
}
}curl -X POST https://hermesagentmail.com/api/v1/outbound/OUTBOUND_MESSAGE_ID/request-send \
-H "Authorization: Bearer $HERMES_API_KEY"
# Requires scope: email:send_request.
{
"data": {
"status": "sent" | "approval_required" | "blocked",
"reason": "optional explanation when blocked"
}
}sent means the message went out. approval_required means it is waiting in the dashboard approval queue. blocked means policy stopped the send — for example a recipient on the suppression list (maintained from bounces and complaints) or an exhausted outbound quota — with the reason field explaining why.
Webhooks
Configure endpoints in the dashboard under Webhooks. When an inbound email is stored, we POST an email.receivedevent to each endpoint, signed with that endpoint's whsec_ secret. Verify the signature, respond with a 2xx quickly, and do the heavy work asynchronously.
| Header | Value |
|---|---|
X-HermesAgentMail-Event | email.received |
X-HermesAgentMail-Timestamp | Unix timestamp in seconds when the delivery was signed. |
X-HermesAgentMail-Signature | Hex HMAC-SHA256 of {timestamp}.{rawBody}using your endpoint's whsec_ secret. |
{
"event": "email.received",
"message_id": "3b9d7c10-...",
"thread_id": "5a2e8f44-...",
"workflow_id": "8f4c1a2e-...",
"address": "research@hermesagentmail.com",
"from": "customer@example.com",
"subject": "Invoice question",
"snippet": "Can you resend my latest invoice?",
"requires_human_approval": true,
"created_at": "2026-07-04T12:00:00Z"
}import crypto from 'node:crypto'
export function verifyHermesWebhook({
rawBody,
timestamp,
signature,
secret, // Your whsec_ endpoint secret from the dashboard.
}: {
rawBody: string
timestamp: string
signature: string
secret: string
}): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')
const a = Buffer.from(signature, 'hex')
const b = Buffer.from(expected, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
// Usage in a route handler: read the raw request body (before any
// JSON parsing) and the two X-HermesAgentMail-* headers, then reject
// the request with a 401 if verification fails.MCP server
HermesAgentMail exposes a Model Context Protocol server over streamable HTTP at https://hermesagentmail.com/api/mcp, authenticated with the same hm_live_… API keys. Agents get email as first-class tools, and human approval is still required before send unless the inbox approval mode is auto. MCP access is available on Pro and Business plans.
{
"mcpServers": {
"hermesagentmail": {
"url": "https://hermesagentmail.com/api/mcp",
"headers": { "Authorization": "Bearer hm_live_..." }
}
}
}| Tool | What it does |
|---|---|
list_inboxes | List the inboxes in your workspace. |
create_inbox | Create a new agent inbox. |
list_emails | List recent inbound messages with metadata and snippets. |
get_email | Fetch a full message, including body and attachments metadata. |
search_thread | Search messages within a thread. |
draft_reply | Draft a reply to an inbound message. |
request_send | Request that a drafted reply is sent (subject to approval). |
send_email | Send an email where policy allows it. |
mark_handled | Mark a message or thread as handled. |
Use with Hermes
Running the Hermes agent? Give it a real inbox in a couple of minutes. There are two ways to install — both need an API key from your dashboard. Every new inbox is approval-required, so your agent drafts and sends without ever being able to send something you did not allow.
Option A — MCP (zero code, recommended)
Hermes is an MCP client. Point it at the HermesAgentMail MCP server by adding this to ~/.hermes/config.yaml, then set HERMES_AGENT_MAIL_API_KEY and restart Hermes. Tools appear as mcp_hermes_agent_mail_*.
mcp_servers:
hermes-agent-mail:
url: "https://www.hermesagentmail.com/api/mcp"
headers:
Authorization: "Bearer ${HERMES_AGENT_MAIL_API_KEY}"
enabled: trueOr add it from the CLI, then restart your session:
export HERMES_AGENT_MAIL_API_KEY="hm_live_..."
hermes mcp add hermes-agent-mail \
--url https://www.hermesagentmail.com/api/mcpOption B — Native plugin (drop-in, no MCP needed)
Prefer native tool names, or not using MCP? Install the standalone Hermes plugin with one command. It adds six agentmail_* tools plus a behavior skill, and depends only on the Python standard library.
curl -fsSL https://www.hermesagentmail.com/hermes/install.sh | bash
# add HERMES_AGENT_MAIL_API_KEY=hm_live_... to ~/.hermes/.env
hermes plugins enable hermes-agent-mail
# restart Hermes — you'll see the "agentmail" toolset| Tool | What it does |
|---|---|
agentmail_list_inboxes | List the agent inboxes in your workspace. |
agentmail_create_inbox | Create name@hermesagentmail.com. |
agentmail_list_emails | List recent inbound messages (metadata). |
agentmail_get_email | Full decrypted message and attachments. |
agentmail_draft_reply | Draft a threaded reply (does not send). |
agentmail_request_send | Send it, or route to human approval. |
Prefer to have an AI agent set it up?
Copy one of these prompts and paste it into your AI agent. For Option A, ask Hermes itself (it has terminal and file tools). For Option B, use a coding agent with shell access like Cursor or Claude Code. Replace hm_live_... with your key from the dashboard.
Prompt for Hermes — configure MCP (Option A):
Configure the HermesAgentMail email integration for yourself.
Add an MCP server to ~/.hermes/config.yaml under "mcp_servers" named
"hermes-agent-mail" with:
url: https://www.hermesagentmail.com/api/mcp
headers.Authorization: "Bearer ${HERMES_AGENT_MAIL_API_KEY}"
enabled: true
Do not remove or overwrite any existing mcp_servers entries. Then make
sure HERMES_AGENT_MAIL_API_KEY=hm_live_... is set in my ~/.hermes/.env
(create it if missing). Finally, tell me to restart my Hermes session so
the email tools load.Prompt for Cursor / Claude Code — install the plugin (Option B):
Install the HermesAgentMail plugin into my Hermes agent.
1. Run: curl -fsSL https://www.hermesagentmail.com/hermes/install.sh | bash
(this downloads the plugin into ~/.hermes/plugins/hermes-agent-mail)
2. Add HERMES_AGENT_MAIL_API_KEY=hm_live_... to ~/.hermes/.env
3. Run: hermes plugins enable hermes-agent-mail
4. Confirm the "agentmail" toolset loaded (hermes plugins list) and tell me
to restart Hermes.Once connected, try a first task with your agent:
Check my HermesAgentMail inboxes, read the most recent email, and draft a
reply. Request the send — if it needs approval, tell me and I'll approve it
in the dashboard.Waking the agent on new email
To have inbound mail wake your agent instead of polling, add a webhook endpoint in the dashboard (Webhooks) pointing at your Hermes gateway. We deliver a signed email.received event — see the Webhooks section for the payload and signature verification.
Errors
Every error response uses the same envelope, with a stable code and a human-readable message.
{
"error": {
"code": "quota_exceeded",
"message": "Your plan's inbox limit has been reached. Upgrade to add more inboxes."
}
}| Status | Code | Meaning |
|---|---|---|
400 | invalid_local_part | The requested local part contains invalid characters. |
400 | reserved_address | The address is reserved by the platform (support@, abuse@, postmaster@, …). |
400 | address_taken | The address is already claimed by another workspace. |
401 | unauthorized | The API key is missing, malformed, or revoked. |
402 | quota_exceeded | Your plan quota (inboxes or monthly emails) has been reached. |
403 | insufficient_scope | The API key does not have the scope required for this endpoint. |
404 | not_found | The resource does not exist or belongs to another workspace. |
429 | rate_limited | Too many requests. Back off and retry. |
Rate limits & quotas
Requests are rate limited per key; back off and retry on 429. Monthly email quotas count both inbound and outbound messages, and message retention varies by plan. When a quota is exhausted the API returns 402 quota_exceeded.
| Plan | Price | Inboxes | Emails / mo (in / out) | Retention | MCP |
|---|---|---|---|---|---|
| Free | $0/mo | 1 | 75 / 25 | 7 days | Included |
| Starter | $5/mo | 3 | 750 / 250 | 30 days | Included |
| Pro | $14.99/mo | 10 | 3,750 / 1,250 | 90 days | Included |
| Business | $99/mo | 100 | 37,500 / 12,500 | 1 year | Included + team |
Want early access?
HermesAgentMail is in early access. Join the waitlist to get an API key when workspaces open up.
