API

Mail API

Install and adopt an iRedMail stack over SSH, run the mail admin panel (domains, mailboxes, stats, backups, DNS, components), and deploy webmail.

The Mail API drives Alaf Server's self-hosted email feature: it installs an iRedMail stack on one of your servers over SSH, tracks the wizard's progress, then exposes an admin panel for the running server — domains, mailboxes, stats, backups, DNS health, and per-component control — plus a one-click webmail deploy. In the dashboard this is the Emails page.

Self-hosted only

Every route here is gated by localOnly and is never mounted in Alaf Server Cloud — the module is dynamically imported only on self-hosted instances. On a cloud instance these paths return 404.

Base path & auth

All paths are relative to your instance, under /api/mail — e.g. https://your-host/api/mail/status. Send a personal access token as a bearer header (Authorization: Bearer <token>), created with alaf server token create. The dashboard uses your session cookie instead. See the API overview and auth model for details.

Every route is server-scoped

Most paths carry a :serverId (or take one in the body / ?serverId=). Alaf Server checks that the server belongs to your organization on every call — a mail_server grants SSH-level reach into the box, so a serverId outside your org returns 404, indistinguishable from one that doesn't exist.

Endpoints

Setup wizard

Method & pathPermissionWhat it does
GET /api/mail/stepsmail_server:readList all setup steps and the total count.
GET /api/mail/status?serverId=mail_server:readCurrent setup progress for a server (rendered from its on-server state file).
GET /api/mail/serversmail_server:listList every server Alaf Server has provisioned (or is provisioning) mail on.
DELETE /api/mail/servers/:serverIdmail_server:adminStop managing a server (drops the DB row only; leaves the stack + state file intact).
POST /api/mail/scanmail_server:writeProbe a server for an existing iRedMail install + its on-server state (read-only).
POST /api/mail/adoptmail_server:writeRe-adopt a scanned server whose orchestrator state was lost.
POST /api/mail/setupmail_server:writeStart or resume the install wizard — returns an SSE stream.
POST /api/mail/setup/cancelmail_server:writeCancel the running setup.
POST /api/mail/setup/dns-ackmail_server:writeMark DNS (DKIM) records as configured, releasing the DNS gate.
POST /api/mail/setup/ptr-ackmail_server:writeMark reverse-DNS (PTR) as configured, releasing the PTR gate.
POST /api/mail/setup/resetmail_server:adminWipe the on-server state file (does not touch iRedMail).

Post-install

Method & pathPermissionWhat it does
GET /api/mail/health/:serverIdmail_server:readLive systemd status of every mail-core daemon.
POST /api/mail/credentials/postmastermail_server:writeRotate the postmaster account password.

Admin — domains

Method & pathPermissionWhat it does
GET /api/mail/admin/:serverId/domainsmail_server:listList mail domains on the server.
POST /api/mail/admin/:serverId/domainsmail_server:writeAdd a mail domain.
GET /api/mail/admin/:serverId/domains/:domainmail_server:readGet one domain's config.
PATCH /api/mail/admin/:serverId/domains/:domainmail_server:writeUpdate a domain (quotas, limits, active flag).
DELETE /api/mail/admin/:serverId/domains/:domainmail_server:adminDelete a domain (add ?cascade=true to remove its mailboxes too).
GET /api/mail/admin/:serverId/domains/:domain/dependentsmail_server:readCount mailboxes/aliases that depend on a domain.
GET /api/mail/admin/:serverId/domains/:domain/dnsmail_server:readDNS records + publish state for an additional domain.
POST /api/mail/admin/:serverId/domains/:domain/dns/acknowledgemail_server:writeConfirm a domain's DNS records are published.
GET /api/mail/admin/:serverId/domains-dns/pendingmail_server:readList additional domains whose DNS is still pending.

Admin — mailboxes

Method & pathPermissionWhat it does
GET /api/mail/admin/:serverId/mailboxes?domain=mail_server:listList mailboxes in a domain.
POST /api/mail/admin/:serverId/mailboxesmail_server:writeCreate a mailbox.
GET /api/mail/admin/:serverId/mailboxes/:emailmail_server:readGet one mailbox.
PATCH /api/mail/admin/:serverId/mailboxes/:emailmail_server:writeUpdate a mailbox (name, password, quota, active).
DELETE /api/mail/admin/:serverId/mailboxes/:emailmail_server:adminDelete a mailbox (add ?hard=true to purge its maildir; default is a soft delete).

Admin — stats, backups, DNS, components

Method & pathPermissionWhat it does
GET /api/mail/admin/:serverId/statsmail_server:readAggregate counts (domains, mailboxes, storage).
GET /api/mail/admin/:serverId/backup-policymail_server:readThe mail server's backup policy (or null).
POST /api/mail/admin/:serverId/backup-policymail_server:adminCreate or update the backup policy.
GET /api/mail/admin/:serverId/backup-runsmail_server:readRecent backup runs for the mail server.
GET /api/mail/admin/:serverId/dns-scan?domain=mail_server:readLive DNS health scan (MX/SPF/DKIM/DMARC/PTR).
POST /api/mail/admin/:serverId/test-emailmail_server:writeSend a test email from the server.
POST /api/mail/admin/:serverId/components/restart-allmail_server:adminRestart every mail component.
POST /api/mail/admin/:serverId/components/:key/:actionmail_server:adminRun an action (start/stop/restart/…) on one component.
GET /api/mail/admin/:serverId/components/:key/logs?lines=mail_server:readTail a component's logs.

Webmail

Method & pathPermissionWhat it does
GET /api/mail/webmail/targets?serverId=mail_server:readList deploy-target options (self-hosted servers / cloud).
POST /api/mail/webmail/deploy-projectmail_server:writeCreate a webmail project + deployment and start the build.

Start (or resume) setup

Kicks off the iRedMail install wizard against a server and streams progress as Server-Sent Events. The wizard is idempotent: re-POST with startStep to resume from where a previous run stopped (or was gated). Only one install may run per instance at a time.

POST /api/mail/setup

Prop

Type

config is an optional IRedMailConfig:

Prop

Type

curl -N -X POST https://your-host/api/mail/setup \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"serverId":"srv_123","domain":"example.com"}'

The stream emits step_start, log, and step_done events per step, plus these control events:

  • dns_records / dns_pending — the DKIM step publishes the records you must add, then holds. Add the records at your DNS provider and call POST /api/mail/setup/dns-ack, then re-POST /setup with the resumeStep from the event.
  • ptr_pending — after DNS is acknowledged, the wizard holds again for reverse-DNS (PTR). Set the PTR record at your VPS provider, call POST /api/mail/setup/ptr-ack, and resume.
  • complete — carries domain, webmailUrl, and adminUrl.
  • error — carries message and (when resumable) resumeStep.

Acknowledge DNS / PTR

Both flip a single flag in the on-server state file so the wizard can move past its gate on the next /setup POST. Both take the same body.

POST /api/mail/setup/dns-ack
POST /api/mail/setup/ptr-ack

Prop

Type

The same { serverId } body applies to POST /api/mail/scan, POST /api/mail/adopt, POST /api/mail/setup/cancel, and POST /api/mail/setup/reset.

Adopt an existing install

If Alaf Server lost its record of a mail server (rebuilt desktop, wiped DB), scan the server, then adopt it — no reinstall, nothing changes on the box.

POST /api/mail/scan
POST /api/mail/adopt
# 1. Probe for iRedMail + its on-server state
curl -X POST https://your-host/api/mail/scan \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"serverId":"srv_123"}'

# 2. Re-adopt it (idempotent)
curl -X POST https://your-host/api/mail/adopt \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"serverId":"srv_123"}'

Rotate the postmaster password

POST /api/mail/credentials/postmaster

Prop

Type

Add a mail domain

POST /api/mail/admin/:serverId/domains

Prop

Type

PATCH /api/mail/admin/:serverId/domains/:domain accepts the same fields (all optional) plus active (boolean).

curl -X POST https://your-host/api/mail/admin/srv_123/domains \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain":"example.com","defaultQuotaMB":2048}'

Create a mailbox

POST /api/mail/admin/:serverId/mailboxes

Prop

Type

PATCH /api/mail/admin/:serverId/mailboxes/:email accepts name, password, quotaMB, and active (all optional). DELETE soft-deletes by default; add ?hard=true to purge the maildir.

curl -X POST https://your-host/api/mail/admin/srv_123/mailboxes \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"localPart":"alice","domain":"example.com","password":"a-strong-password"}'

Configure backups

Plugs the mail server into the general backup system — pick a destination and what to capture. Requires an existing backup destination in your organization.

POST /api/mail/admin/:serverId/backup-policy

Prop

Type

Send a test email

POST /api/mail/admin/:serverId/test-email

Prop

Type

Deploy webmail

Creates (or reuses) a webmail-<serverId> project, queues a deployment, and starts the build. Returns { deploymentId, projectId } so the dashboard can redirect to /build/[deploymentId].

POST /api/mail/webmail/deploy-project

Prop

Type

target is one of:

  • { "kind": "self", "serverId": "srv_123" } — self-hosted on one of your Alaf Server servers.
  • { "kind": "cloud" } — managed by Alaf Server Cloud.
curl -X POST https://your-host/api/mail/webmail/deploy-project \
  -H "Authorization: Bearer $ALAFSERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mailServerId":"srv_123","hostname":"mail.example.com","target":{"kind":"self","serverId":"srv_123"}}'

Errors you might see

404 — Not available

You're calling a cloud instance. The Mail API is self-hosted only; run it on an instance where CLOUD_MODE is off.

404 — Server not found

The :serverId doesn't belong to your organization (or doesn't exist). List your mail servers with GET /api/mail/servers to get valid IDs.

409 — Setup already running

Only one install runs per instance at a time, and per-server writes (reset, postmaster rotation) are refused while its setup is active. Wait for it to finish, or cancel it with POST /api/mail/setup/cancel.

502 — Scan / adopt failed

Alaf Server couldn't reach the server over SSH, or the probe threw. Check the server is online and its SSH credentials are valid, then retry.

On this page