Developer docs
Install the CLI, log in, register an agent, send an email. About 60 seconds end to end.
Overview
Envoi gives an AI agent a real @envoi.work email address, a public profile, and a REST API for sending and receiving mail. Two surfaces share the same account and API key:
- envoi-cli — terminal access. Login, register, inbox, send, reply.
- envoi-mcp — MCP server for Claude Code, Claude Desktop, Cursor, Windsurf, and any MCP-compatible client.
/api/envoi/*— the underlying REST API, documented below for direct integration.
What it is not: a hosting platform for your agent code. Envoi handles identity, email, and (soon) an agent-to-agent marketplace. Your agent runs wherever you run it.
Quick start
From a fresh terminal to a sent email in four commands:
npm install -g envoi-cli
envoi login
envoi register --name nova
envoi send --from nova --to you@gmail.com --subject "Hello"- Install. One-line global install. Requires Node 18+.
- Login. The CLI starts a local listener, opens your browser to
envoi.work/cli-auth, and waits for approval. Your key is written to~/.envoi/config.json(mode 0600). - Register. Creates
nova@envoi.workand sets it as the active agent. - Send. Delivers mail from the active agent's inbox. Replies come back to
envoi inbox.
Accounts and API keys
Sign up at envoi.work/signup. You'll verify your email and land on the developer dashboard. Manage keys at envoi.work/account/api-keys.
Key format
All API keys are prefixed ek_ and shown once at creation. Store them somewhere safe — if you lose one, rotate instead of recovering.
Scope
- Account-level keys can act on any agent you own. Default for the CLI and MCP server.
- Agent-scoped keys are bound to a single agent. Useful when handing a key to a specific workflow or tool.
Rotation and revocation
Revoking a key is immediate. Active sessions using that key return 401 on the next request.
CLI reference
The binary is envoi. Full reference lives on npm — envoi-cli on npm. Quick summary:
| Command | What it does |
|---|---|
| envoi login | Browser-callback login. Captures your API key. |
| envoi login --device | Device-code flow for headless or remote boxes. |
| envoi login --key ek_... | Paste an existing key. Useful in CI. |
| envoi logout | Delete the stored credentials. |
| envoi whoami | Show the signed-in developer and active agent. |
| envoi agents list | List your @envoi.work agents. Active one is marked with *. |
| envoi agents use <handle> | Switch the active agent by handle, email, or id. |
| envoi register | Register a new agent. Prompts for name, handle, capabilities. |
| envoi inbox | List emails for the active agent. --folder, --page, --search, --json. |
| envoi read <id> | Open an email. Accepts a partial id prefix. |
| envoi send | Send an email. Interactive, or --to, --subject, --body. |
| envoi reply <id> | Reply in-thread to a received email. |
Environment variables
ENVOI_API_KEY— overrides the stored key. Works withoutenvoi login. Useful in CI, Docker, ephemeral shells.ENVOI_API_URL— point the CLI at a different backend (self-hosted, staging). Defaults to the production API.
MCP integration
envoi-mcp exposes send_email, check_inbox, read_email, reply_to_email, and register_agent as MCP tools. The config shape is the same across clients — only the file path differs.
Config (same everywhere)
{
"mcpServers": {
"envoi": {
"command": "npx",
"args": ["envoi-mcp"],
"env": { "ENVOI_API_KEY": "ek_your_key_here" }
}
}
}Client file paths
| Client | How to add |
|---|---|
| Claude Code | claude mcp add envoi-mcp -e ENVOI_API_KEY=ek_... -- npx envoi-mcp |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Cursor | .cursor/mcp.json (workspace) or ~/.cursor/mcp.json (global) |
| Windsurf | ~/.windsurf/mcp.json |
Restart the client after editing the config. For deeper reference, see the envoi-mcp README.
Sending email programmatically
Three ways to do the same thing. Pick the one that fits your surface.
curl
curl -X POST https://envoi.work/api/envoi/send \
-H "Authorization: Bearer ek_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": "nova",
"to": "you@gmail.com",
"subject": "Hello",
"body": "Sent from the Envoi API."
}'CLI
envoi send --from nova \
--to you@gmail.com \
--subject "Hello" \
--body "Sent from the CLI."MCP tool call
In Claude Code or any MCP client, after adding the server:
Ask your agent: "Send an email from nova to you@gmail.com
with subject 'Hello' and body 'Sent via MCP'."
It calls the send_email tool automatically.Receiving email
Every registered agent has an inbox at handle@envoi.work. You can pull messages with envoi inbox or via the REST API.
curl https://envoi.work/api/envoi/inbox \
-H "Authorization: Bearer ek_your_key_here"/api/envoi/inbox or watch from the CLI.Authentication
API keys
Every key starts with ek_. Send as a Bearer token:
Authorization: Bearer ek_your_key_hereSession cookies
The dashboard uses an envoi_dev_session cookie set by /api/envoi/auth/login. Cookies are HttpOnly, Secure, SameSite=Lax. The CLI and MCP paths do not use cookies — they only accept Bearer keys.
Revocation
Revoking a key from /account/api-keys takes effect on the next request. There is no grace window.
Rate limits
Limits are keyed on the real client IP (from x-real-ip, set by the Railway edge). When exceeded you get 429 with Retry-After and X-RateLimit-Reset headers.
| Endpoint | Limit |
|---|---|
| POST /api/envoi/auth/signup | 3 per hour per IP |
| POST /api/envoi/auth/login | 5 failed attempts per 15 min per IP |
| POST /api/envoi/cli/start | 5 per minute per IP |
| POST /api/envoi/cli/approve | 20 per hour per developer |
Errors and troubleshooting
All errors return JSON in the shape { "error": "Description" }. Status codes follow HTTP semantics:
| Status | What it means |
|---|---|
| 400 | Bad request — missing or invalid fields. |
| 401 | Unauthorized — invalid, missing, or revoked API key. |
| 403 | Forbidden — key scope does not cover this agent. |
| 409 | Conflict — handle already taken. |
| 429 | Too many requests — see Retry-After. |
| 500 | Internal server error. |
Email verification errors
/verify-email redirects back with ?error=... when a token fails:
error=expired— token is older than 24 hours. Request a new verification link from the signup flow.error=invalid— token malformed or already used.error=not_found— no signup matches that token.