diff --git a/fern/pages/core-concepts/inboxes.mdx b/fern/pages/core-concepts/inboxes.mdx
index fc3a145..b2899ee 100644
--- a/fern/pages/core-concepts/inboxes.mdx
+++ b/fern/pages/core-concepts/inboxes.mdx
@@ -126,6 +126,34 @@ console.log(`Total Inboxes: ${allInboxes.count}`);
domains](/guides/domains/managing-domains) to learn more.
+## Inbox-scoped API keys
+
+You can create API keys that are restricted to a single inbox. An inbox-scoped key can only access that inbox's threads, messages, and drafts. This is useful when you want to give an agent or integration the minimum access it needs.
+
+
+```python
+# Create a key scoped to one inbox
+key = client.inboxes.api_keys.create(
+ new_inbox.inbox_id,
+ name="support-agent-key"
+)
+
+# The full key is only returned once
+print(key.api_key)
+```
+
+```typescript title="TypeScript"
+const key = await client.inboxes.apiKeys.create(newInbox.id, {
+ name: "support-agent-key",
+});
+
+// The full key is only returned once
+console.log(key.apiKey);
+```
+
+
+See the [Multi-Tenancy guide](/multi-tenancy#inbox-scoped-keys) for more on scoped keys.
+
## Copy for Cursor / Claude
Copy one of the blocks below into Cursor or Claude for complete Inboxes API knowledge in one shot.
@@ -143,6 +171,9 @@ Copy one of the blocks below into Cursor or Claude for complete Inboxes API know
- inboxes.list(limit?, page_token?)
- inboxes.update(inbox_id, display_name)
- inboxes.delete(inbox_id)
+ - inboxes.api_keys.create(inbox_id, name) — inbox-scoped key
+ - inboxes.api_keys.list(inbox_id)
+ - inboxes.api_keys.delete(inbox_id, api_key_id)
Errors: SDK raises on 4xx/5xx. Rate limit: 429 with Retry-After.
"""
@@ -174,6 +205,9 @@ Copy one of the blocks below into Cursor or Claude for complete Inboxes API know
* - inboxes.list({ limit?, pageToken? })
* - inboxes.update(inboxId, { displayName })
* - inboxes.delete(inboxId)
+ * - inboxes.apiKeys.create(inboxId, { name }) — inbox-scoped key
+ * - inboxes.apiKeys.list(inboxId)
+ * - inboxes.apiKeys.delete(inboxId, apiKeyId)
*
* Errors: SDK throws on 4xx/5xx. Rate limit: 429 with Retry-After.
*/
diff --git a/fern/pages/core-concepts/pods.mdx b/fern/pages/core-concepts/pods.mdx
index bce31c3..3b07228 100644
--- a/fern/pages/core-concepts/pods.mdx
+++ b/fern/pages/core-concepts/pods.mdx
@@ -194,7 +194,7 @@ async function offboardCustomer(podId: string) {
**What's NOT Isolated to a Pod:**
-- Organization-level API keys (these can access any resources in any pod). Use [scoped API keys](/multi-tenancy#step-3-create-scoped-api-keys) to restrict access to a single pod.
+- Organization-level API keys (these can access any resources in any pod). Use [pod-scoped API keys](/multi-tenancy#pod-scoped-keys) to restrict access to a single pod, or [inbox-scoped API keys](/multi-tenancy#inbox-scoped-keys) to restrict access to a single inbox.
## Common Patterns and Use Cases
@@ -281,9 +281,9 @@ Pod: "Marketing-Agent"
- Yes! You can create scoped API keys that are restricted to a single pod. A
- scoped key can only access resources within its pod. See the
- [Multi-Tenancy guide](/multi-tenancy) for details on provisioning scoped keys.
+ Yes! You can create pod-scoped API keys that are restricted to a single pod, or
+ inbox-scoped API keys that are restricted to a single inbox within a pod. See the
+ [Multi-Tenancy guide](/multi-tenancy#scoped-api-keys) for details on provisioning scoped keys.
## Next Steps
diff --git a/fern/pages/guides/multi-tenancy.mdx b/fern/pages/guides/multi-tenancy.mdx
index c141bde..bd18157 100644
--- a/fern/pages/guides/multi-tenancy.mdx
+++ b/fern/pages/guides/multi-tenancy.mdx
@@ -58,10 +58,14 @@ const domain = await client.pods.domains.create(pod.podId, {
## Scoped API Keys
-By default, API keys are organization-level and can access everything across all pods. Scoped API keys are restricted to a single pod. If a key is scoped to Acme's pod, it can only touch Acme's resources. Nothing else.
+By default, API keys are organization-level and can access everything across all pods. Scoped API keys restrict access to a single pod or a single inbox. If a key is scoped to Acme's pod, it can only touch Acme's resources. Nothing else.
This is useful when you want to hand a key to a tenant's service or agent without exposing your whole org.
+### Pod-scoped keys
+
+Pod-scoped keys can access all resources within a pod (inboxes, threads, drafts, domains).
+
```python
# Create a key that can only access Acme's pod
@@ -84,21 +88,55 @@ console.log(scopedKey.apiKey);
```
+### Inbox-scoped keys
+
+Inbox-scoped keys are even more restrictive: they only grant access to a single inbox and its threads, messages, and drafts. Use these when an agent or integration only needs to operate on one address.
+
+
+```python
+# Create a key that can only access the support inbox
+inbox_key = client.inboxes.api_keys.create(
+ inbox.inbox_id,
+ name="support-inbox-key"
+)
+
+print(inbox_key.api_key)
+```
+
+```typescript
+const inboxKey = await client.inboxes.apiKeys.create(inbox.inboxId, {
+ name: "support-inbox-key",
+});
+
+console.log(inboxKey.apiKey);
+```
+
+
The full API key is only returned **once** at creation. If you lose it, delete it and create a new one.
-You can list and delete scoped keys for any pod:
+You can list and delete scoped keys for any pod or inbox:
```python
+# Pod-scoped keys
keys = client.pods.api_keys.list(pod.pod_id)
client.pods.api_keys.delete(pod.pod_id, scoped_key.api_key_id)
+
+# Inbox-scoped keys
+inbox_keys = client.inboxes.api_keys.list(inbox.inbox_id)
+client.inboxes.api_keys.delete(inbox.inbox_id, inbox_key.api_key_id)
```
```typescript
+// Pod-scoped keys
const keys = await client.pods.apiKeys.list(pod.podId);
await client.pods.apiKeys.delete(pod.podId, scopedKey.apiKeyId);
+
+// Inbox-scoped keys
+const inboxKeys = await client.inboxes.apiKeys.list(inbox.inboxId);
+await client.inboxes.apiKeys.delete(inbox.inboxId, inboxKey.apiKeyId);
```
@@ -161,9 +199,14 @@ def onboard_tenant(tenant_id: str, domain_name: str):
)
domain = client.pods.domains.create(pod.pod_id, domain=domain_name)
- # Scoped key for the tenant
+ # Pod-scoped key for the tenant
key = client.pods.api_keys.create(pod.pod_id, name=f"{tenant_id}-key")
+ # Inbox-scoped key for the support inbox
+ inbox_key = client.inboxes.api_keys.create(
+ inbox.inbox_id, name=f"{tenant_id}-support-key"
+ )
+
# Webhook for their events
webhook = client.webhooks.create(
url=f"https://your-server.com/webhooks/{tenant_id}",
@@ -174,7 +217,8 @@ def onboard_tenant(tenant_id: str, domain_name: str):
return {
"pod_id": pod.pod_id,
"inbox_id": inbox.inbox_id,
- "api_key": key.api_key, # deliver securely to tenant
+ "pod_api_key": key.api_key, # deliver securely to tenant
+ "inbox_api_key": inbox_key.api_key,
"webhook_id": webhook.webhook_id,
}
```
@@ -197,11 +241,16 @@ async function onboardTenant(tenantId: string, domainName: string) {
domain: domainName,
});
- // Scoped key for the tenant
+ // Pod-scoped key for the tenant
const key = await client.pods.apiKeys.create(pod.podId, {
name: `${tenantId}-key`,
});
+ // Inbox-scoped key for the support inbox
+ const inboxKey = await client.inboxes.apiKeys.create(inbox.inboxId, {
+ name: `${tenantId}-support-key`,
+ });
+
// Webhook for their events
const webhook = await client.webhooks.create({
url: `https://your-server.com/webhooks/${tenantId}`,
@@ -212,7 +261,8 @@ async function onboardTenant(tenantId: string, domainName: string) {
return {
podId: pod.podId,
inboxId: inbox.inboxId,
- apiKey: key.apiKey, // deliver securely to tenant
+ podApiKey: key.apiKey, // deliver securely to tenant
+ inboxApiKey: inboxKey.apiKey,
webhookId: webhook.webhookId,
};
}
diff --git a/fern/pages/knowledge-base/getting-api-key.mdx b/fern/pages/knowledge-base/getting-api-key.mdx
index 0fcb9ff..270b6b9 100644
--- a/fern/pages/knowledge-base/getting-api-key.mdx
+++ b/fern/pages/knowledge-base/getting-api-key.mdx
@@ -43,6 +43,15 @@ const inbox = await client.inboxes.create();
console.log(`Connected! Created inbox: ${inbox.inboxId}`);
```
+## Scoped API keys
+
+In addition to organization-level keys, you can create keys scoped to a single pod or inbox. Scoped keys restrict access so that the key can only operate on resources within that pod or inbox.
+
+- **Pod-scoped keys**: Create via `POST /pods/{pod_id}/api-keys` or `client.pods.api_keys.create()`
+- **Inbox-scoped keys**: Create via `POST /inboxes/{inbox_id}/api-keys` or `client.inboxes.api_keys.create()`
+
+See the [Multi-Tenancy guide](/multi-tenancy#scoped-api-keys) for details.
+
## Free tier
No credit card required to get started. The free tier includes:
diff --git a/fern/pages/knowledge-base/pods-multi-tenant.mdx b/fern/pages/knowledge-base/pods-multi-tenant.mdx
index 7defccd..0f6e319 100644
--- a/fern/pages/knowledge-base/pods-multi-tenant.mdx
+++ b/fern/pages/knowledge-base/pods-multi-tenant.mdx
@@ -110,6 +110,7 @@ When you delete an inbox, all associated messages, threads, and drafts are autom
- **Cross-pod email works normally.** Inboxes in different Pods can send and receive emails from each other, just like any other email addresses. Pods isolate data access, not email delivery.
- **Inboxes cannot move between Pods.** If you need to reassign an inbox, create a new one in the target Pod.
- **Pod-scoped API keys.** You can create API keys scoped to a specific Pod, so each tenant only has access to their own resources. Create them via `POST /pods/{pod_id}/api-keys`.
+- **Inbox-scoped API keys.** For even finer control, you can create keys scoped to a single inbox. These keys can only access that inbox's threads, messages, and drafts. Create them via `POST /inboxes/{inbox_id}/api-keys`.
- **No limit on Pods.** You can create as many Pods as you need for your customers.
- **Domains can be scoped to one Pod or all Pods.** A domain cannot be shared across a subset of Pods.