<!-- Canonical: https://staging.startupmail.dev/docs/agent-inboxes -->
<!-- Documentation index: https://staging.startupmail.dev/llms.txt -->

# Agent inboxes

> Provision isolated agent mailboxes, least-privilege keys, reviewed drafts, and communication guardrails.

## Provision through code

An organization is the billing and human-team boundary. A **tenant** is an optional isolation boundary
for a customer, agent fleet, or workflow. A tenant API key can never access another tenant; an inbox key
can only access one mailbox.

Use `clientId` for your own stable ID when creating tenants, mailboxes, and drafts. Repeating the same
request returns the resource that was already created instead of creating a duplicate.

```ts
const tenant = await mail.createTenant({ name: "Acme", clientId: "customer_acme" });
const inbox = await mail.createMailbox({
  domainId: "dom_123",
  tenantId: tenant.id,
  clientId: "acme-support-v1",
  localPart: "support",
  displayName: "Acme Support",
  visibility: "private",
  metadata: { customerId: "customer_acme", agent: "support" },
});
```

Domains must still be verified before an address can be provisioned. Create a domain with `createDomain`,
publish its returned DNS records, then call `verifyDomain` until it reports `verified`; create as many
addresses as the verified domain needs.

## Give the agent only its mailbox

Create the runtime key in **Settings → Agent access** after provisioning the mailbox. Select that
mailbox as the resource boundary and grant only `mailboxes:read`, `mail:read`, `mail:send`,
`drafts:read`, `drafts:write`, and `drafts:send` as needed. The secret is returned exactly once.
Store it in your server-side secret manager, never in agent instructions, source code, or browser code.

API keys cannot create more API keys. This prevents a leaked provisioning credential from expanding
its own authority; key creation remains an authenticated administrator action in the web app.

Capabilities define the allowed operations inside the key's organization, tenant, or inbox boundary.
Use one key per workload and grant only the operations it needs.

## Drafts and approval

Create drafts for human review, set `sendAt` (milliseconds since Unix epoch) for a scheduled delivery, or
call `sendDraft` to queue it immediately. Scheduled drafts are claimed atomically by the Worker cron job,
so repeat cron execution cannot duplicate a send.

```ts
const draft = await mail.createDraft(inbox.id, {
  to: ["customer@example.com"],
  subject: "Re: Your request",
  text: "Here is the update you requested.",
});

// A human can inspect or amend it, then approve the send:
await mail.sendDraft(inbox.id, draft.id);
```

## Communication policies

Add mailbox policies to constrain automated communication. A block always wins; if an inbox has an allow
list, addresses not on it are denied. Policies are evaluated for outbound sends and for inbound mail,
including replies as a distinct direction.

```ts
await mail.createMailboxPolicy(inbox.id, {
  direction: "send",
  action: "allow",
  entry: "customer.example",
});
```
