Skip to content

Feat: Add generic SMTP support for transactional emails #1766

@julianwitzel

Description

@julianwitzel

Problem

Self-hosted Cap deployments are currently locked into Resend as the only email provider. The relevant code path is in packages/database/emails/config.ts, where sendEmail() is wired directly to the Resend SDK with no abstraction.

For self-hosters, this creates real friction:

  • We're forced to add yet another third-party SaaS account just for transactional emails, even when we already operate our own SMTP infrastructure (e.g. Postal, Plunk, Listmonk, AWS SES, or a basic mail relay)
  • It contradicts the spirit of self-hosting — moving data ownership away from cloud services
  • Without Resend configured, magic links and team invites only appear in container logs (sendVerificationRequest fallback), which makes onboarding teammates impractical
  • The current accept-invite flow effectively requires email delivery to work end-to-end

Suggested Solution

Add a generic SMTP provider as a first-class alternative. Detection could be as simple as: if SMTP_HOST is set, use SMTP via nodemailer; otherwise fall back to Resend (or logs).

SMTP_HOST=mail.example.com
SMTP_PORT=587
SMTP_USER=...
SMTP_PASSWORD=...
SMTP_FROM=auth@example.com
SMTP_SECURE=false  # or true for implicit TLS

The React Email rendering pipeline stays untouched — only the transport layer changes. nodemailer is the de-facto standard in the Node ecosystem for this and integrates cleanly with @react-email/render.

Why this matters

Cap is a fantastic open-source alternative to Loom, and the self-hosting story is a big part of its appeal. SMTP support would close one of the last gaps that currently force users into specific external SaaS providers — which is exactly the lock-in self-hosters try to avoid.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions