Developer
A REST email API built for AI agents
AutoEmail's Agent REST API gives an AI agent a token-authenticated email inbox over HTTP: list and read mail, draft or send replies, run outreach, and manage contacts and calendar events. One bearer API key fixes the key's mode - human_in_the_loop queues every write for approval, full_autonomous sends immediately unless told otherwise.
The agent loop
A typical run looks the same whether the caller is a scheduled job or a long-running agent process: authenticate once, discover what the key is allowed to touch, then loop through read, decide, and draft or send. Automated senders should also set a fresh Idempotency-Key header on every write and reuse that same key only when retrying the exact same logical email, so a network timeout or a 5xx response can never send the same reply twice.
- 1GET
/meAuthenticateSend the bearer key and read back its mode (human_in_the_loop or full_autonomous) and its allowedAccounts. Nothing else works until this call succeeds.
- 2GET
/accountsDiscover sendable accountsList the mailboxes this specific key may read and send from - the same non-secret businessId, domain, email, name shape returned inside GET /me.
- 3GET
/emailsRead the inboxPage through mail, filterable by businessId, status, or a search term. Each row already carries the resolved effectiveReplyTo and effectiveReplySubject a reply will actually use.
- 4GET
/emails/{id}Open one messageFetch the full body, any attachments, and the latest draft (or null) for a single email inside the key's allowlist.
- 5POST
/emails/{id}/replyDraft or send a replyReply with final copy, or set mode: "generate" and let AutoEmail write it. What happens next depends entirely on the key's mode - see below.
- 6POST
/emails/{id}/approveApprove the draftOn a full_autonomous key, approve sends the latest draft over SMTP and marks the email sent.
- 7POST
/emails/{id}/declineOr decline itReject a pending item instead. Optional feedback is summarized into a learning that improves future drafts, in either key mode.
Human-in-the-loop vs full-autonomous
The API has two operating modes, fixed per key, that mirror the two paid tiers where API access exists at all. On the Team plan ($149/month billed monthly, $127/month billed annually), every key is human_in_the_loop: a call to POST /emails/{id}/reply or POST /emails/send always creates a pending draft in the dashboard approval queue. Nothing sends until a person reviews it and clicks approve, whatever the request body says.
On the Agentic plan ($399/month billed monthly, $339/month billed annually), keys are full_autonomous: final-copy replies and sends go out over SMTP immediately, and the email is marked sent in the same response. Setting send: false on that same call downgrades it back to a draft for that one request, so an otherwise autonomous key can still ask for review on a case-by-case basis. AI-generated copy (mode: "generate") always queues a draft by default and only sends with an explicit send: true.
The same split governs POST /outreach/batch: a human_in_the_loop key that sets send: true is rejected outright with 403 mode_not_allowed, while a full_autonomous key with send: true drip-sends the accepted recipients with a small jittered delay between each send for deliverability, and reports the real per-recipient outcome asynchronously.
Free and Operator plans have no agent API access at all (agentApiAccess: "none") - no key can be issued on those plans, so GET /me returns 401 for any bearer token presented against them.
Authentication
Every request carries one bearer API key: Authorization: Bearer ak_live_<random>. Auth fails closed (401) on a missing or non-Bearer header, an empty token, an unknown key, or a revoked key.
Keys are created in the dashboard under Settings, API Keys. The plaintext is shown exactly once at creation; the server stores only a SHA-256 hash, so a lost key cannot be recovered, only revoked and replaced. Call GET /me first in any new integration - it returns the key's id, its mode, and the allowedAccounts it may read and send from, which is everything needed to start the loop above.
The API is CORS-closed on purpose: no Access-Control-Allow-* headers, no OPTIONS handlers. It is meant to be called from a backend, never from client-side JavaScript.
Every route also enforces its own per-key rate limit on a 60-second sliding window: discovery reads such as GET /me and GET /accounts allow up to 60 requests per minute, listing endpoints like GET /emails allow 120, writes such as replying or sending are capped at 30, and the IMAP sync trigger is limited to 6. A well-behaved agent reads GET /usage to track remaining quota and backs off before it hits a 429 or a 402 quota_exceeded.
Endpoint reference
28 operations across 13 tags, generated directly from the committed OpenAPI spec (v2.0.2). Open the interactive explorer for full request/response schemas and to try a call.
Discovery
| Method | Path | Summary |
|---|---|---|
| GET | /me | Get key identity and capabilities |
| GET | /accounts | List accounts this key may send from |
Read
| Method | Path | Summary |
|---|---|---|
| GET | /emails | List / filter emails |
| GET | /search | Full-text email search |
| GET | /emails/{id} | Get a single email and its latest draft |
Write
| Method | Path | Summary |
|---|---|---|
| POST | /emails/{id}/reply | Reply to an existing inbound email |
| POST | /emails/send | Compose and send a new outbound email |
| POST | /outreach/batch | Bulk-send outreach to up to 100 recipients (API-only primitive) |
Sync
| Method | Path | Summary |
|---|---|---|
| POST | /accounts/{businessId}/sync | Trigger an IMAP sync for one mailbox |
Threads
| Method | Path | Summary |
|---|---|---|
| GET | /threads | List conversation threads |
| GET | /threads/{id} | Get a thread + message summaries |
State
| Method | Path | Summary |
|---|---|---|
| PATCH | /emails/{id} | Update email state (read/starred/status) or the reply recipient/subject |
Drafts
| Method | Path | Summary |
|---|---|---|
| GET | /emails/{id}/drafts | List draft versions of an email |
| POST | /emails/{id}/approve | Approve + send the latest draft |
| POST | /emails/{id}/decline | Decline a pending email |
Attachments
| Method | Path | Summary |
|---|---|---|
| GET | /emails/{id}/attachments | List attachment metadata for an email |
| GET | /attachments/{id}/download-url | Get a short-lived attachment download URL |
Contacts
| Method | Path | Summary |
|---|---|---|
| GET | /contacts | List/search contacts |
| POST | /contacts | Create or upsert a contact |
| PATCH | /contacts/{id} | Update a contact |
| DELETE | /contacts/{id} | Delete a contact |
Calendar
| Method | Path | Summary |
|---|---|---|
| GET | /calendar-events | List calendar events (read-only) |
Spam
| Method | Path | Summary |
|---|---|---|
| GET | /accounts/{businessId}/spam-settings | Get spam settings |
| PUT | /accounts/{businessId}/spam-settings | Update spam settings |
Lessons
| Method | Path | Summary |
|---|---|---|
| GET | /accounts/{businessId}/lessons | List active learnings + spam lessons |
| POST | /accounts/{businessId}/lessons | Add a manual learning |
| DELETE | /lessons/{id} | Delete a learning |
Usage
| Method | Path | Summary |
|---|---|---|
| GET | /usage | Get current-period quota usage |
Examples
Two real calls against the production base URL. Replace the bearer token with your own key and the email id with one returned by GET /emails.
Discover the key's identity and capabilities:
curl https://courteous-gopher-315.eu-west-1.convex.site/api/v1/me \
-H "Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Reply to an email with final copy, held as a draft for this one call:
curl -X POST https://courteous-gopher-315.eu-west-1.convex.site/api/v1/emails/m3k9q1w5e7r2t4y6u8i0o9p1/reply \
-H "Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3c1e2a-0b4d-4c9e-8a21-9d6f0b2e5c14" \
-d '{
"body": "Your order shipped yesterday - tracking attached.",
"send": false
}'What this is not
This is not a bulk transactional-email provider. POST /outreach/batch takes 1 to 100 recipients per call, with a per-mailbox daily send cap, dedupe against recently-contacted addresses, and a targeted pre-send reply check - it is built for agent-orchestrated outreach, not for replacing an ESP send API designed for six-figure volumes per hour.
This is not CORS-enabled. There are no Access-Control-Allow-* headers and no OPTIONS handlers, so the API cannot be called from a browser. Treat the bearer key like any other server secret and call it from a backend only.
This is not available on every plan. GET /me and GET /accounts work for any valid key, but writes require a plan that grants agent API access: human_in_the_loop on Team, full (including autonomous send) on Agentic. Free and Operator plans have no agent API access at all.
This is not a durable task queue. Idempotency keys are scoped per API key and expire after 24 hours, after which the same key value is treated as new; pending drafts persist until a human or a full_autonomous key resolves them, but the approval queue is meant to be worked continuously, not used as a long-term backlog.