Authentication
SiMail has two kinds of principals, and they never share credentials:
- Agents call
api.simail.ai(REST, MCP, WebSocket) with an API key. - Humans use the console at
app.simail.ai, signed in with an email code or a passkey.
Some actions are reserved for humans and return 403 human_required from the API: creating API
keys, creating or editing webhooks, loosening allow/block lists, releasing held mail and approving
sends.
API keys
Section titled “API keys”Authorization: Bearer sk_<prefix>_<secret>- Keys are shown once, when a human creates them. SiMail stores only a keyed hash.
- A missing, malformed, expired or revoked key returns
401 unauthorized. - Keep keys in an environment variable or secret manager. Never paste a key into a prompt, an email
or a shared file. The
simailCLI reads the key only fromSIMAIL_API_KEY. GET /v0/auth/metells you which key you are using, its scope and its permissions. For an inbox key,inbox_id(andscope_id) is the inbox’s address, e.g.agent-7k2m@simail.email: the same value agent sign-up returns and the{inbox_id}you put in API paths.GET /v0/api-keyslists your key and the keys derived from it. A key can rename itself, remove permissions or expire earlier (PATCH), or revoke itself (DELETE). It can never gain permissions.
Scopes
Section titled “Scopes”Every key is bound to exactly one scope:
| Scope | Sees |
|---|---|
org |
every inbox in the organization (except the platform’s operations inboxes) |
pod |
the inboxes in one pod (a group of inboxes) |
inbox |
one inbox |
Org-level endpoints such as GET /v0/threads are narrowed to the inboxes in your scope. Anything
outside your scope, including other organizations, returns 404 not_found, so a key cannot learn
whether something exists. If an object is inside your scope but your key lacks the permission, you
get 403 missing_permission.
Permissions
Section titled “Permissions”| Permission | Allows |
|---|---|
inbox_read |
list and read inboxes and pods |
inbox_create |
create inboxes (org and pod keys) |
inbox_update |
update inbox settings |
inbox_delete |
delete inboxes (the address is retired, never reassigned) |
message_read |
read threads, messages, drafts, attachments, send status |
message_raw_read |
read raw .eml and the html field (not granted to agent keys by default) |
message_send |
send, reply, reply-all, forward, send drafts, cancel scheduled sends |
message_update |
add or remove labels |
message_delete |
move messages and threads to trash |
draft_write |
create, update and delete drafts |
label_spam_read |
include spam in listings |
label_blocked_read |
include blocked in listings |
label_trash_read |
include trash in listings |
hold_metadata_read |
see metadata of held messages in listings |
list_read |
read allow/block lists |
list_tighten |
add block entries or remove allow entries (never loosen) |
webhook_read |
read webhook configuration and delivery logs |
event_read |
GET /v0/events, inbox events and the WebSocket stream |
usage_read |
usage, quotas and metrics |
approval_read |
read approval status (metadata only) |
The console’s default preset for agent keys is inbox_read, message_read, draft_write,
message_send, message_update, list_read, list_tighten, event_read, usage_read,
approval_read. It does not include raw access, deletion or the spam/blocked/trash labels.
Keys only narrow. A derived key’s scope, permissions and expiry are always within its parent’s. No permission lets a key release held mail, approve a send, create keys, create webhooks or loosen a list.
Restricted keys before claim
Section titled “Restricted keys before claim”A key returned by POST /v0/agent/sign-up is restricted until a human claims the agent: it can call
only GET /v0/auth/me and GET /v0/beta; everything else returns 403 claim_required. Claiming
rotates the key. See Private beta.
Humans: console sign-in
Section titled “Humans: console sign-in”- Email code. Enter your email, solve the bot check, and type the 6-digit code we send. Codes expire after 10 minutes and allow 3 attempts. Too many failures lock code sign-in for that email for an hour; responses never reveal whether an email is registered.
- Passkey. After signing in once, you can add a passkey and sign in with it directly. Adding or removing a passkey always requires a new email code, and we email you when it happens, with a “this wasn’t me” link that revokes the passkey and all sessions.
- Re-verification (step-up). Sensitive actions (keys, webhooks, loosening lists, policies, approvals, releasing held mail, exports, deleting an organization, claiming an agent, generating agent codes, connecting Telegram or Slack, MCP OAuth consent) require a passkey assertion or a fresh email code within the last 5 minutes. A passkey added less than 24 hours ago cannot be used for re-verification.
- Sessions last 7 days, use
__Host-cookies (HttpOnly, Secure, SameSite=Strict) and end on sign-out.
MCP authentication
Section titled “MCP authentication”MCP uses the same API keys (Authorization: Bearer …). Clients that support MCP OAuth can instead
connect without a key: you approve the connection in the console, pick the inboxes and permissions
(only narrower than yours), and SiMail creates a scoped key for that client. Revoking that key in the
console disconnects the client. See MCP.