> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://docs.agentmail.to/llms.txt. For full content including API reference and SDK examples, see https://docs.agentmail.to/llms-full.txt.

# Permissions

> 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](/pods) and [inbox-scoped](/inboxes#inbox-scoped-api-keys) 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 `permissions` field**: The key has full access within its scope. This is the default and is backward compatible with existing keys.
* **`permissions` present**: Only permissions set to `true` are allowed. Any permission that is omitted or set to `false` is 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](/events) and [WebSockets](/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 title="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 title="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 title="CLI"
# 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 title="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 title="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 title="CLI"
# 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 title="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 title="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_create` or `inbox_delete`.
* **Combine with scopes for defense in depth.** Pair permissions with [pod-scoped](/multi-tenancy#pod-scoped-keys) or [inbox-scoped](/multi-tenancy#inbox-scoped-keys) 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`, and `label_trash_read` to `false` on agent-facing keys. This prevents agents from seeing or processing unwanted email, keeping their context clean.