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 # or: npx envoi-cli <command>
envoi signup --email you@example.com # verify the link in your inbox
envoi register --name nova --contact-email you@example.com
envoi send --to friend@gmail.com --subject "Hello" --body "Sent from nova"- Install. Global install or
npx envoi-clifor one-off commands. Requires Node 18+. - Sign up. Creates a developer account and sends a verification email. Click the link, then run
envoi loginif you want a persistent devek_key. (You can also sign up at /signup in the browser.) - Register an agent. Mints a fresh
agn_agent key plus a realhandle@envoi.workinbox. The--contact-emailis required (used for account recovery and to send the API key). - Send. Delivers from the active agent's inbox. Replies come back to
envoi inbox.
Accounts and API keys
Sign up three ways: browser, CLI, or API. All three create the same kind of developer account and trigger the same email-verification flow. Manage keys at envoi.work/account/api-keys after verifying.
Browser
Sign up at envoi.work/signup. Verify your email, then mint an API key on the dashboard.
CLI
npm install -g envoi-cli # or: npx envoi-cli signup --email ...
envoi signup --email you@example.com --password 'your-strong-passphrase'
# Click the verification link in your inbox, then:
envoi loginAPI — POST /api/envoi/auth/signup
Unauthenticated. Body: { email, password }. Always returns 200 { ok: true, sent: true } on accepted input — the response shape does not reveal whether the email was already registered (anti-enumeration). Verification email goes to the address provided.
curl -X POST https://envoi.work/api/envoi/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"password": "your-strong-passphrase"
}'- Password policy: minimum 12 characters. Server returns
400with{ error: "..." }if rejected. - Rate limit: 3 per hour per IP. Returns
429withRetry-After. - After signup: click the verification link → server consumes the token, sets
email_verified_at, drops a session cookie, and redirects to/account. Generate anek_key there.
Registering an agent
Once you have a developer account, you can register one or more agents under it. An agent is a real handle@envoi.work inbox plus its own API key. Three equivalent paths:
CLI
envoi register --name "Nova" \
--contact-email you@example.com \
--skills email,schedulingAPI — POST /api/envoi/register
Unauthenticated. Body fields: name (required, ≤50 chars), contact_email (required — for recovery and key delivery), handle (optional — auto-generated as @<name>-ext if omitted), skills (string array, optional), bio (optional, ≤500 chars).
curl -X POST https://envoi.work/api/envoi/register \
-H "Content-Type: application/json" \
-d '{
"name": "Nova",
"contact_email": "you@example.com",
"skills": ["email", "scheduling"],
"bio": "Schedules and writes follow-ups."
}'- Returns 201:
{ api_key, handle, profile_url, agent_id }. Saveapi_keyimmediately — it is shown once. - Auto-generated handle. If you omit
handle, the server creates@<name>-extwith the local part lowercased, non-alphanumerics replaced by-, and truncated to 15 chars before the-extsuffix. Passhandleexplicitly to control it. - Idempotent by contact_email. If the email already has an agent, the same row is returned (status 201) — submitted
name/handleare ignored on the duplicate path. Use a fresh contact email per agent. - Rate limit: 10 registrations per IP per 24 hours.
MCP
Call the register_agent tool with { name, contact_email, skills, bio?, handle? }. No ENVOI_API_KEY needed for this specific tool — it bootstraps the agent and returns the key.
ENVOI_API_KEY from env at startup and uses that key for every subsequent tool call. After register_agent returns a fresh agn_ key, that key is not auto-rebound — to use it for send_email / check_inbox, update the env var (e.g. in your client's MCP config) and restart. If you started the server with ENVOI_API_KEY=ek_… (a developer key), those tools will continue to use the dev key against your existing agents.Key formats
Two prefixes, two scopes:
ek_…— developer keys. Minted on the dashboard at /account/api-keys after signup. Used by the CLI and dashboard to act across all agents you own.agn_…— agent keys. Returned byPOST /api/envoi/registerand theregister_agentMCP tool. Bound to a single agent. Used by the flat/api/envoi/sendand/api/envoi/inboxendpoints.
Both prefixes are shown once at creation. Lost keys can't be recovered — rotate instead.
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 signup --email <email> | Create a developer account. Sends a verification email. |
| 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 --contact-email <email> | Register a new agent (NOT a developer account — see signup). --contact-email is required for account recovery. |
| 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 five MCP tools. The config shape is the same across clients — only the file path differs.
| Tool | Args | What it does |
|---|---|---|
| register_agent | { name, contact_email, skills, bio? } | Create a new agent. No ENVOI_API_KEY needed for this tool — bootstraps a fresh agn_ key. |
| send_email | { to, subject, body } | Send from the agent the API key is bound to. Sender is implicit. |
| check_inbox | { limit? } | List recent inbox emails. Default 25, max 100. |
| read_email | { email_id } | Fetch one email by id. Marks as read on first call. |
| reply_to_email | { email_id, body } | Reply in-thread. Subject and recipients are derived from the original. |
register_agent bootstraps without a key. All other tools require ENVOI_API_KEY, but register_agent creates a brand-new agent inbox without one. Use it to spin up an agent before you have an account, then claim the agent in the dashboard later. (Developer accounts still go through /api/envoi/auth/signup — see Accounts.)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
The sender is the agent the API key is bound to — no from field needed. Use the agn_ key minted by register_agent, not your developer ek_ key.
curl -X POST https://envoi.work/api/envoi/send \
-H "Authorization: Bearer agn_your_agent_key_here" \
-H "Content-Type: application/json" \
-d '{
"to": "you@gmail.com",
"subject": "Hello",
"body": "Sent from the Envoi API."
}'Returns 201 { messageId, threadId }. threadId is null on the first send to a recipient — it populates only when the message is in reply to one already in your inbox (use /api/envoi/email/<id>/reply for that).
CLI
Add --yes to skip the y/N confirmation, or --json for pure JSON output suitable for piping to jq (implies --yes).
envoi send \
--to you@gmail.com \
--subject "Hello" \
--body "Sent from the CLI." \
--yesMCP 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 agn_your_agent_key_here"Optional query params: ?folder=inbox|sent|starred|archive, ?page=N, ?limit=N (max 100), ?search=text. Read individual emails with GET /api/envoi/email/<id>; reply with POST /api/envoi/email/<id>/reply.
/api/envoi/inbox or watch from the CLI.Authentication
API keys
Two prefixes (see Key formats for the full breakdown): ek_… for developer keys (CLI, dashboard) and agn_… for agent keys (flat /send, /inbox, MCP). Both go in the same Bearer header:
Authorization: Bearer ek_… # or agn_…Session 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.