Emelia Inbox API
The Emelia Inbox API lets your code or an AI agent list, search and read the emails of your mailboxes, label them, prepare drafts and send replies. Same API key as the Emelia API, its own quotas.
The Emelia Inbox API is the programmatic side of the Emelia unified inbox (the Inbox tab of the app). It lets your code or an AI agent list, search and read the emails of the mailboxes connected to Emelia, organise them with labels, AI label rules, saved views and inbox filters, prepare drafts for a human to review, send replies, and forward emails automatically.
Base URL and key
The Inbox API lives on the same host as the Emelia API, under /inbox/api/v1, and takes the same API key: the one you create in Settings, API & Webhooks.
| Emelia API | Emelia Inbox API | |
|---|---|---|
| Base URL | https://api.emelia.io | https://api.emelia.io/inbox/api/v1 |
| Covers | Campaigns, contacts, enrichment, LinkedIn | The emails of the inbox |
| Errors | { "error", "code"? } | { "code", "error", "details"? } |
| Quotas | See Rate limits | See Quotas below |
The full contract is also available as an OpenAPI file: /openapi/inbox.json.
Authentication
Send your API key in the Authorization header on every call, as is (a Bearer prefix is accepted too). The workspace comes from the key: no path carries a workspace id, so a key only reaches the inbox of its own Emelia account.
The key can send emails
As on the rest of the Emelia API, a key has every right: it reads every email of the inbox, and it can send emails, create auto-forward rules and turn on auto-reply. Give one key to each integration, and keep it on your server.
An unknown or disabled key gets 401 INVALID_API_KEY. Disabling a key in Emelia can take up to 5 minutes to apply to the Inbox API. A workspace whose Emelia subscription ended gets 403 WORKSPACE_SUSPENDED.
List and read emails
GET/emails
curl "https://api.emelia.io/inbox/api/v1/emails?folderType=INBOX&limit=50" \
-H "Authorization: $EMELIA_API_KEY"- Pagination: pass
nextCursorback ascursor, and stop whenhasMoreisfalse.nextCursoris onlynullon an empty page, so do not stop on it.limitis 50 by default, 200 at most. - Folders: without
folderType, every folder is listed, spam and trash included. WithfolderType=INBOX, the emails hidden by inbox filters are left out;filtered=onlyreturns them instead. - Body: lists never include the body. Read it, with the whole conversation, with Get the body and thread.
- Search: Search emails by keyword takes 2 to 200 characters. Known issue: a
searchparameter onGET /emailsignoresfiltersandfiltered; do not combine them. - Every read is cancelled after 10 seconds (
408 QUERY_TIMEOUT).
Send a reply
POST/drafts/send
curl -X POST https://api.emelia.io/inbox/api/v1/drafts/send \
-H "Authorization: $EMELIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reply-claire-2026-10-13" \
-d '{
"mailboxId": "cmbxacme0000000000000001",
"to": [{ "email": "claire.fontaine@nexora.io", "name": "Claire Fontaine" }],
"subject": "Re: Quick question about Nexora",
"inReplyTo": "<CAF7x2y3z@mail.nexora.io>",
"references": ["<CAF7x2y3z@mail.nexora.io>"],
"body": { "text": "Hi Claire,\n\nTuesday 10am works for me.", "html": "<p>Hi Claire,</p><p>Tuesday 10am works for me.</p>" }
}'To reply in a thread: send from the mailboxId of the original email, set inReplyTo to its messageId, references to its references followed by its messageId, and prefix the subject with Re: . The signature of the mailbox is added by default. Attachments cannot be sent through the API.
Sending is irreversible
Always send an Idempotency-Key. Known issue: if the message left but a later step failed, the API answers 500 INTERNAL_ERROR and a retry sends the message again. After a 500, look in the SENT folder before retrying. For a human in the loop, create a draft instead: it waits in the inbox for review.
Organise the inbox
| Need | Routes |
|---|---|
| Mark read, star, trash | Mark read or starred, Move an email to the trash, Bulk update emails |
| Labels you apply yourself | Labels, then Apply a label to an email |
| Labels applied by AI from a plain language condition, with optional auto-draft and auto-reply | AI label rules |
Saved filters, read with GET /emails?viewId= | Views |
| Hide emails from the main inbox without moving them | Inbox filters |
| Forward matching emails automatically | Auto-forward rules |
AI work triggered by the inbox (classification of each incoming email while an AI label rule exists, auto-drafts) is paid with Emelia credits.
Quotas
Counted per Emelia account (every key shares them), over a sliding minute and a calendar month in UTC. They are separate from the rate limits of the Emelia API:
| Limit | Per Emelia account |
|---|---|
| Requests per minute | 60 |
| Requests per month | 10,000 |
| Active webhooks | 1 |
One request costs 1 unit. POST /emails/bulk costs one unit per id, checked against the per minute limit too: send at most 60 ids per call. Units are counted when the request comes in, even if it then fails validation. Over a limit, the API answers 429 RATE_LIMIT_EXCEEDED with Retry-After, or 429 QUOTA_EXCEEDED for the month. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Quota, X-RateLimit-Quota-Remaining and X-RateLimit-Quota-Reset.
Conventions
- JSON in and out, bodies of 256 KB at most (
413 PAYLOAD_TOO_LARGE). Dates are ISO 8601 in UTC. Ids are opaque strings. - Errors:
{ "code", "error", "details"? }. Branch oncode. A resource of another workspace answers404, never403. An email not found answersNOT_FOUNDon some routes andEMAIL_NOT_FOUNDon others: treat both the same. Idempotency-Key(255 characters at most) is supported on the creatingPOSTroutes. A successful response is kept 24 hours and replayed withIdempotent-Replay: true; the same key with another request answers409 CONFLICT. Keys are scoped to the workspace, not to the API key.- An unknown path answers an HTML 404 page.
Webhooks
Six events can be pushed to your endpoint: email.received, email.updated, email.deleted, draft.sent, label.applied and mailbox.sync_failed. Each delivery is a POST with { "event", "createdAt", "data" }, an X-Maylee-Event header and an X-Maylee-Signature header (t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">, keyed with the webhook secret). Reject deliveries older than 300 seconds and compare signatures in constant time. A delivery succeeds on a 2xx within 10 seconds; failures are retried 4 times over about 8 hours.
Registering a webhook
Webhook subscriptions cannot be created with an API key, and Emelia has no screen for them yet. Contact contact@emelia.io if you need one.
The payload of each event is on its page, under Webhook events in this section.