# Mailboxes

Source: https://molted.email/docs/mailboxes

Create, manage, and delete mailboxes for receiving inbound email, with plan-based limits.

Mailboxes let your tenant receive inbound email at dedicated addresses. Each mailbox is scoped to a single tenant and can be used to receive and process incoming messages.

The `curl` examples below call `https://mailbox.molted.email`, which fills in your `tenantId` from your key (the CLI and the MCP server go through it too). On `https://api.molted.email` every mailbox route needs your `tenantId` (query string, or the body of a `POST`/`PATCH`); a call without it returns `400`.

| Task | CLI | MCP |
| - | - | - |
| List | `molted mailboxes list` | `list_mailboxes` |
| Get one | `molted mailboxes get <id>` | |
| Create | `molted mailboxes create --address <email>` | |
| Update, pause, resume | `molted mailboxes update <id>` | |
| Activate a provisioning mailbox | `molted mailboxes verify <id>` | |
| Clone | `molted mailboxes clone <id> --address <email>` | |
| Make default | `molted mailboxes set-default <id>` | `set_default_mailbox` |
| Stats | `molted mailboxes stats <id>` | `mailbox_stats` |
| Delete, restore | `molted mailboxes delete <id>`, `molted mailboxes restore <id>` | |
| Resume after a reputation pause | `molted mailboxes unpause <mailboxId>` | |

## Plan limits

The number of mailboxes you can create depends on your plan:

| Plan | Max mailboxes |
| - | - |
| **Trial** | 3 |
| **Free** | 3 |
| **Solo** | 10 |
| **Team** | 50 |
| **Enterprise** | Unlimited |

When your account is in the **expired** state, no new mailboxes can be created.

## Creating a mailbox

Creating a mailbox needs a key with the `admin` scope.

```bash title="CLI"
molted mailboxes create --address support@example.com --display-name "Support Inbox"
```

```bash title="curl"
curl -X POST https://mailbox.molted.email/v1/agent/mailboxes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "support@example.com",
    "displayName": "Support Inbox"
  }'
```

### Response

```json title="Response"
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "tenantId": "tenant-abc",
  "address": "support@example.com",
  "displayName": "Support Inbox",
  "status": "provisioning",
  "autonomyLevel": 3,
  "catchAll": false,
  "isSharedDomain": false,
  "isDefault": false,
  "replyDeadlinesEnabled": false,
  "config": {},
  "createdAt": "2026-03-01T12:00:00Z",
  "updatedAt": "2026-03-01T12:00:00Z"
}
```

A mailbox on the shared domain, or on a custom domain already verified for your account, starts `active`. On a custom domain that isn't verified yet it starts `provisioning` (see [Mailbox status](#mailbox-status)).

### Creating a mailbox in the portal

In the [portal](https://molted.email/docs/portal.md), press **⌘K** and choose **Create mailbox** (or type `new mailbox`). The form asks for:

- **Address**: the part before the `@`, then a domain. The domain list starts with the shared domain, then your verified custom domains; a domain that is still being verified is not offered. The part before the `@` is lowercased, can be up to 64 characters of letters, numbers, dots, dashes and underscores, and has to start and end with a letter or number. If you paste a full address, only the part before the `@` is kept.
- **Display name** (optional), up to 100 characters.

Create mailbox makes the mailbox and opens its page. Every rule on this page applies, including the [shared domain rules](#shared-domain-rules) and the plan limit. When your plan's mailboxes are all in use, the form says so up front with a **See plans** link instead of failing, and a failed create is explained in a sentence ("That address is already taken. Try another."). Only the workspace owner can create mailboxes, see [Owner and member access](#owner-and-member-access).

## Owner and member access

What you change in the portal runs with your role in the workspace, read from the workspace's member list on every request. The **owner** can change mailboxes. Any other member can look but not change: the change fails with `403 owner_required` ("Only the workspace owner can do this."), and on a mailbox's page the **Address**, **Default mailbox**, **Safety** and **Delete mailbox** cards say "Only the workspace owner can change this." A person who is not a member of the workspace at all gets `403 not_a_member`. API keys are different: an agent's key has its own [scopes](https://molted.email/docs/authentication.md#mailbox-scoped-keys), and this role check never applies to it.

## Limit exceeded

If you attempt to create a mailbox beyond your plan's limit, you'll receive a `403 Forbidden` response:

```json title="Error response"
{
  "statusCode": 403,
  "message": "Mailbox limit reached (3). Upgrade your plan to create more mailboxes."
}
```

## Shared domain rules

Addresses on the shared domain (`agent.molted.email` by default) are visible to every tenant, so they're subject to extra checks that a custom domain (which only your tenant can use) is not:

- **Reserved local parts are blocked.** Role-like names (`admin`, `support`, `security`, `billing`, `postmaster`, and others) cannot be used on the shared domain, to avoid phishing and impersonation. This applies to `POST /v1/agent/mailboxes` and `POST /v1/agent/mailboxes/:id/clone`.
- **Addresses must be ASCII, lowercase alphanumeric with dots or hyphens only.** Non-ASCII characters and plus-addressing (`support+x@...`) are rejected: the local part must look exactly like a plain name, not something that folds or reads differently across systems.
- **Addresses are unique across all tenants**, not just within your own, and unique against every tenant's sender addresses too, not just their mailboxes. Creating or cloning onto a shared-domain address another tenant already holds, as either a mailbox or a sender address, returns `409 Conflict`.
- **Catch-all cannot be enabled on the shared domain**: only a verified, receiving-enabled custom domain can be a catch-all.
- **Display names are screened for impersonation.** A display name that reads as an organisation, brand, official role or service (role/security words like `support`, `billing`, `admin`, `official`, `verification`, `bank`, `team`, `noreply`, plus a short list of well-known brands) is rejected, matched regardless of letter case or common lookalike substitutions (e.g. a Cyrillic "а" for a Latin "a", or separators inserted between letters). A display name shaped like an email address or a bare domain (`support@paypal.com`, `ceo@bank.com`, `paypal.com`) is rejected too, even when it contains no banned word: this closes a spoof where a mail client shows only the display name, e.g. `"support@paypal.com" <brave-otter-4821@agent.molted.email>`. On `POST /v1/agent/mailboxes` and `POST /v1/agent/mailboxes/:id/clone`, a rejected display name isn't an error: it's silently replaced with the address's own local part, and the response includes a `displayNameWarning` field saying so. On `PATCH /v1/agent/mailboxes/:id`, a rejected display name is refused outright with `400 Bad Request`.
- **The same screening also runs again on every send.** A shared-domain mailbox's display name is re-checked at send time, not just when it's set: this catches a name saved before the screening existed. A name that now fails the check is dropped for that send only (the email goes out from the bare address, e.g. `brave-otter-4821@agent.molted.email`, with no display name); the mailbox's stored `displayName` is never changed by this. A custom domain's display name is never screened, at write time or send time.

```json title="400 Bad Request: invalid local part"
{
  "statusCode": 400,
  "message": "\"support+x\" is not a valid shared-domain local part (lowercase alphanumeric, dots and hyphens only)"
}
```

```json title="201 Created: display name rejected on create, falls back with a warning"
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "tenantId": "tenant-abc",
  "address": "brave-otter-4821@agent.molted.email",
  "displayName": "brave-otter-4821",
  "status": "active",
  "config": {},
  "createdAt": "2026-03-01T12:00:00Z",
  "updatedAt": "2026-03-01T12:00:00Z",
  "displayNameWarning": "Display name \"PayPal Support\" is not allowed on the shared domain, so this mailbox uses \"brave-otter-4821\" instead. Custom domains allow any display name."
}
```

```json title="400 Bad Request: display name rejected on update"
{
  "statusCode": 400,
  "message": "Display name \"Billing Team\" is not allowed on the shared domain: it reads as an organisation, brand, official role, or an email address or domain. Custom domains allow any display name."
}
```

```json title="409 Conflict: reserved local part"
{
  "statusCode": 409,
  "message": "\"support\" is reserved and cannot be used"
}
```

```json title="409 Conflict: address already in use"
{
  "statusCode": 409,
  "message": "\"sales@agent.molted.email\" is already in use on the shared domain"
}
```

None of this applies to a verified custom domain you own: pick any local part and any display name, and only your tenant's own mailboxes compete for it.

## Mailbox status

Each mailbox has a status field:

| Status | Description |
| - | - |
| `provisioning` | Mailbox is being set up |
| `active` | Mailbox is ready to receive email |
| `paused` | Mailbox is temporarily disabled |
| `error` | Mailbox encountered a configuration error |

On a custom domain, both creating a mailbox and moving it from `provisioning` to `active` require that domain to be verified for **your own** tenant. Creating a mailbox on a domain another tenant has already verified is rejected outright (`409 Conflict`); a mailbox that stayed `provisioning` because the domain wasn't verified yet cannot be activated by `PATCH .../mailboxes/:id` either, until it is (`400 Bad Request`, "Domain ... must be verified before this mailbox can be activated").

Once the domain is verified ([Domains](https://molted.email/docs/domains.md)), activate the mailbox with `PATCH .../mailboxes/:id` and `{ "status": "active" }`, or:

```bash title="CLI"
molted mailboxes verify MAILBOX_ID
```

A mailbox that is already `active` is returned unchanged.

Sends are checked too, at send time: a mailbox whose domain is neither the shared domain nor verified for your account is never used as a sender, whatever its status. The send is blocked with reason `sender_domain_not_verified`. This also covers sends that were already queued or approved. A verified domain that is temporarily failing its DNS checks (`temporary_failure`) still counts as verified here; `pending` and `failed` do not.

## Updating a mailbox

Use `PATCH /v1/agent/mailboxes/:id` to update mailbox properties:

```bash title="CLI"
molted mailboxes update MAILBOX_ID --display-name "New Display Name"
```

```bash title="curl"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "New Display Name"
  }'
```

### Update fields

| Field | Type | Required | Description |
| - | - | - | - |
| `displayName` | `string` or `null` | No | Display name for the mailbox. On the shared domain, a changed name is screened for impersonation, see [Shared domain rules](#shared-domain-rules). |
| `address` | `string` (email) | No | A new address on a verified custom domain, see [Changing the email address](#changing-the-email-address). From an agent key, any change is a human decision, see [Address and catch-all changes](#address-and-catch-all-changes). |
| `status` | `string` | No | Set to `active` or `paused`, see [Pausing and resuming](#pausing-and-resuming). |
| `autonomyLevel` | `1`, `2` or `3` | No | The mailbox's [autonomy level](https://molted.email/docs/autonomy-levels.md). Lowering it is always allowed; raising it from an agent key is a human decision, see [Loosening a control](https://molted.email/docs/errors.md#loosening-a-control). Also `molted mailboxes autonomy set <mailboxId> --level <level>`. |
| `config` | `object` | No | Per-mailbox configuration (see below). |
| `catchAll` | `boolean` | No | Enable or disable catch-all for this domain. Only on a verified, receiving-enabled custom domain, and only one mailbox per domain (`409` otherwise). From an agent key, turning it on or off is a human decision, see [Address and catch-all changes](#address-and-catch-all-changes). |
| `isDefault` | `true` | No | Make this mailbox the [default mailbox](#default-mailbox). Only `true` is accepted. |

Updating a mailbox needs a key with the `admin` scope, and `manage` on the mailbox for a mailbox-scoped key.

`replyDeadlinesEnabled` is not an update field: it is read-only for API keys, and a `PATCH` that includes it returns `403 reply_deadlines_human_only`. See [Reply deadlines](#reply-deadlines).

### Mailbox config

The `config` field is a JSON object stored with the mailbox. The only key Molted reads from it today is `humanizer`:

| Field | Type | Description |
| - | - | - |
| `humanizer` | `object` | This mailbox's own [humanizer](https://molted.email/docs/humanizer.md) settings: `{ enabled, style }`. `enabled` is `true` (humanize this mailbox's mail, even while the account setting is off), `false` (never humanize it), or `null`/unset (follow the account setting, which is where every mailbox starts). `style` is `casual`, `professional` or `friendly`, or `null` for the account's style. An explicit off at the mailbox or the agent wins over an on; the agent's own style and a per-send `humanizeStyle` override this mailbox's style, see [Humanizer precedence](https://molted.email/docs/humanizer.md#precedence). A `null` field clears just that field; `humanizer: null` clears both. From an agent key, turning it on (given the account setting) or any change to its style is a human decision, see [Loosening a control](https://molted.email/docs/errors.md#loosening-a-control); turning it off always works. A plan without the humanizer can't turn it on (`403 humanizer_not_in_plan`). |

Every response that returns a mailbox (`GET /v1/agent/mailboxes/:id`, each mailbox in `GET /v1/agent/mailboxes`, the `PATCH`, create and clone responses, and the `mailbox` in a restore's response) also carries `humanizer`: `{ "setting": true | false | null, "enabled": boolean, "source": "mailbox" | "tenant" | "default" }`. `setting` is the stored `config.humanizer.enabled`; `enabled` is whether a send from this mailbox is humanized when neither the agent nor the send sets it, and `source` says which setting decided that.

`config` replaces the whole stored object on write, with one exception: a PATCH that omits `humanizer` (or gives only part of it) carries the rest of `humanizer` forward from what's stored. Any other key you stored before and leave out is dropped, so read the mailbox first and send back everything you want to keep. Other keys are stored and returned as given, but nothing acts on them.

```bash title="CLI - set the mailbox's humanizer style"
molted mailboxes update MAILBOX_ID --config '{"humanizer": {"style": "friendly"}}'
```

```bash title="CLI - turn the humanizer off for this mailbox"
molted mailboxes update MAILBOX_ID --config '{"humanizer": {"enabled": false}}'
```

```bash title="curl - update config"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "humanizer": { "style": "friendly" }
    }
  }'
```

### Changing the email address

You can change a mailbox's email address if it is on a custom domain. Shared domain mailboxes (`agent.molted.email`) cannot have their address changed.

The new address must be on a verified custom domain owned by the tenant.

```bash title="curl"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "new-address@yourdomain.com"
  }'
```

**Restrictions:**

- Shared domain mailboxes cannot change their address
- The new address must be on a verified custom domain
- The new address must be unique within the tenant
- A catch-all mailbox can't move to a different domain; disable catch-all first
- Changing address affects deliverability since the new address has no sending reputation

### Address and catch-all changes

The address and the catch-all decide which email reaches the mailbox. Changing the address stops mail arriving at the old one, turning catch-all on pulls in every unmatched address on the domain, and turning it off stops that mail. So from an agent key, any change to `address` and turning `catchAll` on or off is a human decision, gated by your account's human-gate mode like a resume (see [Loosening a control](https://molted.email/docs/errors.md#loosening-a-control)):

- `enforce`: `403 human_decision_required` with `kind: "mailbox-address"`, the mailbox id, and a `decisionUrl` to share with a person. Nothing in the request is applied, other fields included.
- `warn`: the change applies, with a `Molted-Deprecation: human-gate` header.
- `off`: the change applies.

The address is compared on the exact stored value, so a change in case only (`Sales@` to `sales@`) counts as a change. Sending the value the mailbox already has is not a change and is never gated.

Two other requests change which mailbox gets an address's mail, so they get the same answer from an agent key:

- **Creating or cloning onto an address another mailbox's catch-all receives.** An exact address beats a catch-all, so the new mailbox would take that address's mail away from the catch-all mailbox. The `id` is the catch-all mailbox's.
- **Deleting a catch-all mailbox.** A deleted mailbox stops being the catch-all at once, and only mail to its own address is held for a restore.

Addresses ignore case within your account: creating, cloning or renaming onto an address a live mailbox already has, in any case (`SALES@acme.com` next to `sales@acme.com`), is `409 mailbox_address_taken` for everyone, since the two would split that address's mail. Restoring a deleted mailbox whose address a live mailbox now has is refused the same way.

A person makes these changes in the portal, on the mailbox's page under **Address** (the `decisionUrl` links there): edit the address, or flip **Catch-all**. A person is never gated. The catch-all control shows only when the mailbox's domain can have one; `GET /v1/agent/mailboxes/:id` says so in `catchAllAvailable` (a verified custom domain with receiving on). An agent holding a portal session sees both settings but can't change them.

Every address or catch-all change made through `PATCH`, a person's or an agent's, is recorded on the Activity timeline (`mailbox.address_changed` or `mailbox.catch_all_changed`, with who made it and the old and new values). Deleting and restoring a mailbox are recorded as `mailbox.deleted` and `mailbox.restored` instead; the restore's record says whether the catch-all came back (`catchAllRestored`).

## Cloning a mailbox

You can clone an existing mailbox to create a new one with the same settings. This is useful when you want to set up a new mailbox with the same autonomy level, humanizer style, rules, and labels as an existing one.

### What gets copied

- **Autonomy level** (L1/L2/L3)
- **Config** (the whole `config` object, including the humanizer switch and style)
- **Custom rules** (with label references remapped to the cloned labels)
- **[Labels](https://molted.email/docs/labels.md)** (formerly folders; copied empty: threads are not copied)
- **Paused state** (see [Guards](#guards))

### What does not get copied

- Threads and sending history
- Catch-all setting (only one mailbox per domain can be catch-all)
- Email address and display name (you provide new ones)
- [Reply deadlines](#reply-deadlines) (a new mailbox starts with them off)

### Via API and CLI

```bash title="CLI"
molted mailboxes clone MAILBOX_ID --address sales@example.com --display-name "Sales Inbox"
```

```bash title="curl"
curl -X POST "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID/clone" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "sales@example.com",
    "displayName": "Sales Inbox"
  }'
```

The response is the newly created mailbox object, identical to the response from creating a mailbox (including `displayNameWarning` when a shared-domain display name was replaced). The portal has no clone action.

### Guards

- **Requires `admin` scope.** Only API keys with the `admin` scope can clone mailboxes, and a mailbox-scoped key also needs `read` on the source mailbox.
- **The same address rules as creating apply**, including the [shared domain rules](#shared-domain-rules) and the `409 mailbox_archived` for a deleted mailbox's address.
- **Plan limits apply.** The clone counts against your mailbox quota just like creating a new mailbox.
- **Address must be unique.** The new address must not already exist for your tenant.
- **A paused mailbox clones paused.** Cloning a paused mailbox (paused by hand or by the reputation monitor) creates a paused copy, which only a person can resume. On a custom domain that isn't verified for your account yet, the copy starts `provisioning` instead.

## Pausing and resuming

You can pause a mailbox to temporarily stop all outbound sending. This is useful during incidents, migrations, or while investigating deliverability issues.

### Via API and CLI

```bash title="CLI"
molted mailboxes update MAILBOX_ID --status paused
molted mailboxes update MAILBOX_ID --status active
```

```bash title="Pause a mailbox"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "paused"
  }'
```

```bash title="Resume a mailbox"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "active"
  }'
```

Only an `active` mailbox can be paused: pausing a `provisioning` mailbox returns `400 Bad Request`. A mailbox on a custom domain can only be resumed while that domain is verified for your account (`400 Bad Request` otherwise).

Only a person resumes a paused mailbox, including one that was paused automatically. An agent key can always pause; its resume is a human decision (`403 human_decision_required` with a `decisionUrl`, or a `Molted-Deprecation: human-gate` warning while the account is in `warn`). See [Loosening a control](https://molted.email/docs/errors.md#loosening-a-control).

### Reputation pauses

The reputation monitor can also pause a mailbox when its bounce or complaint rate crosses a threshold, see [Reputation](#reputation). The pause is kept on the mailbox's reputation, and blocks sends with the same `mailbox_paused` reason. Check it with `GET /v1/agent/mailboxes/:id/reputation` (`molted mailboxes reputation <mailboxId>`). Lifting it is `POST /v1/agent/mailboxes/:id/unpause` (`molted mailboxes unpause <mailboxId>`), which needs `manage` on the mailbox, answers `400` when the mailbox isn't reputation-paused, and from an agent key is the same human decision as a resume.

### Via portal

In the portal, open the mailbox's page and use **Pause this mailbox** (or **Resume** on a paused one) next to its sending status.

### What happens when a mailbox is paused

- **Outbound emails are blocked.** Any send attempt returns a `mailbox_paused` block reason.
- **Journey steps and scheduled follow-ups wait.** They are retried every 15 minutes while the mailbox is paused, go out once it's resumed, and are cancelled (`paused_timeout`) after 72 hours paused.
- **Inbound messages continue to be received** and stored normally.

## Reputation

Every mailbox has a reputation, worked out from what happened to its mail in the last 30 days. It is how a mailbox that starts bouncing or drawing complaints is stopped before it damages your sending domain.

```bash title="CLI"
molted mailboxes reputation MAILBOX_ID
molted mailboxes reputation recalculate MAILBOX_ID
```

```bash title="curl"
curl "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID/reputation" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json title="Response"
{
  "id": "7d2a...",
  "mailboxId": "550e8400-e29b-41d4-a716-446655440000",
  "tenantId": "tenant-abc",
  "score": 85,
  "bounceRate": 0.03,
  "complaintRate": 0,
  "totalSends": 200,
  "totalBounces": 6,
  "totalComplaints": 0,
  "status": "healthy",
  "lastCalculatedAt": "2026-03-31T14:22:00Z",
  "createdAt": "2026-03-01T12:00:00Z",
  "updatedAt": "2026-03-31T14:22:00Z"
}
```

Reading needs `read` on the mailbox (MCP: `check_reputation`). A mailbox with no reputation yet is calculated on the first read.

### How it is counted

- **Window.** Only sends from the last **30 days** count, and only sends that were accepted or sent: blocked, rejected and held sends never reached a provider. After a person resumes a reputation-paused mailbox, only sends since the resume count, so the history that caused the pause cannot pause it again at once.
- **Bounces and complaints** are counted per send, so a retried delivery notice does not count twice. Hard and soft bounces both count.
- **Score** runs from 0 to 100: `100 - round(bounceRate * 500) - 25 * complaints`, never below 0. A 3% bounce rate scores 85, 5% scores 75 and 15% scores 25.
- **Status** follows the rates:

| `status` | When |
| - | - |
| `healthy` | Bounce rate under 5%, and complaints under the limit below. |
| `warning` | Bounce rate of **5%** or more. |
| `paused` | Bounce rate of **15%** or more, or complaints at the limit below. |

- **Minimum volume.** The bounce rate can only pause a mailbox once it has at least **20 sends** in the window. Below that, a rate of 5% or more reads `warning`, never `paused`: one bounce out of your first few sends is not yet a signal.
- **Complaints** are not gated by volume. A mailbox pauses when its complaints reach 0.3% of its sends in the window, rounded up, and never fewer than 1. So one complaint pauses a mailbox with up to 333 sends in the window, and a bulk sender gets a proportional limit instead.

These are Molted's default thresholds.

### What a pause does

When the status turns `paused`, the mailbox is paused too: its sends are blocked with `mailbox_paused`, its journey steps and follow-ups wait (see [Pausing and resuming](#pausing-and-resuming)), and its pause reason starts with "Reputation:". Only a person resumes it, from the mailbox's page or with `molted mailboxes unpause`, which resets the counters (see [Reputation pauses](#reputation-pauses)).

### Recalculating

The reputation is recalculated whenever a delivery event arrives for one of the mailbox's sends. To refresh it by hand:

```
POST https://mailbox.molted.email/v1/agent/mailboxes/:id/reputation/recalculate
```

It needs `manage` on the mailbox, and a mailbox recalculated in the last 5 minutes answers `400` ("Reputation was recalculated recently. Try again in a few minutes."). A person in the portal has the same route under `/v1/me/mailboxes/:id/reputation/recalculate`, and the same cooldown. The portal's mailbox page shows a "Last 7 days" bounce rate rather than this score.

## Deleting a mailbox

Mailboxes can be deleted via the API, the CLI or the portal, by your agent or by you. A deletion can be undone for **14 days**: the mailbox is archived first, and only deleted for good once the 14 days are up.

### Via API

```bash title="curl"
curl -X DELETE "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

A successful deletion returns the date it goes for good:

```json title="Response"
{
  "deleted": true,
  "archived": true,
  "mailboxId": "MAILBOX_ID",
  "address": "support@yourdomain.com",
  "archivedAt": "2026-10-01T10:00:00.000Z",
  "purgeAfter": "2026-10-15T10:00:00.000Z",
  "restorableBy": "agent_or_person",
  "message": "Archived support@yourdomain.com: ..."
}
```

`restorableBy` is `agent_or_person` when an agent key deleted it (that agent can [restore](#restoring-a-deleted-mailbox) it too), and `person` otherwise.

```bash title="CLI"
molted mailboxes delete MAILBOX_ID
```

### Via portal

In the portal, open the mailbox's page, click **Delete mailbox** in the last card and type the mailbox's address to confirm. Only the workspace owner can, and never for the last mailbox. You land on Home with a line confirming it.

### What happens when a mailbox is deleted

For the 14 days after a deletion, the mailbox is archived:

- **It stops sending at once**, and your agents no longer see it: it is gone from mailbox lists, and its threads and messages are hidden from every agent route. Its threads are not moved to trash, so a restore brings them back exactly as they were.
- **Its address stays reserved.** Nobody, in your workspace or another, can create it again while it can still be restored.
- **Mail sent to it is kept.** It is stored and held under the mailbox, out of your agents' sight (no `inbound.received` or `inbound.classified` events), and comes back with it on a restore. Only mail to the mailbox's own address: a deleted catch-all mailbox stops being the catch-all at once, and mail to the other addresses it caught is not held for it. That is why an agent deleting a catch-all mailbox needs a person, see [Address and catch-all changes](#address-and-catch-all-changes).
- **You get a card on Home** when an agent deleted it ("support-bot archived [support@yourdomain.com](mailto:support@yourdomain.com). It's deleted for good on ..."), with **Restore** and **Dismiss**.
- **It doesn't count** against your plan's mailbox limit.
- **Its scheduled follow-ups wait.** They never go out from another address, and resume if it's restored.
- **If it was your default mailbox, it no longer is.** Sends without a mailbox go out from your oldest active mailbox, and restoring it does not make it the default again.

After 14 days it is deleted for good: its threads, messages, held mail and attachments, the content of the emails it sent, and its pending follow-ups are removed. Its key scopes, rules, folders and reputation go with it; your send history stays (who, when, delivery), without the content or the mailbox. The address is free again.

A mailbox deleted before this 14-day window existed was given one retroactively: it shows up as a normal archived mailbox with **Restore**, and a Home card of its own ("[support@yourdomain.com](mailto:support@yourdomain.com) was deleted before restore existed. It will be permanently removed on ..."). Its threads, already moved to trash by the old delete, are hidden with the rest of the mailbox while it's archived and come back to Trash on restore, not pulled back into the open inbox. (One exception: if its address on the shared domain now collides with another account's, it was left exactly as it was, with no restore window at all; contact support.)

### Restoring a deleted mailbox

Within the 14 days, restore it from the portal: **Settings** lists deleted mailboxes with **Restore** and the days left, the deleted mailbox's own page has **Restore mailbox**, and so does the Home card. Your agent can restore a mailbox an agent deleted, with the same access it needs to delete it (a mailbox you deleted can only be restored by a person, from the portal):

```bash title="curl"
curl -X POST "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID/restore" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```bash title="CLI"
molted mailboxes restore MAILBOX_ID
```

```json title="Response"
{
  "restored": true,
  "mailbox": { "id": "MAILBOX_ID", "address": "support@yourdomain.com", "status": "active" },
  "catchAllRestored": false,
  "releasedMessages": 3
}
```

`mailbox` is the full mailbox object. `releasedMessages` counts the emails that arrived while it was deleted.

The mailbox, its threads and settings come back as they were, and each email that arrived meanwhile is released to your agents with an `inbound.released` event (`reason: "mailbox_restored"`). A catch-all mailbox gets its catch-all back unless another mailbox took it meanwhile; mail to the other addresses it caught while it was deleted is not among the released emails.

If the mailbox is on a custom domain, its ownership is re-checked at restore time, since the 14 days in between is enough for a domain's verification to lapse or for someone else to claim it:

- **Domain no longer verified for you:** the mailbox comes back as `provisioning` instead of its old status, and the response carries `"domainNeedsVerification": true` plus a message saying it needs (re-)verifying before it can send again. (The shared domain and a domain you still own are unaffected.)
- **Domain now verified by a different account:** the restore is refused outright with `409 domain_conflict`, rather than handing you back a mailbox on someone else's domain.

On the shared domain, restore also re-checks that nobody else now holds the exact address: refused with `409 address_taken` if another account has since taken it, as a mailbox or as a sender address.

### Guards

- **You cannot delete the last mailbox** (`400`). Every tenant must have at least one active mailbox. Create another mailbox first if you need to delete the current one.
- **Requires `admin` scope** (and `manage` on the mailbox for a mailbox-scoped key), for deleting and restoring alike.
- **An agent restoring a mailbox a person deleted** returns `403 human_archive`; restore it from the portal.
- **Restoring after the 14 days** returns `410 archive_expired`; the mailbox is being deleted for good.
- **Restoring at your plan's mailbox limit** returns `403 mailbox_limit`. Delete another mailbox or upgrade first.
- **Restoring onto a domain someone else now owns** returns `409 domain_conflict`.
- **Restoring onto a shared-domain address someone else now holds** returns `409 address_taken`.
- **Creating a deleted mailbox's address again** within the 14 days returns `409 mailbox_archived`, with the `mailboxId` to restore instead (in the error body, next to `error` and `message`).

## Listing mailboxes

```bash title="CLI"
molted mailboxes list
```

```bash title="curl"
curl "https://mailbox.molted.email/v1/agent/mailboxes" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Each mailbox carries `replyDeadlinesEnabled` (see [Reply deadlines](#reply-deadlines)) and `isDefault`: `true` on the workspace's default mailbox, the one that sends without a `mailboxId` go out from (see [Default mailbox](#default-mailbox)). A key scoped to some mailboxes sees the same flag (it marks the workspace default, which may not be one of its mailboxes, so it may see none flagged), but its own sends without a `mailboxId` go out from its oldest active scoped mailbox, not the workspace default.

## Reply deadlines

Each inbound email normally gets a reply deadline, and an email that is still unanswered when its deadline passes is escalated to you: the conversation moves to **Needs you**, Activity shows "Reply deadline missed", and a `thread.sla_breached` [event](https://molted.email/docs/events.md#thread-and-workspace-events) is published. That suits a support or sales mailbox. For a personal mailbox it serves no purpose, so each mailbox has a **Reply deadlines** switch.

- **On:** an email gets a deadline of **60 minutes**. Auto-replies, opt-outs and delivery notices get none. A shorter deadline applies to mail the classifier holds for a person because it reads as conflicting or adversarial (15 minutes), or because it was unsure about an auto-reply, delivery notice or opt-out (30 minutes). Mail held as spam or for a safety review is never escalated. See [Routing actions](https://molted.email/docs/inbound.md#routing-actions) for how the deadline follows the email's label.
- **Off:** new email in this mailbox gets no deadline, and a deadline that was already pending when you turned the switch off is ignored. Nothing else changes: the same routing, approvals, quarantine and notifications apply.

The switch is on the mailbox's page in the portal, under **Reply deadlines**. It is on for every mailbox that existed before the switch was introduced and off for mailboxes created after, including the one you sign up with. Only a person can change it: turning deadlines off means nobody is told about an unanswered email, so an API key, the CLI or an agent cannot. Agents can see it: every mailbox you list or fetch carries `replyDeadlinesEnabled`.

## Default mailbox

Every send that names no mailbox (an API or MCP send without `mailboxId`, a journey step with no mailbox, a follow-up, a reply from the portal to an older thread with no mailbox) goes out from your workspace's default mailbox. The mailbox you sign up with is the default.

Choose another one from the portal (a mailbox's page, **Make default**), the CLI, MCP (`set_default_mailbox`) or the API:

```bash title="CLI"
molted mailboxes set-default MAILBOX_ID
```

```bash title="curl"
curl -X PATCH "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "isDefault": true }'
```

The response is the mailbox, with `"isDefault": true`. Every mailbox list and mailbox page then tags it as the default.

- **Only an active mailbox can be the default.** A paused or provisioning mailbox returns `409 mailbox_not_active`.
- **It needs a key with access to every mailbox** (or a person in the portal). A key scoped to some mailboxes returns `403 mailbox_scope_denied`, because the default changes where every unattributed send in the workspace goes.
- **`"isDefault": false` returns `400`.** There is always a default; change it by choosing another mailbox.
- **If the default is paused or deleted**, sends without a mailbox fall back to your oldest active mailbox, and `isDefault` moves to that one. A paused default takes over again once it's resumed. A deleted one does not, even if it's restored.
- The change is recorded in the audit log (`mailbox.default_changed`).

## Mailbox statistics

Get per-mailbox usage statistics including send volume, deliverability metrics, and inbound counts. Needs `read` on the mailbox.

```bash title="CLI"
molted mailboxes stats MAILBOX_ID --period 7d
```

MCP: `mailbox_stats`.

```bash title="curl"
curl "https://mailbox.molted.email/v1/agent/mailboxes/MAILBOX_ID/stats?period=7d" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Query parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `period` | `24h` \| `7d` \| `30d` | `7d` | Time window for metrics |

### Response

```json title="Response"
{
  "mailboxId": "550e8400-e29b-41d4-a716-446655440000",
  "period": "7d",
  "sends": {
    "total": 1250,
    "period": 342
  },
  "inbound": {
    "period": 87
  },
  "deliverability": {
    "delivered": 330,
    "bounced": 8,
    "complained": 1,
    "opened": 210,
    "clicked": 45,
    "deliveryRate": 0.973,
    "bounceRate": 0.024,
    "complaintRate": 0.003,
    "openRate": 0.614,
    "clickRate": 0.132
  },
  "suppressionCount": 12,
  "lastSendAt": "2026-03-31T14:22:00Z",
  "dailySends": [
    { "day": "2026-03-25", "count": 48 },
    { "day": "2026-03-26", "count": 52 },
    { "day": "2026-03-27", "count": 41 }
  ]
}
```

The stats are computed from existing send and delivery data. The `dailySends` array provides data for charting send volume over the selected period.

## Checking your usage

Mailbox usage is included in the billing status endpoint (CLI `molted billing status`, MCP `billing_status`):

```bash title="curl"
curl "https://mailbox.molted.email/v1/billing/status" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

The response includes a `mailboxes` field:

```json title="Mailbox usage"
{
  "mailboxes": {
    "used": 2,
    "limit": 3
  }
}
```

A `null` limit means unlimited mailboxes (Enterprise plan).
