> ## Documentation Index
> Fetch the complete documentation index at: https://docs.levelrail.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mailboxes, aliases and the mail queue

> Create team owned mailboxes on the mox mail server, suspend and reset them, forward addresses with aliases, and hold or release outgoing mail.

> [!NOTE]
>
> Available only when `APP_EXPERIMENTAL=mail` is set on the control plane and a mail app from the [mail-mox template](https://docs.levelrail.com/mail-template.md) is running.

## Who owns what

Each mail domain belongs to one team. Mailboxes and aliases on that domain belong to the same team. Members of other teams do not see them: the API answers 404, as if they did not exist. A second team cannot claim a domain that is already taken.

Levelrail keeps only metadata: the address, the owning team, an optional linked Levelrail user, whether the mailbox is suspended, who created it and when. Passwords live in the mail server only, and the control plane never reads message content.

## Claim a domain

The domain must already be served by the mail server. A team owner or admin with the root ability claims it:

```bash theme={"dark"}
levelrail-cli mail domains claim example.com --team <team-id>
levelrail-cli mail domains list
```

## Mailboxes

```bash theme={"dark"}
levelrail-cli mail mailboxes add jo@example.com --linked-user <user-id>
levelrail-cli mail mailboxes list --domain example.com
levelrail-cli mail mailboxes suspend jo@example.com
levelrail-cli mail mailboxes resume jo@example.com
levelrail-cli mail mailboxes passwd jo@example.com
levelrail-cli mail mailboxes delete jo@example.com --confirm jo@example.com
```

* `add` and `passwd` print a generated password **once**. Levelrail does not store it, so copy it and hand it to the mailbox user. Users cannot choose their own password. The length comes from `APP_MAIL_PASSWORD_LENGTH` (default 24).
* `suspend` blocks IMAP, SMTP submission and webmail logins. Mail keeps arriving.
* When a member leaves a team, is removed from it, or is disabled through SCIM, mailboxes linked to them in that team are suspended. Nothing is deleted automatically.
* If the mail server is unreachable at that moment, the mailbox is marked `suspend_pending`, the audit log records it, and the suspend is applied on the next mail call that reaches the server. `mailboxes list` shows the pending state.
* `--linked-user` must be a member of the team that owns the domain.
* `delete` asks for your password, needs the address typed again, and refuses a mailbox that still holds mail unless you pass `--delete-mail`. Suspending is usually the better choice.
* Each new mailbox reports a send cap from `APP_MAIL_DEFAULT_SEND_CAP_PER_HOUR` (default 200). It is recorded as data only and is not enforced yet.

## Aliases

```bash theme={"dark"}
levelrail-cli mail aliases add team@example.com --to jo@example.com,sam@example.com
levelrail-cli mail aliases list --domain example.com
levelrail-cli mail aliases remove team@example.com
```

Alias targets must be mailboxes your team owns.

## Queue

```bash theme={"dark"}
levelrail-cli mail queue list
levelrail-cli mail queue hold
levelrail-cli mail queue release --ids 12,13
```

The queue view shows the sender account, recipient domain, attempts, next attempt and last error. It never shows subjects or bodies. Listing needs the `read:sensitive` ability. Hold and release apply to the whole queue unless you pass `--ids`. Release asks for your password. The queue is an instance wide view, so only instance members can use it.

## Permissions

| Action | Ability |
| - | - |
| List domains, mailboxes, aliases | `read` |
| List the queue | `read:sensitive` |
| Add a mailbox or alias, suspend, resume | `write:sensitive` |
| Reset a password | `write:sensitive` plus step-up |
| Hold the queue | `write:sensitive` |
| Release the queue | `write:sensitive` plus step-up |
| Claim a domain, delete a mailbox | `root` plus step-up |

IAM policies can name `mail:service:<app>`, `mail:domain:<domain>` and `mail:mailbox:<address>`. A Deny on a domain hides every mailbox on it. Every change is written to the audit log with the address, never a password.

## System emails

`levelrail-cli mail system-email enable <domain>` (or **System emails** on the Mail page) sends invites, password resets, alerts and status page notices as `noreply@<domain>` through this mail server. It creates or reuses the `noreply` mailbox with a generated password that is sealed straight into the email provider settings and never shown, then sends a test to your own address (`--to` to change it).
The provider submits to the mail server's private network address on port 587 (STARTTLS, or `--port 465`), trusting exactly the certificate it saw at enable time (a pinned SHA-256 fingerprint), so the self-signed default certificate works without turning verification off.
An existing provider is never replaced silently: pass `--replace-existing`. `disable` puts the earlier provider back and suspends (never deletes) the mailbox; `rotate` sets a new sealed password; `status` shows the sender, the last test and whether SPF, DKIM and DMARC pass.
Until DNS passes and the server's PTR matches its mail host, mail to outside recipients lands in spam or is rejected. Enable, rotate and test are rate limited per caller (`APP_MAIL_SYSTEM_EMAIL_ACTIONS_PER_HOUR`, default 10) and every action, `status` included, needs an instance admin; enable, disable, rotate and test also need step-up. The DNS check behind `status` is cached for `APP_MAIL_SYSTEM_EMAIL_DNS_CACHE_SECONDS` (default 60).
The `noreply` mailbox is marked as the system account: it is hidden from team mailbox lists, cannot be an alias target, and the team mailbox and alias routes (password reset, suspend, resume, delete, aliases) refuse it. Only `mail system-email` manages it.
If the provider was switched to something else after enable, a later `enable` asks for `--replace-existing` again, `disable` leaves the newer provider and its credentials alone and only suspends the mailbox, and `rotate` changes only the system email's own sealed password. A failed enable puts the earlier credentials, provider and mailbox state back.
The engine is trusted only when the instance identity is readable, its container carries this instance's label and the address is on the mail app's own network; otherwise enable and test are refused. The pin is bound to that address: if the mail server's address changes (redeploy or restart), `status` reports `pin: mismatch` and `test` refuses; re-run `enable` to re-pin. Residual risk: the submission hop is plain IP on a Docker bridge, so a container on the same bridge with `NET_RAW` could try ARP spoofing. The default container hardening (`APP_CONTAINER_HARDENING=enforce`) drops all capabilities including `NET_RAW`; with `warn` or `off`, Docker's default set grants it. The certificate pin still makes a spoofed listener fail the TLS handshake.

## Limits

* mox keeps one password per account, so revoking one device means resetting the password for all of them.
* mox has no per hour send limit and no app passwords.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.