Appearance
Permissions
Fresh 🌱Learn how to configure fine-grained permissions on API keys to restrict access to specific resources and operations.
What are Permissions?
Permissions let you restrict what an API key can do. By default, an API key has full access within its scope (organization, pod, or inbox). When you provide a permissions object, it acts as a whitelist: only the permissions you explicitly set to true are granted. Everything else is denied.
This gives you fine-grained control over which resources and operations each key can access, on top of the existing scope-based isolation provided by pod-scoped and inbox-scoped keys.
How Permissions Work
Whitelist model
When you create an API key with a permissions object, it switches from "full access" mode to "whitelist" mode:
- No
permissionsfield: The key has full access within its scope. This is the default and is backward compatible with existing keys. permissionspresent: Only permissions set totrueare allowed. Any permission that is omitted or set tofalseis denied.
Existing API keys without a permissions field continue to work exactly as before with full access. The whitelist only activates when you explicitly provide the permissions object.
Intersection with scope
Permissions are intersected with the key's scope. A key scoped to a single inbox cannot gain organization-level capabilities (like inbox_create or domain_create) even if those permissions are set to true. The effective permissions are always the intersection of what the scope allows and what the permissions object grants.
Privilege escalation protection
A restricted API key cannot create a child key with more permissions than itself. When creating a new key, the child's permissions are automatically constrained to the parent's effective permissions. This prevents privilege escalation through key creation.
Permissions Reference
Inboxes
| Permission | Description |
|---|---|
inbox_read | Read inbox details |
inbox_create | Create new inboxes |
inbox_update | Update inbox settings |
inbox_delete | Delete inboxes |
Threads
| Permission | Description |
|---|---|
thread_read | Read threads |
thread_delete | Delete threads |
Messages
| Permission | Description |
|---|---|
message_read | Read messages |
message_send | Send messages |
message_update | Update message labels |
Label Visibility
| Permission | Description |
|---|---|
label_spam_read | Access messages labeled as spam |
label_blocked_read | Access messages labeled as blocked |
label_trash_read | Access messages labeled as trash |
When a label visibility permission is denied, items with that label are automatically excluded from list results and return "not found" on direct access. For example, setting label_spam_read to false means the key will never see spam messages in any listing or lookup.
Label visibility permissions also control whether an API key can subscribe to the corresponding event types on webhooks and WebSockets. An API key without label_spam_read cannot create a webhook or WebSocket subscription for message.received.spam events, and an API key without label_blocked_read cannot subscribe to message.received.blocked events.
Drafts
| Permission | Description |
|---|---|
draft_read | Read drafts |
draft_create | Create drafts |
draft_update | Update drafts |
draft_delete | Delete drafts |
draft_send | Send drafts |
Webhooks
| Permission | Description |
|---|---|
webhook_read | Read webhook configurations |
webhook_create | Create webhooks |
webhook_update | Update webhooks |
webhook_delete | Delete webhooks |
Domains
| Permission | Description |
|---|---|
domain_read | Read domain details |
domain_create | Create domains |
domain_update | Update domains |
domain_delete | Delete domains |
Lists
| Permission | Description |
|---|---|
list_entry_read | Read list entries |
list_entry_create | Create list entries |
list_entry_delete | Delete list entries |
Metrics
| Permission | Description |
|---|---|
metrics_read | Read metrics |
API Keys
| Permission | Description |
|---|---|
api_key_read | Read API keys |
api_key_create | Create API keys |
api_key_delete | Delete API keys |
Pods
| Permission | Description |
|---|---|
pod_read | Read pods |
pod_create | Create pods |
pod_delete | Delete pods |
Code Examples
Read-only key
Create an API key that can read all resources but cannot create, update, or delete anything.
python
from agentmail import AgentMail
client = AgentMail(api_key="YOUR_API_KEY")
key = client.api_keys.create(
name="read-only-agent",
permissions={
"inbox_read": True,
"thread_read": True,
"message_read": True,
"draft_read": True,
"webhook_read": True,
"domain_read": True,
"list_entry_read": True,
"metrics_read": True,
"api_key_read": True,
"pod_read": True,
"label_spam_read": True,
"label_blocked_read": True,
"label_trash_read": True,
}
)
print(key.api_key)typescript
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "YOUR_API_KEY" });
const key = await client.apiKeys.create({
name: "read-only-agent",
permissions: {
inboxRead: true,
threadRead: true,
messageRead: true,
draftRead: true,
webhookRead: true,
domainRead: true,
listEntryRead: true,
metricsRead: true,
apiKeyRead: true,
podRead: true,
labelSpamRead: true,
labelBlockedRead: true,
labelTrashRead: true,
},
});
console.log(key.apiKey);bash
# create a read-only api key
agentmail api-keys create \
--name "read-only-agent" \
--permissions '{
"inbox_read": true,
"thread_read": true,
"message_read": true,
"draft_read": true,
"webhook_read": true,
"domain_read": true,
"list_entry_read": true,
"metrics_read": true,
"api_key_read": true,
"pod_read": true,
"label_spam_read": true,
"label_blocked_read": true,
"label_trash_read": true
}'No-spam key
Create a key with full access but block visibility into spam, blocked, and trash content. This is useful for agent-facing keys where you want to prevent the agent from processing unwanted email.
python
key = client.api_keys.create(
name="clean-inbox-agent",
permissions={
"inbox_read": True,
"inbox_create": True,
"inbox_update": True,
"inbox_delete": True,
"thread_read": True,
"thread_delete": True,
"message_read": True,
"message_send": True,
"message_update": True,
"label_spam_read": False,
"label_blocked_read": False,
"label_trash_read": False,
"draft_read": True,
"draft_create": True,
"draft_update": True,
"draft_delete": True,
"draft_send": True,
"webhook_read": True,
"webhook_create": True,
"webhook_update": True,
"webhook_delete": True,
"domain_read": True,
"domain_create": True,
"domain_update": True,
"domain_delete": True,
"list_entry_read": True,
"list_entry_create": True,
"list_entry_delete": True,
"metrics_read": True,
"api_key_read": True,
"api_key_create": True,
"api_key_delete": True,
"pod_read": True,
"pod_create": True,
"pod_delete": True,
}
)typescript
const key = await client.apiKeys.create({
name: "clean-inbox-agent",
permissions: {
inboxRead: true,
inboxCreate: true,
inboxUpdate: true,
inboxDelete: true,
threadRead: true,
threadDelete: true,
messageRead: true,
messageSend: true,
messageUpdate: true,
labelSpamRead: false,
labelBlockedRead: false,
labelTrashRead: false,
draftRead: true,
draftCreate: true,
draftUpdate: true,
draftDelete: true,
draftSend: true,
webhookRead: true,
webhookCreate: true,
webhookUpdate: true,
webhookDelete: true,
domainRead: true,
domainCreate: true,
domainUpdate: true,
domainDelete: true,
listEntryRead: true,
listEntryCreate: true,
listEntryDelete: true,
metricsRead: true,
apiKeyRead: true,
apiKeyCreate: true,
apiKeyDelete: true,
podRead: true,
podCreate: true,
podDelete: true,
},
});bash
# create a key with full access but no spam/blocked/trash visibility
agentmail api-keys create \
--name "clean-inbox-agent" \
--permissions '{
"inbox_read": true,
"inbox_create": true,
"inbox_update": true,
"inbox_delete": true,
"thread_read": true,
"thread_delete": true,
"message_read": true,
"message_send": true,
"message_update": true,
"label_spam_read": false,
"label_blocked_read": false,
"label_trash_read": false,
"draft_read": true,
"draft_create": true,
"draft_update": true,
"draft_delete": true,
"draft_send": true,
"webhook_read": true,
"webhook_create": true,
"webhook_update": true,
"webhook_delete": true,
"domain_read": true,
"domain_create": true,
"domain_update": true,
"domain_delete": true,
"list_entry_read": true,
"list_entry_create": true,
"list_entry_delete": true,
"metrics_read": true,
"api_key_read": true,
"api_key_create": true,
"api_key_delete": true,
"pod_read": true,
"pod_create": true,
"pod_delete": true
}'Copy for Cursor / Claude
Copy one of the blocks below into Cursor or Claude for complete Permissions API knowledge in one shot.
python
"""
AgentMail Permissions — copy into Cursor/Claude.
Permissions are a whitelist on API keys. No `permissions` field = full access.
When `permissions` is set, only `True` values are granted; omitted or `False` = denied.
Permissions intersect with scope (pod/inbox). A restricted key cannot create a more privileged child key.
Permission fields (all optional<bool>, resource_action format):
Inboxes: inbox_read, inbox_create, inbox_update, inbox_delete
Threads: thread_read, thread_delete
Messages: message_read, message_send, message_update
Label visibility: label_spam_read, label_blocked_read, label_trash_read
Drafts: draft_read, draft_create, draft_update, draft_delete, draft_send
Webhooks: webhook_read, webhook_create, webhook_update, webhook_delete
Domains: domain_read, domain_create, domain_update, domain_delete
Lists: list_entry_read, list_entry_create, list_entry_delete
Metrics: metrics_read
API Keys: api_key_read, api_key_create, api_key_delete
Pods: pod_read, pod_create, pod_delete
API: api_keys.create(name, permissions={...})
"""
from agentmail import AgentMail
client = AgentMail(api_key="YOUR_API_KEY")
# read-only key
key = client.api_keys.create(
name="read-only",
permissions={"inbox_read": True, "message_read": True, "thread_read": True}
)
print(key.api_key)typescript
/**
* AgentMail Permissions — copy into Cursor/Claude.
*
* Permissions are a whitelist on API keys. No `permissions` field = full access.
* When `permissions` is set, only `true` values are granted; omitted or `false` = denied.
* Permissions intersect with scope (pod/inbox). A restricted key cannot create a more privileged child key.
*
* Permission fields (all optional boolean, resourceAction format):
* Inboxes: inboxRead, inboxCreate, inboxUpdate, inboxDelete
* Threads: threadRead, threadDelete
* Messages: messageRead, messageSend, messageUpdate
* Label visibility: labelSpamRead, labelBlockedRead, labelTrashRead
* Drafts: draftRead, draftCreate, draftUpdate, draftDelete, draftSend
* Webhooks: webhookRead, webhookCreate, webhookUpdate, webhookDelete
* Domains: domainRead, domainCreate, domainUpdate, domainDelete
* Lists: listEntryRead, listEntryCreate, listEntryDelete
* Metrics: metricsRead
* API Keys: apiKeyRead, apiKeyCreate, apiKeyDelete
* Pods: podRead, podCreate, podDelete
*
* API: apiKeys.create({ name, permissions: {...} })
*/
import { AgentMailClient } from "agentmail";
const client = new AgentMailClient({ apiKey: "YOUR_API_KEY" });
async function main() {
// read-only key
const key = await client.apiKeys.create({
name: "read-only",
permissions: { inboxRead: true, messageRead: true, threadRead: true },
});
console.log(key.apiKey);
}
main();Best Practices
- Use the principle of least privilege. Only grant the permissions your agent actually needs. A support agent that reads and replies to emails does not need
domain_createorinbox_delete. - Combine with scopes for defense in depth. Pair permissions with pod-scoped or inbox-scoped keys. Scopes limit which resources a key can see, while permissions limit what it can do.
- Filter unwanted content from agents. Set
label_spam_read,label_blocked_read, andlabel_trash_readtofalseon agent-facing keys. This prevents agents from seeing or processing unwanted email, keeping their context clean.