Docs
Authentication
API keys, headers, scoped permissions and the audit log.
Every request under /v0 and /mcp needs an API key. A key belongs to one organization and sees only that organization's data.
Sending the key
Use the Authorization header with the Bearer scheme. X-Api-Key is accepted as an alternative, which is convenient for MCP clients that only let you set a plain header.
Authorization: Bearer mai_live_your_key
# or
X-Api-Key: mai_live_your_keyKey format and storage
- Keys start with
mai_live_followed by 32 random characters. The fixed prefix makes leaked keys easy to detect with secret scanning. - The plaintext key is shown once, when it is created. The server stores only a SHA-256 hash, so a lost key cannot be recovered: create a new one.
- Treat a key like a password: keep it in environment variables or a secret manager, never in client side code or a public repository.
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_authorization | No key in Authorization or X-Api-Key. |
| 401 | unknown_api_key | The key is malformed, revoked or does not exist. |
| 401 | api_key_expired | The key passed its expires_at. Create a new one. |
| 403 | missing_permission | The key is scoped and lacks the permission the endpoint needs. |
| 403 | permission_escalation | A key tried to create or revoke a key with permissions it does not hold. |
| 429 | rate_limit_exceeded | Too many requests, or too many failed authentications from the same address. Wait for Retry-After. |
Scoped keys
A key either has full access or a list of permissions. Give each agent a key with only what it needs: a triage agent that reads mail and sets labels does not need message_send. Keys can also carry an expires_at, and revocation takes effect immediately.
| Permission | Allows |
|---|---|
inbox_read | List and get inboxes. |
inbox_write | Create, rename and delete inboxes. |
message_read | List and read threads, messages, raw messages and attachments. |
message_send | Send new messages and replies. |
message_update | Add and remove labels on messages. |
domain_read | List and get domains. |
domain_write | Add, verify and delete domains. |
webhook_read | List webhooks. |
webhook_write | Create and delete webhooks. |
api_key_read | List API keys. |
api_key_write | Create and revoke API keys, within the caller's own permissions. |
audit_read | Read the audit log. |
message_review | Read messages held for security review with include_review=true. For a human review console, not for agents. |
message_release | Release a message held for review, which then fires message.received. Keep it away from the agent that reads the mail. A full access key does not hold it: only keys that list it do, and the operator issues the first one. |
billing_read | Read the plan and the subscription state with GET /v0/billing. |
billing_write | Start a Stripe Checkout for a paid plan and open the customer portal. |
data_export | |
data_delete | |
ticket_read | |
ticket_write |
- A key can create keys only with permissions it holds itself, and a scoped key cannot create a full access key. Violations return
403 permission_escalation. - Omit
permissionswhen creating a key to copy the calling key's permissions. - The same permissions apply to MCP tools, so a scoped key limits what a coding agent can do too.
curl -s -X POST https://api.162-55-213-24.sslip.io/v0/api-keys \
-H "Authorization: Bearer $MAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "triage-agent", "permissions": ["inbox_read", "message_read", "message_update"], "expires_at": "2027-01-31T00:00:00Z"}'Audit log
Security relevant actions of your organization, including actions taken by agents through MCP, are recorded. Read them with GET /v0/audit-logs (requires audit_read).
Endpoints
GET/v0/auth/me
Caller summary (kept for older clients; same body as GET /v0/me)
Responses
200OK: Me401Error envelope: Error
POST/v0/api-keys
Create a key with at most the caller's permissions (key shown once)
Body
| Name | Type | Notes |
|---|---|---|
name | string | max length 100 |
permissions | Permission[] | omit to copy the caller's permissions |
expires_at | string (date-time) |
Responses
201Created403Error envelope: Error
GET/v0/api-keys
List active keys (requires api_key_read)
Responses
200OK
DELETE/v0/api-keys/{key}
Revoke a key immediately (requires api_key_write, no escalation)
Parameters
| Name | Type | Notes |
|---|---|---|
key | string (uuid) | in path, required |
Responses
204Revoked
GET/v0/audit-logs
Security relevant actions of the organization (requires audit_read)
Parameters
| Name | Type | Notes |
|---|---|---|
limit | integer | in query, min 1, max 200, default 50 |
Responses
200OK