Skip to content

Platform Email

The platform mailer sends transactional mail tied to accounts: invitations, password resets, and email-change confirmations. It is separate from Notification Channels, which deliver event notifications such as deployment results. The two have independent configuration — configuring one does not configure the other.

Until the mailer is configured, account emails cannot be delivered. Nothing is lost: messages queue and drain automatically once a working transport is saved.

Set the SMTP transport in the UI under Administration → Mailer, or via PUT /api/v1/admin/mailer/settings. Both require the mailer:manage permission (mailer:read to view).

FieldNotes
enabledMaster switch. When off, mail queues rather than sends
smtp_host, smtp_portSubmission endpoint, typically port 587
smtp_use_tlsWith port 465 this means implicit TLS; on any other port, STARTTLS
smtp_username, smtp_passwordOptional. Setting a username without a password is rejected
smtp_from_address, smtp_from_nameEnvelope sender and display name

The password is encrypted before storage and is never echoed back. To remove it, send clear_smtp_password: true rather than an empty string.

Many providers require the sender address to match the authenticated account.

POST /api/v1/admin/mailer/test sends a message to the configured sender address and reports the result. This send is synchronous — it exercises the SMTP transport but not the queue, so a passing test confirms credentials and connectivity rather than the full delivery path. The first real invitation exercises the rest.

Failure detail is scrubbed of secrets before it reaches the client.

Messages are written to an outbox table and delivered by a background worker, so an SMTP outage never fails a user-facing request.

  • The worker polls every 5 seconds; each attempt has a 30-second timeout.
  • Permanent failures (a rejected recipient) stop immediately — no retries.
  • Transient failures (timeouts, connection refused, 4xx) retry with exponential backoff and jitter: 30 seconds initially, doubling to a 1-hour ceiling, up to 5 attempts before the message is marked failed.
  • An unconfigured or disabled mailer counts as transient, which is what allows queued mail to drain once you save a working transport.

Jitter matters when several instances share a database: without it, a mail-server outage would produce a synchronised retry storm on recovery.

New accounts are not active. A created user is pending_verification and cannot log in until they follow their invitation link and set a password.

StatusMeaning
pending_verificationInvited; waiting on the user to activate
pending_approvalProvisioned by an identity provider that requires admin approval
activeCan log in
inactiveDisabled by an administrator
lockedToo many failed logins

Token lifetimes, all single-use:

TokenExpires
Activation (invitation)7 days
Password reset60 minutes
Email change confirmation60 minutes

POST /api/v1/admin/users/{id}/resend-invite issues a fresh invitation and invalidates the previous one. It is rate-limited to one per minute per account so it cannot be used to flood a mailbox.

Each identity provider chooses how accounts it provisions are created:

  • Auto-activate (default) — the account is created active. Because an account with no default role holds no permissions, it can sign in but do nothing until an administrator grants a role.
  • Require approval — the account is created pending_approval and cannot log in until an administrator approves it via POST /api/v1/admin/users/{id}/approve. Appropriate where directory membership does not imply entitlement.

Transactional mail is easy to get quarantined. Publish SPF, DKIM, and DMARC records for the sending domain, and prefer a submission relay that signs your mail over sending directly from the application host — many hosting providers block outbound port 25 and their address ranges carry poor reputation.

A DMARC policy with no rua reporting address will silently quarantine mail with no visibility. Include one.

Mail is queued but never arrives. Check that enabled is on and run the test endpoint. Queued messages drain on their own once the transport works — no manual replay is needed.

Test passes but users report nothing. The test only proves the transport. Look for messages marked failed after exhausting retries, and check the recipient domain’s spam placement before suspecting the platform.

Invitations expire before use. The activation window is 7 days; use resend-invite rather than recreating the account, which would orphan the original token.