Skip to content

Mailboxes

Purpose. A mailbox is an IMAP account (and POP3, unless POP3 is switched off server-wide with protocols.pop3) with a password, a Maildir and an optional quota. Dovecot authenticates against a view over the same table the API writes (v_dovecot_credentials; v_dovecot_users is the userdb and iterate source), so changes are live.

Behaviour.

  • POST /api/v1/domains/{id}/mailboxes { localPart, password, displayName?, quotaBytes? } — local part lower-cased, 1–64 characters from a-z0-9._+-, starting and ending with a letter or digit, no consecutive dots; password ≥10 chars, stored as Argon2id; quota defaults to the domain default. Refused with 402 license_degraded from the licence’s degraded stage on, or 402 license_limit_reached at the licence’s mailbox limit (nothing is refused in grace or in an install’s 30-day evaluation); existing mail is unaffected. 409 if the address exists as a mailbox or alias.
  • PATCH /api/v1/mailboxes/{id} — suspended:true blocks login and inbound delivery (mail is rejected at SMTP, not queued) and ends every webmail session, so lifting the suspension does not bring them back (active:false does the same); active:false is the soft-delete equivalent; ending the sessions also holds every message the mailbox scheduled with Send later (Compose, send and drafts), so none goes on its own after the suspension is lifted: each waits in Scheduled, marked Not sent, for the owner to send, move or cancel; quotaBytes, displayName. sendingBlocked:false is the one way to clear a containment block (Compromised-mailbox containment); it cannot be set to true by hand, only by the worker.
  • sendingBlocked is not suspended. A mailbox blocked for outbound abuse still signs in, still receives, and still reads its mail — only submission is refused. sendingBlocked, sendingBlockedReason and sendingBlockedAt are read-only on the mailbox and shown as a banner on its admin page.
  • POST /api/v1/mailboxes/{id}/password resets the password and ends every webmail session of the mailbox, an administrator’s on-behalf session included; an access token already issued is refused on its next request, because its session has ended. IMAP sessions already open stay open until they reconnect, and app passwords keep working — they are separate credentials, revoked one by one. It holds every message the mailbox scheduled with Send later: none leaves on its own, and each waits in Scheduled, marked Not sent, until somebody sends, moves or cancels it, because which session scheduled one is not recorded. After a compromise, cancel there what the owner did not write.
  • DELETE removes the row, and with it the mailbox’s settings, its profile picture and every message it has waiting to be sent later or held; the Maildir on disk is kept (reclaim via backup/restore tooling — deliberate; a purge command is planned).
  • Export (owner and admin): POST /mailboxes/:id/export → 201 { url, expiresIn }, a single-use link valid five minutes; GET it to download <address>-<date>.tar.gz, one mboxrd file per folder (INBOX.mbox, Archive/2026.mbox), which Thunderbird, Apple Mail and most clients import. It holds the folders’ mail only: messages waiting in Scheduled and the profile picture are in the database, not in a folder, and are not in it. Read-only — no message is marked read. A domain_admin gets 403, because managing a mailbox is not reading it, a system mailbox 404, a server with no IMAP bridge 503 export_unavailable. Audited when the link is spent (mailbox.export_started), then mailbox.exported with the folder and message counts when the archive is complete, or mailbox.export_failed when it is not — so a download that breaks half-way still leaves a row. The admin console offers it on the mailbox page and in the delete confirmation.
  • Sending: authenticated submission on :587 (STARTTLS) / :465 (TLS) with the mailbox address as username; the envelope sender must be the mailbox or an alias that points to it (reject_sender_login_mismatch).
  • Quota is enforced by Dovecot (maildir quota backend). quotaBytes: 0 is unlimited, and takes effect at once, also on a mailbox that had a limit before. A message that would exceed it is accepted, then deferred at delivery with 452 4.2.2 Quota exceeded: it stays in this server’s mail queue and Postfix retries it, so it is delivered at the next retry (within about an hour) after the owner clears space or an admin raises the quota, and bounced to the sender only after five days (maximal_queue_lifetime). It shows on the Mail queue page meanwhile (quota_full_tempfail = yes, a top-level Dovecot setting). Through 0.12.1 it was refused with 552 5.2.2 and bounced at once.
  • Usage. GET /api/v1/mailboxes/{id}/usage → { mailboxId, address, quotaBytes, usedBytes, messages, percent, updatedAt }; GET /api/v1/domains/{id}/usage returns the same shape for every mailbox of the domain (unpaged). Figures come from Dovecot’s quota_clone plugin, which upserts quota_usage(username, bytes, messages) through a pgsql dict on every change to the mailbox (delivery, expunge, move) — so updatedAt is null and the counters are 0 until the mailbox has received or changed something. percent is null for unlimited quotas and can exceed 100 after a quota is lowered. Deleting a mailbox removes its usage row. To rebuild a row from disk (e.g. after a restore) run doveadm quota recalc -u <address> in the dovecot container.
  • Opening a mailbox in the webmail. An owner or admin can open any active mailbox in the mail client as its owner sees it (POST /api/v1/mailboxes/{id}/webmail-session), and an owner, admin or domain_admin can open their own (POST /api/v1/mailboxes/mine/webmail-session); both return a single-use hand-off URL that the webmail spends at POST /api/v1/auth/handoff, and the access is audited under the administrator’s name (REST API, Admin UI).

Configuration. Outbound rate limit per sender, per hour (limits.perUserMessagesPerHour) and message size (limits.messageSizeBytes) are server settings (PUT /api/v1/server/config).

Limits & edge cases. The dmarc-reports@<first domain> mailbox is a hidden system mailbox (DMARC report ingestion, DMARC aggregate reports), and so is noreply@<first domain> once a password-reset link or a licence warning has been sent (Password reset by email, Licensing); neither is listed, deleted or given a password (409), and no token sends as one, because noreply@ is the one login the licence’s send stop lets through. Usage is reported by the daemons, not computed from disk by the API: a mailbox that has never had a delivery shows 0 even if its Maildir was restored from a backup. Address = localPart@domain is unique across mailboxes and aliases. Deleting a domain requires deleting its mailboxes first. App-specific passwords: App passwords and mailbox two-factor authentication. Filters and autoresponders: Mailbox filters & autoresponders.

App passwords and 2FA. A mailbox owner can mint per-device credentials for IMAP and SMTP and enrol a second factor for webmail; enabling the second factor makes app passwords the only way into a mail client, because IMAP cannot prompt for a code. See App passwords and mailbox two-factor authentication.