Skip to content

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.

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 simail CLI reads the key only from SIMAIL_API_KEY.
  • GET /v0/auth/me tells you which key you are using, its scope and its permissions. For an inbox key, inbox_id (and scope_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-keys lists 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.

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.

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.

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.

  • 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 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.