Every request carries a bearer key:
curl https://api.carlyemail.com/v0/inboxes \
-H "Authorization: Bearer ce_us_..."
Keys are stored as a SHA-256 hash. The plaintext is returned once, at creation, and cannot be recovered — rotate rather than retrieve.
Scopes
A key resolves to one of three scopes, and the scope is fixed at creation.
organization scope
Reaches every inbox and pod in the organization. What sign-up returns.
pod scope
Reaches one pod. Useful when a pod is one of your customers — see pods.
inbox scope
Reaches exactly one mailbox.
Inbox-scoped keys are the interesting case: they are what you hand to an
individual agent, so that a prompt injection can at worst misuse one mailbox
rather than the whole organization. A key scoped to an inbox that asks for a
sibling inbox gets inbox_out_of_scope, not an empty list.
Permissions
A key may also carry a permission map — message_send, inbox_create,
api_key_create and so on.
Permissions are a whitelist, never a merge. A key with no permission object has everything its scope allows. The moment a key carries any explicit permission, that object is the complete grant and anything absent is denied. There is no default map that fills in the gaps, because falling back to one is how a read-only agent key silently gains the ability to send.
Scope always wins over permissions. An inbox-scoped key cannot hold
organization-level capabilities like inbox_create even if its permission map
sets them true — otherwise an agent handed one mailbox could mint itself another.
Creating narrower keys
curl -X POST https://api.carlyemail.com/v0/api-keys \
-H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
-H 'content-type: application/json' \
-d '{
"name": "support agent",
"inbox_id": "support@carlyemail.com",
"permissions": { "message_read": true, "message_send": true }
}'
A key can only delegate permissions it holds itself, so a restricted key cannot mint a broader one.
Revoking
curl -X DELETE https://api.carlyemail.com/v0/api-keys/$KEY_ID \
-H "Authorization: Bearer $CARLYEMAIL_API_KEY"
A revoked key stops working immediately but stays in the record as the audit trail of what it did. It drops out of listings.
OAuth for MCP clients
Chat clients that cannot hold a static key authenticate over OAuth 2.1 instead. That path issues a real API key behind the scenes, so an MCP caller runs through exactly the same scope and permission checks as everybody else rather than a second model alongside it. See MCP.