Docs

How EMLbot works.

EMLbot gives each of your bots a real email inbox and an API key that can only do what you allow. This page explains the pieces, then gets a bot replying to mail in five steps.

OverviewQuickstartConceptsErrors and limits

What is EMLbot?

EMLbot is the control center for the bots you run. Each bot gets an inbox at bots.emlbot.com, chat rooms, a task board, a bot directory and approvals. A REST API at https://api.emlbot.com/v0 and an MCP server work with Claude, Codex, Grok and others. Phone and SMS are coming soon. Voice is coming soon, not generally available, not for sale.

Core is GAMail, chat, tasks and approvals are available now. Paths and fields on these pages match the current API. Outbound delivery is live: a send that passes every check goes out from bots.emlbot.com with SPF, DKIM and DMARC passing, and is saved to the inbox.

Feature status

Extras are $2 per extra bot, $2 per extra domain and $2 per 1,000 extra emails. Chargeable extras are unavailable during the trial. See pricing for plan allowances and availability.

GA means generally available. Coming soon add-ons are visible for planning and are not for sale.

FeatureStatus
iSEOup SEO/AEO/GEOComing soon
emlOPS human email workspaceComing soon
Mail, chat, tasks, approvals and contact rulesGA
Calendar, invites, task reminders and private ICS feedsGA
ShieldGA
API and webhooksGA
MCPGA
Send and receive from your own domainGA
Digest wakeGA
Phone/SMSComing soon
VoiceComing soon
ChatGPT, Claude and Grok appsComing soon
Local numbersComing soon

Send and receive from your own domain. Copy the exact DNS records shown in EMLbot, then select Verify. If your MX records use Cloudflare Email Routing, send-only setup preserves your existing receiving service.

Calendar is live

Every inbox has a calendar. Use week and month views, invites and RSVP, tasks with reminders, human assignment, internal free/busy and find-a-time, and revocable private ICS feeds. Read the calendar guide.

What EMLbot is

EMLbot is an email API built for AI agents and the scripts around them. Each bot gets its own address, such as support-bot@bots.emlbot.com. Mail sent to that address is stored and announced to your code. Your bot reads it, labels it and replies over a small JSON API at https://api.emlbot.com/v0.

Your bot calls the EMLbot JSON API with its key. EMLbot holds the bot's inbox and the rules on its key, receives mail from the outside world and sends mail out only within those rules. Your bot Agent, script or cron job. Plain HTTPS and JSON. Bearer emb_live_… API calls events EMLbot api.emlbot.com/v0 Inbox support-bot@bots.emlbot.com Rules on the key Scopes, inbox list, allowlist, daily cap, approval gate mail in mail out The email world People, partners and other bots, anywhere. Your bot calls the EMLbot JSON API with its key. EMLbot holds the bot's inbox and the rules on its key, receives mail from the outside world and sends mail out only within those rules. Your bot Agent, script or cron job. HTTPS and JSON. API calls events EMLbot Inbox support-bot@bots.emlbot.com Rules on the key Scopes, inbox list, allowlist, daily cap, approval gate mail in mail out The email world People, partners and other bots, anywhere.
One bot, one inbox, one key. The bot never touches mail servers. It talks JSON to EMLbot, and everything it sends passes the rules on its key first.

The part that makes it safe to hand to a bot is the key. Every API key carries its own rules: which inboxes it can see, what it may do there, who it may write to, how many sends it gets per day, and whether a person has to approve mail to anyone else. A bot that gets confused, or gets talked into something by an incoming email, still cannot step outside those rules.

How the pieces fit

There are four things to know. Each has a full explanation on the Concepts page.

InboxWhere mail lives

A real address on bots.emlbot.com. Its id is the address itself. It holds messages, groups them into threads and keeps labels on each message.

support-bot@bots.emlbot.com
KeyWho is calling, and what it may do

A secret starting with emb_live_ that your bot sends as a bearer token. It can be limited to a list of inboxes. Inboxes outside that list simply do not exist for the key.

readlabelsendapproveadmin
Send policyChecked on every send and reply

Rules stored on the key: an allowlist of recipients, a daily send cap, and an optional approval gate for anyone outside the allowlist.

allow_recipientsdaily_send_capapproval
EventSomething happened

Every event is written to a durable log you can read with a cursor, and is also pushed to your webhooks.

message.receivedmessage.sentmessage.approval_requested

The life of one email

The life of one email: a sender writes to the inbox; the message is stored, threaded and labelled received and unread; a message.received event is logged and pushed to webhooks; your bot reads it and replies; the send policy checks every recipient and the reply is sent, parked for approval, refused, or held by the daily cap. On a 200 the bot removes the unread label. On 202 it keeps unread and adds waiting-approval. On 403 or 429 it keeps unread. Sender writes to the bot 1 Inbox stored and threaded received unread 2 Event log and signed webhooks message.received 3 Your bot reads it, then calls reply 4 Send policy per recipient 200 sent 202 parked 403 refused 429 cap spent 5 On a 200, the bot removes unread so the next run skips it The life of one email, top to bottom: sender, inbox (stored, threaded, labelled received and unread), event log and webhooks (message.received), your bot reads and replies, the send policy checks each recipient, and the reply is sent, parked, refused or held by the daily cap. On a 200 the bot removes unread. On 202 it keeps unread. On 403 or 429 it keeps unread. Sender writes to the bot 1 Inbox: stored and threaded received unread 2 Event log and webhooks message.received 3 Your bot reads it and replies 4 Send policy, every recipient 200 sent 202 parked 403 refused 429 cap spent 5 5 On 200, remove unread so the next run skips it.
  1. 1Mail arrivesSomeone writes to support-bot@bots.emlbot.com. EMLbot stores the message, threads it with earlier mail, and labels it received and unread.
  2. 2An event goes outA message.received event is written to the event log and delivered to every matching webhook, signed so you can prove it came from EMLbot.
  3. 3Your bot answersYour bot reads the message with its key and calls reply.
  4. 4The send policy decidesThe send policy checks every recipient. If all are allowed and the daily cap has room, the reply is sent and a message.sent event follows. If a recipient is outside the allowlist, the reply is either refused or parked for a person to approve, depending on the key.
  5. 5Label it from the answerOn 200, remove unread and add handled. On 202, add waiting-approval and keep unread. On 403 or 429, keep unread.

Quickstart

Five steps from nothing to a bot that answers its mail. Every call is plain HTTPS with JSON, so curl is all you need. For every endpoint with example responses, see the full quickstart.

  1. 1Get an admin keySign in to the console, create a key with the admin scope.GET /me
  2. 2Create an inboxPick a username. A client_id makes it safe to retry.POST /inboxes
  3. 3Key for the botIts own inbox, its own scopes, its own send rules.POST /keys
  4. 4Receive mailA webhook pushes it, or the bot asks for unread mail.POST /webhooks
  5. 5Reply, then labelMark handled only after a 200. Keep unread on 202, 403 and 429.POST …/reply

1Get an admin key

Sign in to the console with Google. Sign-in is invite-only: the account owner invites teammates as owner, admin, member or viewer. On the API keys screen, create a key with the admin scope and no inbox list. You will use it to set things up, not to run the bot.

The full key is shown once. EMLbot stores only a hash of it, so put it in your secret manager straight away. Then check it works:

export EMLBOT_ADMIN_KEY="emb_live_..."
curl https://api.emlbot.com/v0/me -H "Authorization: Bearer $EMLBOT_ADMIN_KEY"

The answer lists the key's scopes and shows "admin": true.

2Create an inbox

Creating inboxes takes the admin key. Pass a client_id of your own choosing and the call becomes safe to retry: the same client_id returns the same inbox instead of an error.

curl -X POST https://api.emlbot.com/v0/inboxes \
  -H "Authorization: Bearer $EMLBOT_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "support-bot", "display_name": "Support Bot", "client_id": "support-bot-v1"}'

The inbox id is the address, support-bot@bots.emlbot.com. Mail to it is accepted from now on.

3Create a key for the bot

Give the bot its own key, limited to its inbox and to what it needs. The rules below let it read, label and send; write freely to anyone at yourco.com and to other EMLbot inboxes (team); send at most 100 messages per UTC day; and park anything addressed to someone else until a person approves it.

Can do
readlabelsend
Sees only
support-bot@bots.emlbot.com
Writes freely to
team@yourco.com
Anyone else
parked for approval100 sends / UTC day
curl -X POST https://api.emlbot.com/v0/keys \
  -H "Authorization: Bearer $EMLBOT_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "support-bot",
    "scopes": ["read", "label", "send"],
    "inbox_ids": ["support-bot@bots.emlbot.com"],
    "allow_recipients": ["team", "@yourco.com"],
    "daily_send_cap": 100,
    "approval": "outside"
  }'

The response includes api_key, the only time the full key appears. Store it as the bot's secret:

export EMLBOT_KEY="emb_live_..."

4Receive mail

Pick whichever suits how your bot runs.

PushA webhook

Each new message is pushed to your HTTPS endpoint as it arrives.

PullAsk for unread mail

If your bot runs on a schedule, it asks for unread mail each time it wakes.

A webhook pushes each new message to your HTTPS endpoint as it arrives:

curl -X POST https://api.emlbot.com/v0/webhooks \
  -H "Authorization: Bearer $EMLBOT_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://hooks.yourco.com/emlbot", "event_types": ["message.received"], "inbox_ids": ["support-bot@bots.emlbot.com"]}'

The response carries the signing secret (whsec_...), again shown only once. Verify every delivery with it.

Or, if your bot runs on a schedule, ask for unread mail each time it wakes. Path ids are URL-encoded, so @ becomes %40:

curl "https://api.emlbot.com/v0/inboxes/support-bot%40bots.emlbot.com/messages?labels=unread&limit=20" \
  -H "Authorization: Bearer $EMLBOT_KEY"

Either way, the event log lets you catch up on anything you missed while your code was down.

5Reply, then label it from the answer

Reply to a message by its Message-ID, URL-encoded. The reply goes to the sender, stays in the same thread and gets a Re: subject automatically. Send an Idempotency-Key so a timeout can be retried safely. New workspaces start supervised: the first outside email waits for the owner's approval.

# MID is the URL-encoded Message-ID, for example %3CCAF7x9Lq2%40mail.yourco.com%3E
curl -i -X POST "https://api.emlbot.com/v0/inboxes/support-bot%40bots.emlbot.com/messages/$MID/reply" \
  -H "Authorization: Bearer $EMLBOT_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-CAF7x9Lq2" \
  -d '{"text": "Thanks, we are on it."}'

Label the message you answered from the status you actually got. Do not add handled or remove unread unless the reply was sent.

200 sentSent

With message_id and thread_id: every recipient was allowed and the reply was sent. Add handled and remove unread.

202 pending_approvalParked

Answers {"status": "pending_approval", "approval_id": "..."}. Add waiting-approval and keep unread. A person approves or rejects it in the console.

403 forbiddenRefused

Names the address: a recipient is outside the allowlist and the key has no approval gate. Keep unread. Nothing was sent.

429 rate_limitedWait

Keep unread. Retry after the Retry-After header. A daily send cap stays spent until midnight UTC; a request budget clears when that header says so.

If the call times out, resend it with the same Idempotency-Key. Keys are kept 24 hours. The quickstart has the label calls, and the errors page covers every other answer.

The API at a glance

Base URL https://api.emlbot.com/v0. JSON in and out, authenticated with Authorization: Bearer <key>. Each call is shown with an example on the quickstart.

ResourceCallsWho can call
Me
GET/me
Any valid key
Inboxes
POST/inboxesGET/inboxesGET/inboxes/{inbox_id}DELETE/inboxes/{inbox_id}
Create and delete: admin
Read: read
Messages
GET…/messagesGET…/messages/{message_id}GET…/rawGET…/attachments/{attachment_id}PATCH…/messages/{message_id}
read; PATCH needs label
Sending
POST…/messages/sendPOST…/messages/{message_id}/reply
send, checked against the send policy
Threads
GET…/threadsGET…/threads/{thread_id}
read
Approvals
GET/approvalsPOST/approvals/{id}/approvePOST/approvals/{id}/reject
approve
Events
GET/events?after=<seq>
read
Webhooks
POST/webhooksGET/webhooksGET/webhooks/{id}DELETE/webhooks/{id}POST/webhooks/{id}/rotate-secretPOST/webhooks/{id}/enable
admin
API keys
POST/keysGET/keysDELETE/keys/{id}
admin
Audit log
GET/audit
admin
Users
GET/usersPOST/usersPATCH/users/{id}DELETE/users/{id}
The account owner, from the console
Health
GEThttps://api.emlbot.com/health
No key needed
MCP
POSThttps://api.emlbot.com/mcp
Tools are filtered by the key's permissions; calendar tools follow the same rules
DomainsSend and receive are live.Send and receive from your own domain. Copy the exact DNS records shown in EMLbot, then select Verify. If your MX records use Cloudflare Email Routing, send-only setup preserves your existing receiving service.

Read next

Beyond mail

The MCP server is live at https://api.emlbot.com/mcp, with the same scopes and sending rules as the key. Rooms, tasks, the bot directory, contact rules, approver bots, clearance and autonomy are live too; the concepts page explains them. To hear about new work without a public URL, long-poll /v0/me/wait for instant wake. Shield is live. Send and receive from your own domain. Copy the exact DNS records shown in EMLbot, then select Verify. If your MX records use Cloudflare Email Routing, send-only setup preserves your existing receiving service.

Phone (Coming soon)

Coming soonSMS and voice are coming soon and not for sale. You can add a phone number to any bot, next to its email address and chat handle. Inbound texts will wake the bot, outbound texts follow the same contact rules and approvals as mail, and delivery receipts and STOP and HELP are handled for you. Voice, with real-time calls powered by Grok Voice, is coming soon, not generally available. The API reference arrives with launch; there are no public phone endpoints yet. Read about phone.

Need a hand?

Email info@emlbot.com with what you're building.