API reference

Base path /v0. Authenticate with Authorization: Bearer <api_key>. Lists return { count, limit, next_page_token?, <plural> } and accept limit (1 to 100) and page_token. Missing resources and anything outside your key's scope return 404.

This reference is checked against the server's registered routes in CI, so a route cannot ship undocumented, and the permission shown for each route is probed against the server. The request and response examples were recorded from the running application, with ids, timestamps and secrets replaced by stable look-alikes and long lists shortened. Field-level details are in the guides. Which routes also have a dashboard screen, SDK method or MCP tool is in the feature table.

Organizations and agent sign-up#

Bootstrap and self-serve agent onboarding. Humans normally sign up in the dashboard instead.

POST/v0/agent/sign-up

Create an organization, an inbox and an API key in one call. No auth; rate limited per IP. Sending stays locked until a human is attached and verified.

Body: username (required), human_email, source, referrer

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/agent/sign-up" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"my-agent","human_email":"owner@example.com"}'

Response: HTTP 200

{
  "organization_id": "org_c9c37ce2e4f2a1279e83f57a",
  "inbox_id": "inbox_e3b8714e669f7f03aadf2880",
  "api_key": "am_bd48fa291f9292daa2953e3f4d7252ff3a916dfdfd7cda1c"
}
POST/v0/agent/attach-human

Attach (or replace, at most twice) the human who must verify the agent. Emails a 6-digit code.

Permission: (any key) · Body: human_email

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/agent/attach-human" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"human_email":"owner@example.com"}'

Response: HTTP 200

{
  "human_email": "owner@example.com",
  "instructions": "A 6-digit verification code was sent to owner@example.com. Submit it to POST /v0/agent/verify as otp_code."
}
POST/v0/agent/verify

Submit the 6-digit code to unlock sending.

Permission: (any key) · Body: otp_code

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/agent/verify" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"otp_code":"123456"}'

Response: HTTP 200

{
  "verified": true
}

Pods#

Isolated sub-tenants inside an organization.

POST/v0/pods

Create a pod.

Permission: pod_create · Body: name, client_id

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/pods" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme Corp","client_id":"acme"}'

Response: HTTP 200

{
  "pod_id": "pod_5ea618a82d38c4b9d0322273",
  "name": "Acme Corp",
  "client_id": "acme",
  "created_at": "2026-03-01T11:00:07.000Z",
  "updated_at": "2026-03-01T11:00:07.000Z"
}
GET/v0/pods

List pods.

Permission: pod_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/pods" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 50,
  "pods": [
    {
      "pod_id": "pod_5ea618a82d38c4b9d0322273",
      "name": "Acme Corp",
      "client_id": "acme",
      "created_at": "2026-03-01T11:00:07.000Z",
      "updated_at": "2026-03-01T11:00:07.000Z"
    },
    {
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "name": "Default Pod",
      "created_at": "2026-03-01T11:00:14.000Z",
      "updated_at": "2026-03-01T11:00:14.000Z"
    }
  ]
}
GET/v0/pods/:pod_id

Get a pod.

Permission: pod_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/pods/pod_5ea618a82d38c4b9d0322273" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "pod_id": "pod_5ea618a82d38c4b9d0322273",
  "name": "Acme Corp",
  "client_id": "acme",
  "created_at": "2026-03-01T11:00:07.000Z",
  "updated_at": "2026-03-01T11:00:07.000Z"
}
DELETE/v0/pods/:pod_id

Delete a pod. Fails while it still has child resources.

Permission: pod_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/pods/pod_1006a522b3866744235688db" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Inboxes#

POST/v0/inboxes
POST/v0/pods/:pod_id/inboxes

Create an inbox. Omit username for a random one. domain defaults to the server's base domain; any other domain must be a verified custom domain of yours (or a subdomain of one with subdomains_enabled). Returns the inbox: inbox_id, email, display_name, status, pod_id, created_at, updated_at. 402 plan_limit_exceeded when the plan's inbox limit is reached; 422 reserved_username for reserved names.

Permission: inbox_create · Body: username, domain, display_name, client_id, status, metadata

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"support-agent","display_name":"Support Agent","client_id":"support-v1","metadata":{"team":"support"}}'

Response: HTTP 200

{
  "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "email": "support-agent@agents.example.com",
  "display_name": "Support Agent",
  "client_id": "support-v1",
  "status": "active",
  "metadata": {
    "team": "support"
  },
  "created_at": "2026-03-01T11:00:28.000Z",
  "updated_at": "2026-03-01T11:00:28.000Z"
}
GET/v0/inboxes
GET/v0/pods/:pod_id/inboxes

List inboxes: { count, limit, next_page_token?, inboxes[] }, newest first.

Permission: inbox_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes?limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 2,
  "inboxes": [
    {
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "email": "support-agent@agents.example.com",
      "display_name": "Support Agent",
      "client_id": "support-v1",
      "status": "active",
      "metadata": {
        "team": "support"
      },
      "created_at": "2026-03-01T11:00:28.000Z",
      "updated_at": "2026-03-01T11:00:28.000Z"
    },
    {
      "pod_id": "pod_5ea618a82d38c4b9d0322273",
      "inbox_id": "inbox_bafed46d2e293c0b5c5cfcf7",
      "email": "acme-support@agents.example.com",
      "status": "active",
      "created_at": "2026-03-01T11:00:21.000Z",
      "updated_at": "2026-03-01T11:00:21.000Z"
    }
  ]
}
GET/v0/inboxes/:inbox_id

Get an inbox. :inbox_id is the inbox_id or the full email address, on every inbox route.

Permission: inbox_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "email": "support-agent@agents.example.com",
  "display_name": "Support Agent",
  "client_id": "support-v1",
  "status": "active",
  "metadata": {
    "team": "support"
  },
  "created_at": "2026-03-01T11:00:28.000Z",
  "updated_at": "2026-03-01T11:00:28.000Z"
}
PATCH/v0/inboxes/:inbox_id

Update display_name, status (active or paused) or metadata.

Permission: inbox_update

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"display_name":"Support","metadata":{"team":"support","tier":"1"}}'

Response: HTTP 200

{
  "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "email": "support-agent@agents.example.com",
  "display_name": "Support",
  "client_id": "support-v1",
  "status": "active",
  "metadata": {
    "team": "support",
    "tier": "1"
  },
  "created_at": "2026-03-01T11:00:28.000Z",
  "updated_at": "2026-03-01T11:00:35.000Z"
}
DELETE/v0/inboxes/:inbox_id
DELETE/v0/pods/:pod_id/inboxes/:inbox_id

Delete an inbox and its data.

Permission: inbox_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Messages#

Send, reply, forward, list, search and label messages. Send endpoints accept an Idempotency-Key header.

POST/v0/inboxes/:inbox_id/messages/send

Send a message. Returns { message_id, thread_id }. At least one of to, cc or bcc is required (400 validation_error); 403 message_rejected when the provider refuses a recipient; 402 plan_limit_exceeded over the plan's send caps; 403 email_unverified until the account email is verified.

Permission: message_send · Body: to, cc, bcc, reply_to, subject, text, html, attachments, headers, labels, track_opens

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/send" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
 -H "Idempotency-Key: order-4821-shipped" \
  -d '{"to":"maria@northwind.example","subject":"Your order #4821","text":"Hi Maria, it shipped yesterday."}'

Response: HTTP 200

{
  "message_id": "msg_92253243f3471651d425293d",
  "thread_id": "thr_8a0dd6009c22913f7b6e1396"
}
POST/v0/inboxes/:inbox_id/messages/:message_id/reply

Reply to the sender (threaded).

Permission: message_reply · Body: text, html, to, cc, bcc, attachments, headers, reply_all, track_opens

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829/reply" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"You are welcome."}'

Response: HTTP 200

{
  "message_id": "msg_231cf49d59380f36c61d1a7b",
  "thread_id": "thr_8a0dd6009c22913f7b6e1396"
}
POST/v0/inboxes/:inbox_id/messages/:message_id/reply-all

Reply to everyone on the message.

Permission: message_reply

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829/reply-all" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Thanks everyone."}'

Response: HTTP 200

{
  "message_id": "msg_7d2e1591f4f2dbb0fe599d30",
  "thread_id": "thr_8a0dd6009c22913f7b6e1396"
}
POST/v0/inboxes/:inbox_id/messages/:message_id/forward

Forward a message with its attachments.

Permission: message_send · Body: to (required), subject, text, html, cc, bcc, attachments, track_opens

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829/forward" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"ops@example.com","text":"FYI"}'

Response: HTTP 200

{
  "message_id": "msg_ef4bc57db6967a0cdfef7d2a",
  "thread_id": "thr_7268b4e2ae6593f1bfbd24de"
}
GET/v0/inboxes/:inbox_id/messages

List messages. Filters: labels, from, to, subject, before, after, ascending, limit, page_token.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages?labels=received&limit=10" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "limit": 10,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "message_id": "msg_f6bca2ea669a35112ab2a829",
      "rfc_message_id": "<reply-1@northwind.example>",
      "labels": [
        "received",
        "unread"
      ],
      "timestamp": "2026-03-01T11:00:42.000Z",
      "from": "Maria Costa <maria@northwind.example>",
      "to": [
        "support-agent@agents.example.com"
      ],
      "size": 94,
      "updated_at": "2026-03-01T11:00:49.000Z",
      "created_at": "2026-03-01T11:00:49.000Z",
      "subject": "Re: Your order #4821",
      "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
      "in_reply_to": "<msg-ref@x>",
      "references": [
        "<msg-ref@x>"
      ]
    }
  ]
}
GET/v0/inboxes/:inbox_id/messages/search

Ranked full-text search (q) over from, to, subject and body.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/search?q=thank" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 50,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "message_id": "msg_f6bca2ea669a35112ab2a829",
      "rfc_message_id": "<reply-1@northwind.example>",
      "labels": [
        "received",
        "unread"
      ],
      "timestamp": "2026-03-01T11:00:42.000Z",
      "from": "Maria Costa <maria@northwind.example>",
      "to": [
        "support-agent@agents.example.com"
      ],
      "size": 94,
      "updated_at": "2026-03-01T11:00:49.000Z",
      "created_at": "2026-03-01T11:00:49.000Z",
      "subject": "Re: Your order #4821",
      "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
      "in_reply_to": "<msg-ref@x>",
      "references": [
        "<msg-ref@x>"
      ],
      "highlights": {
        "text": "Great, **thank** you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday.\n"
      }
    },
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_7268b4e2ae6593f1bfbd24de",
      "message_id": "msg_ef4bc57db6967a0cdfef7d2a",
      "rfc_message_id": "<8f3c2a91-5b1e-4c7d-9a60-2d41e7b0c5aa@agents.example.com>",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:00:56.000Z",
      "from": "Support <support-agent@agents.example.com>",
      "to": [
        "ops@example.com"
      ],
      "size": 619,
      "updated_at": "2026-03-01T11:00:56.000Z",
      "created_at": "2026-03-01T11:00:56.000Z",
      "subject": "Fwd: Re: Your order #4821",
      "preview": "FYI ---------- Forwarded message --------- From: Maria Costa <maria@northwind.example> Date: 2026-03-01T11:00:42.000Z Subject: Re: Your order #4821 To: support-agent@agents.example.com Great, thank yo",
      "highlights": {
        "text": "FYI\n\n---------- Forwarded message ---------\nFrom: Maria Costa <maria@northwind.example>\nDate: 2026-03-01T11:00:42.000Z\nSubject: Re: Your order #4821\nTo: support-agent@agents.example.com\n\nGreat, **thank** you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday."
      }
    }
  ]
}
POST/v0/inboxes/:inbox_id/messages/search

Same as the GET form, parameters in the body.

Permission: message_read

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/search" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q":"thank","limit":5}'

Response: HTTP 200

{
  "count": 2,
  "limit": 5,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "message_id": "msg_f6bca2ea669a35112ab2a829",
      "rfc_message_id": "<reply-1@northwind.example>",
      "labels": [
        "received",
        "unread"
      ],
      "timestamp": "2026-03-01T11:00:42.000Z",
      "from": "Maria Costa <maria@northwind.example>",
      "to": [
        "support-agent@agents.example.com"
      ],
      "size": 94,
      "updated_at": "2026-03-01T11:00:49.000Z",
      "created_at": "2026-03-01T11:00:49.000Z",
      "subject": "Re: Your order #4821",
      "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
      "in_reply_to": "<msg-ref@x>",
      "references": [
        "<msg-ref@x>"
      ],
      "highlights": {
        "text": "Great, **thank** you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday.\n"
      }
    },
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_7268b4e2ae6593f1bfbd24de",
      "message_id": "msg_ef4bc57db6967a0cdfef7d2a",
      "rfc_message_id": "<8f3c2a91-5b1e-4c7d-9a60-2d41e7b0c5aa@agents.example.com>",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:00:56.000Z",
      "from": "Support <support-agent@agents.example.com>",
      "to": [
        "ops@example.com"
      ],
      "size": 619,
      "updated_at": "2026-03-01T11:00:56.000Z",
      "created_at": "2026-03-01T11:00:56.000Z",
      "subject": "Fwd: Re: Your order #4821",
      "preview": "FYI ---------- Forwarded message --------- From: Maria Costa <maria@northwind.example> Date: 2026-03-01T11:00:42.000Z Subject: Re: Your order #4821 To: support-agent@agents.example.com Great, thank yo",
      "highlights": {
        "text": "FYI\n\n---------- Forwarded message ---------\nFrom: Maria Costa <maria@northwind.example>\nDate: 2026-03-01T11:00:42.000Z\nSubject: Re: Your order #4821\nTo: support-agent@agents.example.com\n\nGreat, **thank** you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday."
      }
    }
  ]
}
POST/v0/inboxes/:inbox_id/messages/batch-get

Fetch up to 100 messages by id.

Permission: message_read · Body: message_ids

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/batch-get" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_ids":["msg_f6bca2ea669a35112ab2a829"]}'

Response: HTTP 200

{
  "count": 1,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "message_id": "msg_f6bca2ea669a35112ab2a829",
      "rfc_message_id": "<reply-1@northwind.example>",
      "labels": [
        "received",
        "unread"
      ],
      "timestamp": "2026-03-01T11:00:42.000Z",
      "from": "Maria Costa <maria@northwind.example>",
      "to": [
        "support-agent@agents.example.com"
      ],
      "size": 94,
      "updated_at": "2026-03-01T11:00:49.000Z",
      "created_at": "2026-03-01T11:00:49.000Z",
      "subject": "Re: Your order #4821",
      "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
      "in_reply_to": "<msg-ref@x>",
      "references": [
        "<msg-ref@x>"
      ]
    }
  ]
}
POST/v0/inboxes/:inbox_id/messages/batch-update

Add or remove labels on up to 100 messages.

Permission: message_update · Body: message_ids, add_labels, remove_labels

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/batch-update" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_ids":["msg_f6bca2ea669a35112ab2a829"],"add_labels":["handled"]}'

Response: HTTP 200

{
  "count": 1,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "message_id": "msg_f6bca2ea669a35112ab2a829",
      "rfc_message_id": "<reply-1@northwind.example>",
      "labels": [
        "received",
        "vip",
        "handled"
      ],
      "timestamp": "2026-03-01T11:00:42.000Z",
      "from": "Maria Costa <maria@northwind.example>",
      "to": [
        "support-agent@agents.example.com"
      ],
      "size": 94,
      "updated_at": "2026-03-01T11:01:10.000Z",
      "created_at": "2026-03-01T11:00:49.000Z",
      "subject": "Re: Your order #4821",
      "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
      "in_reply_to": "<msg-ref@x>",
      "references": [
        "<msg-ref@x>"
      ]
    }
  ]
}
GET/v0/inboxes/:inbox_id/messages/:message_id

Get one message with text, html and extracted_text.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "thread_id": "thr_8a0dd6009c22913f7b6e1396",
  "message_id": "msg_f6bca2ea669a35112ab2a829",
  "rfc_message_id": "<reply-1@northwind.example>",
  "labels": [
    "received",
    "unread"
  ],
  "timestamp": "2026-03-01T11:00:42.000Z",
  "from": "Maria Costa <maria@northwind.example>",
  "to": [
    "support-agent@agents.example.com"
  ],
  "size": 94,
  "updated_at": "2026-03-01T11:00:49.000Z",
  "created_at": "2026-03-01T11:00:49.000Z",
  "subject": "Re: Your order #4821",
  "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
  "text": "Great, thank you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday.\n",
  "extracted_text": "Great, thank you!",
  "in_reply_to": "<msg-ref@x>",
  "references": [
    "<msg-ref@x>"
  ],
  "headers": {
    "from": "\"Maria Costa\" <maria@northwind.example>",
    "to": "support-agent@agents.example.com",
    "subject": "Re: Your order #4821",
    "message-id": "<reply-1@northwind.example>",
    "date": "2026-03-01T11:00:42.000Z",
    "in-reply-to": "<msg-ref@x>",
    "references": "<msg-ref@x>",
    "content-type": "{\"value\":\"text/plain\",\"params\":{\"charset\":\"utf-8\"}}"
  }
}
PATCH/v0/inboxes/:inbox_id/messages/:message_id

Add or remove labels.

Permission: message_update · Body: add_labels, remove_labels

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"add_labels":["vip"],"remove_labels":["unread"]}'

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "thread_id": "thr_8a0dd6009c22913f7b6e1396",
  "message_id": "msg_f6bca2ea669a35112ab2a829",
  "rfc_message_id": "<reply-1@northwind.example>",
  "labels": [
    "received",
    "vip"
  ],
  "timestamp": "2026-03-01T11:00:42.000Z",
  "from": "Maria Costa <maria@northwind.example>",
  "to": [
    "support-agent@agents.example.com"
  ],
  "size": 94,
  "updated_at": "2026-03-01T11:01:03.000Z",
  "created_at": "2026-03-01T11:00:49.000Z",
  "subject": "Re: Your order #4821",
  "preview": "Great, thank you! On Fri, Oct 3, 2026 Support Agent wrote: > Hi Maria, it shipped yesterday.",
  "text": "Great, thank you!\n\nOn Fri, Oct 3, 2026 Support Agent wrote:\n> Hi Maria, it shipped yesterday.\n",
  "extracted_text": "Great, thank you!",
  "in_reply_to": "<msg-ref@x>",
  "references": [
    "<msg-ref@x>"
  ],
  "headers": {
    "from": "\"Maria Costa\" <maria@northwind.example>",
    "to": "support-agent@agents.example.com",
    "subject": "Re: Your order #4821",
    "message-id": "<reply-1@northwind.example>",
    "date": "2026-03-01T11:00:42.000Z",
    "in-reply-to": "<msg-ref@x>",
    "references": "<msg-ref@x>",
    "content-type": "{\"value\":\"text/plain\",\"params\":{\"charset\":\"utf-8\"}}"
  }
}
DELETE/v0/inboxes/:inbox_id/messages/:message_id

Delete a message.

Permission: message_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74/messages/msg_c5535d8ba2efb95d5c54ba3c" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)
GET/v0/inboxes/:inbox_id/messages/:message_id/raw

Get a short-lived download_url for the raw .eml: { message_id, size, expires_at, download_url }. The message source itself is at /raw/download.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829/raw" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "message_id": "msg_f6bca2ea669a35112ab2a829",
  "size": 383,
  "expires_at": "2026-03-01T11:01:17.000Z",
  "download_url": "https://mail.example.com/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_f6bca2ea669a35112ab2a829/raw/download"
}
GET/v0/inboxes/:inbox_id/messages/:message_id/raw/download

Download the raw RFC 822 message.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74/messages/msg_c5535d8ba2efb95d5c54ba3c/raw/download" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

From: Pat <pat@example.com>
To: scratch-bot@agents.example.com
Subject: Scratch
Message-ID: <scratch-1@example.com>
Date: Fri, 03 Oct 2026 10:00:00 +0000
Content-Type: text/plain; charset=utf-8

Scratch reply

Attachments#

Downloads are served with Content-Disposition: attachment, X-Content-Type-Options: nosniff and Content-Security-Policy: sandbox.

GET/v0/inboxes/:inbox_id/messages/:message_id/attachments/:attachment_id

Attachment metadata and a download_url.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_5614c0c994194fc530fb1b0d/attachments/att_31a6c30df6a314a70fe36ae2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "attachment_id": "att_31a6c30df6a314a70fe36ae2",
  "size": 26,
  "filename": "invoice.txt",
  "content_type": "text/plain",
  "download_url": "https://mail.example.com/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_5614c0c994194fc530fb1b0d/attachments/att_31a6c30df6a314a70fe36ae2/raw",
  "expires_at": "2026-03-01T11:01:24.000Z"
}
GET/v0/inboxes/:inbox_id/messages/:message_id/attachments/:attachment_id/raw
GET/v0/inboxes/:inbox_id/messages/:message_id/attachments/:attachment_id/download

Download the attachment bytes.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/messages/msg_5614c0c994194fc530fb1b0d/attachments/att_31a6c30df6a314a70fe36ae2/raw" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

Invoice 4821
Total: 42.00

Threads#

GET/v0/threads
GET/v0/pods/:pod_id/threads
GET/v0/inboxes/:inbox_id/threads

List threads.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/threads?limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 2,
  "next_page_token": "Mg",
  "threads": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_ce84e98d61f7bcf20b274ddc",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:01:31.000Z",
      "senders": [
        "Support <support-agent@agents.example.com>"
      ],
      "recipients": [
        "maria@northwind.example"
      ],
      "last_message_id": "msg_5614c0c994194fc530fb1b0d",
      "message_count": 1,
      "size": 35,
      "sent_timestamp": "2026-03-01T11:01:31.000Z",
      "subject": "Invoice",
      "preview": "Attached.",
      "updated_at": "2026-03-01T11:01:31.000Z",
      "created_at": "2026-03-01T11:01:31.000Z"
    },
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_7268b4e2ae6593f1bfbd24de",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:00:56.000Z",
      "senders": [
        "Support <support-agent@agents.example.com>"
      ],
      "recipients": [
        "ops@example.com"
      ],
      "last_message_id": "msg_ef4bc57db6967a0cdfef7d2a",
      "message_count": 1,
      "size": 619,
      "sent_timestamp": "2026-03-01T11:00:56.000Z",
      "subject": "Fwd: Re: Your order #4821",
      "preview": "FYI ---------- Forwarded message --------- From: Maria Costa <maria@northwind.example> Date: 2026-03-01T11:00:42.000Z Subject: Re: Your order #4821 To: support-agent@agents.example.com Great, thank yo",
      "updated_at": "2026-03-01T11:01:38.000Z",
      "created_at": "2026-03-01T11:00:56.000Z"
    }
  ]
}
GET/v0/threads/search
GET/v0/pods/:pod_id/threads/search
GET/v0/inboxes/:inbox_id/threads/search

Ranked search over threads (q).

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/threads/search?q=order" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 50,
  "threads": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396",
      "labels": [
        "received",
        "vip",
        "handled",
        "sent"
      ],
      "timestamp": "2026-03-01T11:01:45.000Z",
      "senders": [
        "Maria Costa <maria@northwind.example>",
        "Support <support-agent@agents.example.com>"
      ],
      "recipients": [
        "support-agent@agents.example.com",
        "maria@northwind.example",
        "Maria Costa <maria@northwind.example>"
      ],
      "last_message_id": "msg_7d2e1591f4f2dbb0fe599d30",
      "message_count": 4,
      "size": 157,
      "received_timestamp": "2026-03-01T11:00:42.000Z",
      "sent_timestamp": "2026-03-01T11:01:45.000Z",
      "subject": "Your order #4821",
      "preview": "Thanks everyone.",
      "updated_at": "2026-03-01T11:01:52.000Z",
      "created_at": "2026-03-01T11:01:59.000Z"
    },
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_7268b4e2ae6593f1bfbd24de",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:00:56.000Z",
      "senders": [
        "Support <support-agent@agents.example.com>"
      ],
      "recipients": [
        "ops@example.com"
      ],
      "last_message_id": "msg_ef4bc57db6967a0cdfef7d2a",
      "message_count": 1,
      "size": 619,
      "sent_timestamp": "2026-03-01T11:00:56.000Z",
      "subject": "Fwd: Re: Your order #4821",
      "preview": "FYI ---------- Forwarded message --------- From: Maria Costa <maria@northwind.example> Date: 2026-03-01T11:00:42.000Z Subject: Re: Your order #4821 To: support-agent@agents.example.com Great, thank yo",
      "updated_at": "2026-03-01T11:01:38.000Z",
      "created_at": "2026-03-01T11:00:56.000Z"
    }
  ]
}
GET/v0/threads/:thread_id
GET/v0/inboxes/:inbox_id/threads/:thread_id

Get a thread with its messages (messages[], oldest first; thread.messages.length equals message_count). Sent messages carry the sent label, received ones received.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/threads/thr_ce84e98d61f7bcf20b274ddc" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "thread_id": "thr_ce84e98d61f7bcf20b274ddc",
  "labels": [
    "sent"
  ],
  "timestamp": "2026-03-01T11:01:31.000Z",
  "senders": [
    "Support <support-agent@agents.example.com>"
  ],
  "recipients": [
    "maria@northwind.example"
  ],
  "last_message_id": "msg_5614c0c994194fc530fb1b0d",
  "message_count": 1,
  "size": 35,
  "sent_timestamp": "2026-03-01T11:01:31.000Z",
  "subject": "Invoice",
  "preview": "Attached.",
  "updated_at": "2026-03-01T11:01:31.000Z",
  "created_at": "2026-03-01T11:01:31.000Z",
  "attachments": [
    {
      "attachment_id": "att_31a6c30df6a314a70fe36ae2",
      "filename": "invoice.txt",
      "content_type": "text/plain",
      "content_disposition": "attachment",
      "size": 26
    }
  ],
  "count": 1,
  "limit": 50,
  "messages": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_ce84e98d61f7bcf20b274ddc",
      "message_id": "msg_5614c0c994194fc530fb1b0d",
      "rfc_message_id": "<8f3c2a91-5b1e-4c7d-9a60-2d41e7b0c5aa@agents.example.com>",
      "labels": [
        "sent"
      ],
      "timestamp": "2026-03-01T11:01:31.000Z",
      "from": "Support <support-agent@agents.example.com>",
      "to": [
        "maria@northwind.example"
      ],
      "subject": "Invoice",
      "preview": "Attached.",
      "text": "Attached.",
      "extracted_text": "Attached.",
      "size": 35,
      "attachments": [
        {
          "attachment_id": "att_31a6c30df6a314a70fe36ae2",
          "filename": "invoice.txt",
          "content_type": "text/plain",
          "content_disposition": "attachment",
          "size": 26
        }
      ],
      "created_at": "2026-03-01T11:01:31.000Z",
      "updated_at": "2026-03-01T11:01:31.000Z"
    }
  ]
}
PATCH/v0/threads/:thread_id
PATCH/v0/inboxes/:inbox_id/threads/:thread_id

Add or remove labels on every message in the thread.

Permission: message_update · Body: add_labels, remove_labels

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/threads/thr_ce84e98d61f7bcf20b274ddc" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"add_labels":["resolved"]}'

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "thread_id": "thr_ce84e98d61f7bcf20b274ddc",
  "labels": [
    "sent",
    "resolved"
  ],
  "timestamp": "2026-03-01T11:01:31.000Z",
  "senders": [
    "Support <support-agent@agents.example.com>"
  ],
  "recipients": [
    "maria@northwind.example"
  ],
  "last_message_id": "msg_5614c0c994194fc530fb1b0d",
  "message_count": 1,
  "size": 35,
  "sent_timestamp": "2026-03-01T11:01:31.000Z",
  "subject": "Invoice",
  "preview": "Attached.",
  "updated_at": "2026-03-01T11:02:06.000Z",
  "created_at": "2026-03-01T11:01:31.000Z"
}
DELETE/v0/threads/:thread_id
DELETE/v0/inboxes/:inbox_id/threads/:thread_id

Delete a thread.

Permission: message_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74/threads/thr_aaa80e4eb84817354bfe92d6" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Drafts#

Drafts can be scheduled with send_at. A draft that comes due while its inbox is paused fails and is not retried.

POST/v0/inboxes/:inbox_id/drafts

Create a draft.

Permission: draft_create · Body: to, cc, bcc, subject, text, html, attachments, in_reply_to, forward_of, reply_all, send_at, client_id, track_opens

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/drafts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"maria@northwind.example","subject":"Follow-up","text":"Checking in.","send_at":"2026-12-01T09:00:00Z"}'

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "draft_id": "draft_9a793a7ce9e58c68abc4125a",
  "labels": [
    "draft",
    "scheduled"
  ],
  "to": [
    "maria@northwind.example"
  ],
  "subject": "Follow-up",
  "preview": "Checking in.",
  "text": "Checking in.",
  "send_status": "scheduled",
  "send_at": "2026-03-01T11:02:13.000Z",
  "updated_at": "2026-03-01T11:02:20.000Z",
  "created_at": "2026-03-01T11:02:20.000Z"
}
GET/v0/drafts
GET/v0/inboxes/:inbox_id/drafts

List drafts across all inboxes (each item carries inbox_id): { count, limit, next_page_token?, drafts[] }. Fields that were never set (to, subject, text) are absent, not null.

Permission: draft_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/drafts" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "limit": 50,
  "drafts": [
    {
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "draft_id": "draft_9a793a7ce9e58c68abc4125a",
      "labels": [
        "draft",
        "scheduled"
      ],
      "to": [
        "maria@northwind.example"
      ],
      "subject": "Follow-up",
      "preview": "Checking in.",
      "text": "Checking in.",
      "send_status": "scheduled",
      "send_at": "2026-03-01T11:02:13.000Z",
      "updated_at": "2026-03-01T11:02:20.000Z",
      "created_at": "2026-03-01T11:02:20.000Z"
    }
  ]
}
GET/v0/drafts/:draft_id
GET/v0/inboxes/:inbox_id/drafts/:draft_id

Get a draft.

Permission: draft_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/drafts/draft_9a793a7ce9e58c68abc4125a" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "draft_id": "draft_9a793a7ce9e58c68abc4125a",
  "labels": [
    "draft",
    "scheduled"
  ],
  "to": [
    "maria@northwind.example"
  ],
  "subject": "Follow-up",
  "preview": "Checking in.",
  "text": "Checking in.",
  "send_status": "scheduled",
  "send_at": "2026-03-01T11:02:13.000Z",
  "updated_at": "2026-03-01T11:02:20.000Z",
  "created_at": "2026-03-01T11:02:20.000Z"
}
PATCH/v0/inboxes/:inbox_id/drafts/:draft_id

Update a draft (nullable fields clear it).

Permission: draft_update

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/drafts/draft_9a793a7ce9e58c68abc4125a" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Follow-up on your order"}'

Response: HTTP 200

{
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "draft_id": "draft_9a793a7ce9e58c68abc4125a",
  "labels": [
    "draft",
    "scheduled"
  ],
  "to": [
    "maria@northwind.example"
  ],
  "subject": "Follow-up on your order",
  "preview": "Checking in.",
  "text": "Checking in.",
  "send_status": "scheduled",
  "send_at": "2026-03-01T11:02:13.000Z",
  "updated_at": "2026-03-01T11:02:27.000Z",
  "created_at": "2026-03-01T11:02:20.000Z"
}
DELETE/v0/inboxes/:inbox_id/drafts/:draft_id

Delete a draft.

Permission: draft_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74/drafts/draft_d5597c16704a3536ee942724" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)
POST/v0/inboxes/:inbox_id/drafts/:draft_id/send

Send a draft now. Returns { message_id, thread_id }; the draft is consumed, so GET on it afterwards is 404.

Permission: draft_send

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/inboxes/inbox_bac2d3c9c3d41de7622edf3e/drafts/draft_9a793a7ce9e58c68abc4125a/send" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Response: HTTP 200

{
  "message_id": "msg_251a027005556a5921d41389",
  "thread_id": "thr_5c75dc4a06e42831e19e462c"
}

Allow and block lists#

direction is send, receive or reply; type is allow or block. Org, pod and inbox scopes.

POST/v0/lists/:direction/:type
POST/v0/pods/:pod_id/lists/:direction/:type
POST/v0/inboxes/:inbox_id/lists/:direction/:type

Add an entry (address or domain).

Permission: list_entry_create · Body: entry, reason (block only)

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/lists/send/allow" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"entry":"northwind.example"}'

Response: HTTP 200

{
  "entry": "northwind.example",
  "direction": "send",
  "list_type": "allow",
  "organization_id": "org_81e8bc749d45a4d9aa68b3cf",
  "created_at": "2026-03-01T11:02:41.000Z"
}
GET/v0/lists/:direction/:type
GET/v0/pods/:pod_id/lists/:direction/:type
GET/v0/inboxes/:inbox_id/lists/:direction/:type

List entries.

Permission: list_entry_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/lists/send/allow" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "limit": 50,
  "entries": [
    {
      "entry": "northwind.example",
      "direction": "send",
      "list_type": "allow",
      "organization_id": "org_81e8bc749d45a4d9aa68b3cf",
      "created_at": "2026-03-01T11:02:41.000Z"
    }
  ]
}
GET/v0/lists/:direction/:type/:entry
GET/v0/pods/:pod_id/lists/:direction/:type/:entry
GET/v0/inboxes/:inbox_id/lists/:direction/:type/:entry

Get an entry.

Permission: list_entry_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/lists/send/allow/northwind.example" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "entry": "northwind.example",
  "direction": "send",
  "list_type": "allow",
  "organization_id": "org_81e8bc749d45a4d9aa68b3cf",
  "created_at": "2026-03-01T11:02:41.000Z"
}
DELETE/v0/lists/:direction/:type/:entry
DELETE/v0/pods/:pod_id/lists/:direction/:type/:entry
DELETE/v0/inboxes/:inbox_id/lists/:direction/:type/:entry

Remove an entry.

Permission: list_entry_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/lists/send/allow/northwind.example" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Suppressions#

Addresses that bounced or complained are suppressed automatically.

GET/v0/suppressions

List suppressed addresses.

Permission: list_entry_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/suppressions" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "suppressions": [
    {
      "address": "gone@example.org",
      "reason": "bounce",
      "source_message_id": "msg_92253243f3471651d425293d",
      "created_at": "2026-03-01T11:04:33.000Z",
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "thread_id": "thr_8a0dd6009c22913f7b6e1396"
    }
  ],
  "limit": 50
}
DELETE/v0/suppressions/:address

Remove an address so you can send to it again.

Permission: list_entry_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/suppressions/gone@example.org" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Webhooks#

Signed deliveries with svix-id, svix-timestamp and svix-signature headers; see the webhooks guide.

POST/v0/webhooks
POST/v0/pods/:pod_id/webhooks
POST/v0/inboxes/:inbox_id/webhooks

Create a webhook. The signing secret (whsec_...) is returned only in this response; get, list and update never include it. event_types defaults to the eight default events. 400 invalid_webhook_url unless the URL is a public https URL; 402 plan_limit_exceeded at the plan's webhook limit.

Permission: webhook_create · Body: url, event_types, inbox_ids, pod_ids, client_id, headers

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/webhooks" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/mail","event_types":["message.received","message.bounced"]}'

Response: HTTP 200

{
  "webhook_id": "wh_cda90e6267fa3c9d2fbc06f8",
  "url": "https://example.com/hooks/mail",
  "secret": "whsec_MfKQ9r8GR4tJ7xP2Yb6mWq1Zs0Vn5Ae3",
  "enabled": true,
  "event_types": [
    "message.received",
    "message.bounced"
  ],
  "updated_at": "2026-03-01T11:02:48.000Z",
  "created_at": "2026-03-01T11:02:48.000Z"
}
GET/v0/webhooks
GET/v0/pods/:pod_id/webhooks
GET/v0/inboxes/:inbox_id/webhooks

List webhooks: { count, limit, next_page_token?, webhooks[] }. Items never include the secret.

Permission: webhook_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/webhooks" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "limit": 50,
  "webhooks": [
    {
      "webhook_id": "wh_cda90e6267fa3c9d2fbc06f8",
      "url": "https://example.com/hooks/mail",
      "enabled": true,
      "event_types": [
        "message.received",
        "message.bounced"
      ],
      "updated_at": "2026-03-01T11:02:48.000Z",
      "created_at": "2026-03-01T11:02:48.000Z"
    }
  ]
}
GET/v0/webhooks/:webhook_id
GET/v0/pods/:pod_id/webhooks/:webhook_id
GET/v0/inboxes/:inbox_id/webhooks/:webhook_id

Get a webhook.

Permission: webhook_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/webhooks/wh_cda90e6267fa3c9d2fbc06f8" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "webhook_id": "wh_cda90e6267fa3c9d2fbc06f8",
  "url": "https://example.com/hooks/mail",
  "enabled": true,
  "event_types": [
    "message.received",
    "message.bounced"
  ],
  "updated_at": "2026-03-01T11:02:48.000Z",
  "created_at": "2026-03-01T11:02:48.000Z"
}
PATCH/v0/webhooks/:webhook_id
PATCH/v0/pods/:pod_id/webhooks/:webhook_id
PATCH/v0/inboxes/:inbox_id/webhooks/:webhook_id

Update url, enabled, event_types, scope.

Permission: webhook_update

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/webhooks/wh_cda90e6267fa3c9d2fbc06f8" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":false}'

Response: HTTP 200

{
  "webhook_id": "wh_cda90e6267fa3c9d2fbc06f8",
  "url": "https://example.com/hooks/mail",
  "enabled": false,
  "event_types": [
    "message.received",
    "message.bounced"
  ],
  "updated_at": "2026-03-01T11:02:55.000Z",
  "created_at": "2026-03-01T11:02:48.000Z"
}
DELETE/v0/webhooks/:webhook_id
DELETE/v0/pods/:pod_id/webhooks/:webhook_id
DELETE/v0/inboxes/:inbox_id/webhooks/:webhook_id

Delete a webhook.

Permission: webhook_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/webhooks/wh_f9fc112fdaf52e085c35fddf" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)
GET/v0/webhooks/:webhook_id/headers
GET/v0/pods/:pod_id/webhooks/:webhook_id/headers
GET/v0/inboxes/:inbox_id/webhooks/:webhook_id/headers

List the names of custom headers (values are write-only).

Permission: webhook_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/webhooks/wh_cda90e6267fa3c9d2fbc06f8/headers" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "headers": [
    "X-Source"
  ]
}
PATCH/v0/webhooks/:webhook_id/headers
PATCH/v0/pods/:pod_id/webhooks/:webhook_id/headers
PATCH/v0/inboxes/:inbox_id/webhooks/:webhook_id/headers

Set or remove custom headers.

Permission: webhook_update · Body: set, remove

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/webhooks/wh_cda90e6267fa3c9d2fbc06f8/headers" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"set":{"X-Source":"mail"}}'

Response: HTTP 200

{
  "headers": [
    "X-Source"
  ]
}
GET/v0/webhooks/:webhook_id/deliveries

List recent delivery attempts for a webhook, newest first: { count, limit, next_page_token?, deliveries[] } where each delivery has delivery_id, event_id, event_type, status (pending, sending, delivered, failed), status_code (when the endpoint answered), attempts, error (http_4xx, http_5xx, timeout, ...) and timestamps. Supports limit (max 100) and page_token.

Permission: webhook_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/webhooks/wh_cda90e6267fa3c9d2fbc06f8/deliveries" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 0,
  "limit": 20,
  "deliveries": []
}

Events and WebSocket#

GET/v0/events

List recent events.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/events?limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 2,
  "next_page_token": "Mg",
  "events": [
    {
      "type": "event",
      "event_type": "message.sent",
      "event_id": "evt_e7c523914124347f5c4bcdfe",
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "created_at": "2026-03-01T11:03:02.000Z",
      "send": {
        "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
        "thread_id": "thr_5c75dc4a06e42831e19e462c",
        "message_id": "msg_251a027005556a5921d41389",
        "timestamp": "2026-03-01T11:03:02.000Z",
        "recipients": [
          "maria@northwind.example"
        ]
      }
    },
    {
      "type": "event",
      "event_type": "message.sent",
      "event_id": "evt_549daa0c133fe17d77b17f06",
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "created_at": "2026-03-01T11:03:09.000Z",
      "send": {
        "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
        "thread_id": "thr_ce84e98d61f7bcf20b274ddc",
        "message_id": "msg_5614c0c994194fc530fb1b0d",
        "timestamp": "2026-03-01T11:01:31.000Z",
        "recipients": [
          "maria@northwind.example"
        ]
      }
    }
  ]
}
GET/v0/inboxes/:inbox_id/events

List recent events for one inbox.

Permission: message_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/inboxes/inbox_6fb378a4cb6950fb2ad6af74/events?limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 2,
  "events": [
    {
      "type": "event",
      "event_type": "message.received",
      "event_id": "evt_bd13dfa858979da25eca160c",
      "inbox_id": "inbox_6fb378a4cb6950fb2ad6af74",
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "created_at": "2026-03-01T11:04:05.000Z",
      "message": {
        "inbox_id": "inbox_6fb378a4cb6950fb2ad6af74",
        "thread_id": "thr_aaa80e4eb84817354bfe92d6",
        "message_id": "msg_c5535d8ba2efb95d5c54ba3c",
        "rfc_message_id": "<scratch-1@example.com>",
        "labels": [
          "received",
          "unread"
        ],
        "timestamp": "2026-03-01T11:00:42.000Z",
        "from": "Pat <pat@example.com>",
        "to": [
          "scratch-bot@agents.example.com"
        ],
        "size": 14,
        "updated_at": "2026-03-01T11:04:12.000Z",
        "created_at": "2026-03-01T11:04:12.000Z",
        "subject": "Scratch",
        "preview": "Scratch reply",
        "text": "Scratch reply\n",
        "extracted_text": "Scratch reply",
        "headers": {
          "from": "\"Pat\" <pat@example.com>",
          "to": "scratch-bot@agents.example.com",
          "subject": "Scratch",
          "message-id": "<scratch-1@example.com>",
          "date": "2026-03-01T11:00:42.000Z",
          "content-type": "{\"value\":\"text/plain\",\"params\":{\"charset\":\"utf-8\"}}"
        }
      },
      "thread": {
        "inbox_id": "inbox_6fb378a4cb6950fb2ad6af74",
        "thread_id": "thr_aaa80e4eb84817354bfe92d6",
        "labels": [
          "received",
          "unread",
          "sent"
        ],
        "timestamp": "2026-03-01T11:04:19.000Z",
        "senders": [
          "Pat <pat@example.com>",
          "scratch-bot@agents.example.com"
        ],
        "recipients": [
          "scratch-bot@agents.example.com",
          "x@example.com"
        ],
        "subject": "Scratch",
        "preview": "Scratch.",
        "last_message_id": "msg_6ba0bb5675a04881e2df2ae6",
        "message_count": 2,
        "size": 22,
        "updated_at": "2026-03-01T11:04:05.000Z",
        "created_at": "2026-03-01T11:04:19.000Z"
      }
    },
    {
      "type": "event",
      "event_type": "message.sent",
      "event_id": "evt_63bc9f6f3b62134fac1e0d53",
      "inbox_id": "inbox_6fb378a4cb6950fb2ad6af74",
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "created_at": "2026-03-01T11:04:26.000Z",
      "send": {
        "inbox_id": "inbox_6fb378a4cb6950fb2ad6af74",
        "thread_id": "thr_aaa80e4eb84817354bfe92d6",
        "message_id": "msg_6ba0bb5675a04881e2df2ae6",
        "timestamp": "2026-03-01T11:04:19.000Z",
        "recipients": [
          "x@example.com"
        ]
      }
    }
  ]
}
GET/v0/websocket

Upgrade to a WebSocket and send a subscribe message. Accepts ?api_key= for browsers.

Custom domains#

Plan-limited. See the domains guide.

POST/v0/domains
POST/v0/pods/:pod_id/domains

Add a domain. Returns the full domain: status NOT_STARTED and the DNS records to publish. 402 plan_limit_exceeded when the plan allows no more domains (Free allows none).

Permission: domain_create · Body: domain, feedback_enabled, subdomains_enabled, tracking_enabled, client_id

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/domains" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"agents.northwind.example"}'

Response: HTTP 200, long lists shortened

{
  "domain_id": "agents.northwind.example",
  "domain": "agents.northwind.example",
  "feedback_enabled": true,
  "subdomains_enabled": false,
  "tracking_enabled": false,
  "created_at": "2026-03-01T11:03:16.000Z",
  "updated_at": "2026-03-01T11:03:16.000Z",
  "status": "NOT_STARTED",
  "records": [
    {
      "type": "MX",
      "name": "agents.northwind.example",
      "value": "inbound-smtp.us-east-1.amazonaws.com",
      "priority": 10,
      "status": "MISSING"
    },
    {
      "type": "TXT",
      "name": "agents.northwind.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "status": "MISSING"
    }
  ]
}
GET/v0/domains
GET/v0/pods/:pod_id/domains

List domains: { count, limit, next_page_token?, domains[] }. Items are summaries (domain_id, domain, flags, timestamps) WITHOUT status or records; fetch GET /v0/domains/:domain_id for those.

Permission: domain_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/domains" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 1,
  "limit": 50,
  "domains": [
    {
      "domain_id": "agents.northwind.example",
      "domain": "agents.northwind.example",
      "feedback_enabled": true,
      "subdomains_enabled": false,
      "tracking_enabled": false,
      "created_at": "2026-03-01T11:03:16.000Z",
      "updated_at": "2026-03-01T11:03:16.000Z"
    }
  ]
}
GET/v0/domains/:domain_id
GET/v0/pods/:pod_id/domains/:domain_id

Get a domain with status (NOT_STARTED, PENDING, VERIFYING, VERIFIED, FAILED), a reason while it is not verified (for example dns_records_missing) and its DNS records (type, name, value, priority for MX, per-record status MISSING, INVALID or VALID). :domain_id may also be the domain name.

Permission: domain_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/domains/agents.northwind.example" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200, long lists shortened

{
  "domain_id": "agents.northwind.example",
  "domain": "agents.northwind.example",
  "feedback_enabled": true,
  "subdomains_enabled": false,
  "tracking_enabled": false,
  "created_at": "2026-03-01T11:03:16.000Z",
  "updated_at": "2026-03-01T11:03:16.000Z",
  "status": "NOT_STARTED",
  "records": [
    {
      "type": "MX",
      "name": "agents.northwind.example",
      "value": "inbound-smtp.us-east-1.amazonaws.com",
      "priority": 10,
      "status": "MISSING"
    },
    {
      "type": "TXT",
      "name": "agents.northwind.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "status": "MISSING"
    }
  ]
}
PATCH/v0/domains/:domain_id
PATCH/v0/pods/:pod_id/domains/:domain_id

Update feedback, subdomain and tracking flags.

Permission: domain_update

Example request and response (recorded from the running server)
curl -X PATCH "$BASE_URL/v0/domains/agents.northwind.example" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tracking_enabled":true}'

Response: HTTP 200, long lists shortened

{
  "domain_id": "agents.northwind.example",
  "domain": "agents.northwind.example",
  "feedback_enabled": true,
  "subdomains_enabled": false,
  "tracking_enabled": true,
  "created_at": "2026-03-01T11:03:16.000Z",
  "updated_at": "2026-03-01T11:04:40.000Z",
  "status": "PENDING",
  "records": [
    {
      "type": "MX",
      "name": "agents.northwind.example",
      "value": "inbound-smtp.us-east-1.amazonaws.com",
      "priority": 10,
      "status": "MISSING"
    },
    {
      "type": "TXT",
      "name": "agents.northwind.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "status": "MISSING"
    }
  ],
  "reason": "dns_records_missing"
}
DELETE/v0/domains/:domain_id
DELETE/v0/pods/:pod_id/domains/:domain_id

Delete a domain. Responds 204.

Permission: domain_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/domains/scratch.example.net" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)
POST/v0/domains/:domain_id/verify
POST/v0/pods/:pod_id/domains/:domain_id/verify

Check DNS now and update the status. Responds 200 with an empty body; read the domain again for the new status.

Permission: domain_update

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/domains/agents.northwind.example/verify" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Response: HTTP 200

(empty body)
GET/v0/domains/:domain_id/zone-file
GET/v0/pods/:pod_id/domains/:domain_id/zone-file

Download the records as a zone file.

Permission: domain_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/domains/agents.northwind.example/zone-file" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

$ORIGIN agents.northwind.example.
$TTL 3600
agents.northwind.example.	IN	MX	10	inbound-smtp.us-east-1.amazonaws.com.
agents.northwind.example.	IN	TXT	"v=spf1 include:amazonses.com ~all"
mail1._domainkey.agents.northwind.example.	IN	TXT	"v=DKIM1; k=rsa; p=A7ebpssajPq9QCKWW6rXe+huSd3hO0eQe3OkPDGusrY="
_dmarc.agents.northwind.example.	IN	TXT	"v=DMARC1; p=none"

API keys#

POST/v0/api-keys

Create a key, optionally scoped to a pod or inbox and limited to named permissions. The secret (api_key, starting with the returned prefix) is shown once in this response; list never returns it. 403 email_unverified until the account email is verified.

Permission: api_key_create · Body: name, pod_id, inbox_id, permissions

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/api-keys" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"support-reader","inbox_id":"inbox_bac2d3c9c3d41de7622edf3e","permissions":["inbox_read","message_read"]}'

Response: HTTP 200

{
  "api_key_id": "key_8ca0692fef99229301a9c46e",
  "prefix": "am_af3f6ce",
  "name": "support-reader",
  "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
  "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
  "permissions": [
    "inbox_read",
    "message_read"
  ],
  "created_at": "2026-03-01T11:03:23.000Z",
  "api_key": "am_b774b982ad17764f5783632b0c4c841b80ec78ee67f3ccc5"
}
GET/v0/api-keys

List keys: { count, limit, next_page_token?, api_keys[] } with api_key_id, prefix, name, permissions, scope and created_at. Never the secret.

Permission: api_key_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/api-keys" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "count": 2,
  "limit": 50,
  "api_keys": [
    {
      "api_key_id": "key_8ca0692fef99229301a9c46e",
      "prefix": "am_af3f6ce",
      "name": "support-reader",
      "pod_id": "pod_7279e37cebf9a9b8e62c8cb2",
      "inbox_id": "inbox_bac2d3c9c3d41de7622edf3e",
      "permissions": [
        "inbox_read",
        "message_read"
      ],
      "created_at": "2026-03-01T11:03:23.000Z"
    },
    {
      "api_key_id": "key_be6463cd7e9e1395bb53c80d",
      "prefix": "am_b875258",
      "name": "default",
      "created_at": "2026-03-01T11:03:30.000Z"
    }
  ]
}
DELETE/v0/api-keys/:api_key_id

Revoke a key immediately. Responds 204.

Permission: api_key_delete

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/v0/api-keys/key_8ca0692fef99229301a9c46e" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 204

(empty body)

Metrics#

GET/v0/metrics/usage
GET/v0/pods/:pod_id/metrics/usage
GET/v0/inboxes/:inbox_id/metrics/usage

Running totals over time (storage_bytes, message_count, thread_count, inbox_count, domain_count, pod_count) for the last 90 days at most.

Permission: metrics_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/metrics/usage?usage_types=message_count&period=86400&limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "message_count": [
    {
      "timestamp": "2026-03-01T11:03:37.000Z",
      "value": 0
    },
    {
      "timestamp": "2026-03-01T11:03:44.000Z",
      "value": 7
    }
  ]
}
GET/v0/metrics/events

Event counts by type between start and end.

Permission: metrics_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/metrics/events?event_types=message.sent&period=86400&limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "total": 7,
  "counts": {
    "message.sent": 7
  }
}
GET/v0/metrics/rates
GET/v0/pods/:pod_id/metrics/rates
GET/v0/inboxes/:inbox_id/metrics/rates

Rolling bounce and complaint rates (plus open rate when tracking data exists) sampled every period over a trailing window. Each point has timestamp, sent (per recipient) and rate (null when nothing was sent).

Permission: metrics_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/metrics/rates?rate_types=bounce&period=86400&limit=2" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "bounce": [
    {
      "timestamp": "2026-03-01T11:03:51.000Z",
      "sent": 0,
      "rate": null
    },
    {
      "timestamp": "2026-03-01T11:03:58.000Z",
      "sent": 6,
      "rate": 0
    }
  ]
}

Billing#

Account billing endpoints used by the dashboard. Authenticated like the rest of /v0; billing permissions are never granted to scoped or permission-restricted API keys. GET /public/plans (no auth) lists plans as { id, name, prices: { usd | inr: { monthly, yearly } }, limits }.

GET/v0/billing

Current plan and usage: plan (free, pro, scale), plan_name, status (none, active, past_due, canceled, incomplete), source (free, admin, subscription), provider and interval (null on Free), current_period_end (null on Free), cancel_at_period_end, grace_ends_at while past_due, limits { inboxes, sends_per_month, sends_per_day (null = no daily cap), domains, webhooks } and usage { inboxes, sends_this_month, sends_today, domains, webhooks }. Needs an unrestricted key or an owner/admin dashboard session (permission billing_read).

Permission: billing_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/billing" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "plan": "free",
  "plan_name": "Free",
  "status": "none",
  "source": "free",
  "provider": null,
  "interval": null,
  "current_period_end": null,
  "cancel_at_period_end": false,
  "limits": {
    "inboxes": 3,
    "sends_per_month": 300,
    "sends_per_day": 100,
    "domains": 0,
    "webhooks": 1
  },
  "usage": {
    "inboxes": 2,
    "sends_this_month": 0,
    "sends_today": 0,
    "domains": 1,
    "webhooks": 1
  }
}
GET/v0/billing/usage

Only plan, limits and usage from the summary above.

Permission: billing_read

Example request and response (recorded from the running server)
curl "$BASE_URL/v0/billing/usage" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 200

{
  "plan": "free",
  "limits": {
    "inboxes": 3,
    "sends_per_month": 300,
    "sends_per_day": 100,
    "domains": 0,
    "webhooks": 1
  },
  "usage": {
    "inboxes": 2,
    "sends_this_month": 0,
    "sends_today": 0,
    "domains": 1,
    "webhooks": 1
  }
}
POST/v0/billing/checkout

Start a checkout for plan (pro or scale) and interval (monthly or yearly). Returns { url, provider }; send the user to url. country IN selects Razorpay unless provider is given. 503 billing_not_configured when that provider is not set up; 409 already_subscribed when an active subscription exists (use the portal).

Permission: billing_manage · Body: plan, interval, provider, country

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/billing/checkout" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plan":"pro","interval":"monthly"}'

Response: HTTP 200

{
  "url": "https://checkout.example/session/cs_1",
  "provider": "stripe"
}
POST/v0/billing/portal

Link to manage the subscription: { provider, url } (Stripe) or { provider, instructions } (Razorpay). 503 billing_not_configured when the provider is not set up.

Permission: billing_manage

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/billing/portal" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Response: HTTP 409

{
  "name": "ConflictError",
  "code": "no_subscription",
  "message": "No Stripe customer for this organization yet",
  "fix": "Start a subscription with POST /v0/billing/checkout first"
}
POST/v0/billing/cancel

Cancel at period end. Returns the billing summary with cancel_at_period_end true and a message. 409 no_subscription when there is nothing to cancel.

Permission: billing_manage

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/v0/billing/cancel" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Response: HTTP 409

{
  "name": "ConflictError",
  "code": "no_subscription",
  "message": "No active subscription to cancel"
}

Inbound mail (operators)#

Used by the mail forwarder you deploy, not by agents. Guarded by the x-inbound-secret header; the un-prefixed form exists because /v0 requires a Bearer key.

POST/inbound/raw

Deliver a raw RFC 822 message. Query: to=addr[,addr]. Headers: x-inbound-secret, optional x-auth-spf / x-auth-dkim / x-auth-dmarc.

POST/inbound/resend-events

Receiver for the email provider's delivery events (signature verified). Operator route; not for API clients.

Hosted MCP endpoint#

Remote Model Context Protocol server (Streamable HTTP, stateless) exposing the mail tools to MCP clients. Authenticate with Authorization: Bearer <api key>. Add ?read_only=1 to hide every tool that sends or changes data. Each tool call runs as the same REST request with your key, so permissions, plan limits and tenant isolation are identical. Body limit 256 KB; no CORS; responses are never cached. See the MCP guide.

POST/mcp

JSON-RPC 2.0 over HTTP: initialize, tools/list, tools/call. Missing or invalid key returns 401 with a WWW-Authenticate: Bearer header.

Permission: (any key; each tool needs its own API permission) · Body: JSON-RPC request (Content-Type: application/json). Query: read_only=1

Example request and response (recorded from the running server)
curl -X POST "$BASE_URL/mcp" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
 -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Response: HTTP 200, long lists shortened

{
  "result": {
    "tools": [
      {
        "name": "create_inbox",
        "description": "Create a new email inbox. Returns its inbox_id and email address.",
        "inputSchema": "..."
      },
      {
        "name": "list_inboxes",
        "description": "List inboxes",
        "inputSchema": "..."
      }
    ]
  },
  "jsonrpc": "2.0",
  "id": 1
}
GET/mcp

405 Method Not Allowed. The endpoint is stateless and has no server-initiated stream.

Example request and response (recorded from the running server)
curl "$BASE_URL/mcp" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 405

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Method not allowed: this server is stateless; use POST"
  },
  "id": null
}
DELETE/mcp

405 Method Not Allowed. There are no sessions to terminate.

Example request and response (recorded from the running server)
curl -X DELETE "$BASE_URL/mcp" \
  -H "Authorization: Bearer $API_KEY"

Response: HTTP 405

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Method not allowed: this server is stateless; use POST"
  },
  "id": null
}