Skip to content

Email Delivery

Audience: Operators configuring LMS email delivery and support engineers diagnosing missing transactional email.

Scope: Provider selection, environment setup, production readiness, local testing, and troubleshooting. Architecture and event ownership live in Email System.

Delivery Modes

The LMS selects one delivery driver at process startup:

  1. RESEND_API_KEY present: Resend.
  2. Otherwise, SMTP_HOST present: SMTP.
  3. Otherwise, when NODE_ENV is not production: console.
  4. Otherwise: startup/send configuration fails with EmailNotConfiguredError.

Resend always takes precedence over SMTP. Restart or redeploy the LMS after changing email environment variables because the selected client is cached in-process.

Environment Variables

Variable Required Purpose
EMAIL_FROM Yes for external delivery Sender mailbox, using a provider-authorized domain
RESEND_API_KEY For Resend Resend API credential
SMTP_HOST For SMTP SMTP server hostname
SMTP_PORT For SMTP when required by server SMTP port
SMTP_USER For authenticated SMTP SMTP username
SMTP_PASSWORD For authenticated SMTP SMTP password
APP_BASE_DOMAIN Yes Base host used for tenant routing and links

These values are server-only. Never prefix them with NEXT_PUBLIC_, log credentials, or commit .env.local.

Resend Setup

  1. Add and verify the sending domain in the Resend account.
  2. Publish the DNS records Resend requests and wait for verification.
  3. Create an API key for the intended deployment environment.
  4. Set EMAIL_FROM to a mailbox on the verified domain.
  5. Set RESEND_API_KEY and redeploy.
  6. Trigger a transactional flow on the correct tenant host.
  7. Confirm an email/sent structured log, then use its messageId in the Resend dashboard.

An API key and sender domain must belong to the same Resend account. A generic or unverified sender domain is rejected even when the key itself is valid.

SMTP Setup

  1. Leave RESEND_API_KEY unset; otherwise Resend wins provider selection.
  2. Set SMTP_HOST and the server's required port and credentials.
  3. Set EMAIL_FROM to an address the SMTP server permits.
  4. Restart or redeploy the LMS.
  5. Trigger a transactional flow and inspect email/sent or email/failed.

TLS and relay behavior are controlled by the SMTP service configuration. Validate production requirements with the selected service before launch.

Local Development

For token-flow development, leave RESEND_API_KEY and SMTP_HOST unset. The non-production console driver logs:

  • the full recipient address;
  • subject and tags;
  • links extracted from the rendered HTML and plaintext.

This output is intentionally more sensitive than normal structured email logs. Use it only in a local development environment and do not paste token links into tickets or shared logs.

To test rendered mail in a local inbox UI, use a local SMTP service such as Mailpit and configure SMTP_HOST/SMTP_PORT while leaving RESEND_API_KEY unset.

Production Readiness Checklist

  • EMAIL_FROM is authorized by the selected provider.
  • At least one external provider is configured; production cannot fall back to console.
  • Resend and SMTP credentials are environment-specific and stored as secrets.
  • Tenant auth links use the expected public host and HTTPS.
  • Sign-up verification, forgot password, resend verification, magic link, email change, and admin invitation/reset flows have been smoke-tested.
  • Invitation links resolve to /invite/\[token\].
  • email/sent and email/failed logs are collected without exposing recipient addresses or tokens.
  • DNS authentication required by the provider is active.

Provider webhooks, bounce suppression, unsubscribe handling, and dedicated deliverability alerts are not implemented. Track those gaps in T-026 Email Platform Roadmap.

Troubleshooting

The UI says success, but no message arrives

Account-discovery flows deliberately use enumeration-safe responses. A successful response means the request was accepted, not that an account was found or a provider accepted a message.

Check in order:

  1. Tenant host: submit on the tenant host that owns the account. Unknown, suspended, apex, or different-tenant hosts may not be eligible.
  2. Account eligibility: the normalized address must match the tenant user. Forgot-password mail also requires a local password; OAuth-only users use their OAuth provider.
  3. Rate limit: repeated auth requests may be refused before delivery.
  4. Selected driver: a non-empty RESEND_API_KEY overrides SMTP and console.
  5. Sender authorization: confirm EMAIL_FROM is accepted by the selected provider.
  6. Structured logs: find email/sent or email/failed.
  7. Provider state: when email/sent includes a messageId, inspect that ID in the provider dashboard.
  8. Inbox behavior: check spam/quarantine and provider-level suppression.

There is no email log

  • Confirm the request reached an implemented email event.
  • Confirm tenant and user eligibility.
  • Check rate-limit logs.
  • Check for an earlier action error.

The LMS does not write email_events records yet, so database inspection cannot currently prove whether a send was attempted.

EmailNotConfiguredError in production

Neither RESEND_API_KEY nor SMTP_HOST is configured. Set one provider and EMAIL_FROM, then redeploy. Production intentionally has no console fallback.

Resend rejects the sender

Confirm the exact domain in EMAIL_FROM is verified in the same Resend account as the active API key. After correcting environment values, redeploy before retesting.

SMTP is not selected

Unset RESEND_API_KEY, restart the process, and verify SMTP_HOST is non-empty. Provider selection is cached.

Logging And Data Handling

Normal send logs contain only the recipient domain, not the full address. They may include the subject, event tags, provider-neutral failure detail, and provider message ID. Keep subjects free of secrets and personal data.

The console driver is the exception: it logs full recipients and token-bearing links for local testing. It is unavailable as an automatic production fallback.