For the complete documentation index, see llms-docs.txt or the site index llms.txt. Full docs corpus: llms-full-docs.txt. Prefer the markdown version of this page at /docs/setup/backend/email.md. Product capabilities: skill.md. Docs MCP: /docs/mcp. Site MCP: /mcp.

Email

Set up, configure, and run the Reloop platform email microservice locally.

Platform transactional emails triggered by NATS events (invites, OTPs, billing alerts).

Overview

PropertyValue
Directoryapps/backend/email
Port8022
Local URLhttps://local.reloop.sh/api/email
Swagger UIhttps://local.reloop.sh/api/email/openapi
StackElysiaJS · Redis · NATS

Quick start

bun be:email:dev

Or bun backend:dev / bun dev. In development, when neither RELOOP_API_KEY nor SMTP_HOST is set, mail falls back to Mailpit SMTP (localhost:1025). That fallback is development-only: see How system email is delivered.

Environment

apps/backend/email/.env (from bun setup / bun env:setup).

EMAIL_PORT=8022
PORT=8022
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
RELOOP_API_KEY=
RELOOP_SENDER_DOMAIN=
ONBOARDING_TEST_DOMAIN=
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_SECURE=false
NODE_ENV=development
VariableRequiredDefaultNotes
EMAIL_PORT / PORTYES8022Listening port
BASE_URLYEShttps://local.reloop.shPlatform base URL
NATS_URLYESnats://localhost:4222Event subscriptions
RELOOP_API_KEYOne of-API key for the org that owns the sender domain(s); used by reloop-email
RELOOP_SENDER_DOMAINWith API key-System product mail From domain (auth, billing, invites)
ONBOARDING_TEST_DOMAINProd-Onboarding “Send email” From domain only (can differ)
SMTP_HOSTOne of-Relay system mail through your own SMTP server instead of Reloop
SMTP_PORTNo587Use 465 with SMTP_SECURE=true
SMTP_USERNo-Omit for an unauthenticated relay
SMTP_PASSWORDNo-Paired with SMTP_USER
SMTP_SECURENofalsetrue for implicit TLS
MAILPIT_HOSTNolocalhostDevelopment capture host
MAILPIT_PORTNo1025Development capture port
APP_NAMENoReloopCompany name shown as Self-hosted Reloop × <APP_NAME> in system email senders, subjects and bodies

How system email is delivered

System email is anything Reloop sends to your own users: sign-in codes, invitations, billing alerts. It is separate from customer mail, which always goes through mail.

The service picks a transport at startup, in this order:

ConditionTransport
RELOOP_API_KEY is setReloop API, sending from RELOOP_SENDER_DOMAIN
otherwise SMTP_HOST is setThat SMTP server directly
otherwise, NODE_ENV is not productionMailpit on MAILPIT_HOST:MAILPIT_PORT
otherwiseNothing. The send fails and logs why.

The chosen transport is logged once on boot, so reloop logs email tells you which one is live:

System email transport: SMTP smtp.acme.test:587 (authenticated)

Choosing a transport

Through Reloop. Best once you have a verified sending domain. Create an API key in the organization that owns it, then set RELOOP_API_KEY and RELOOP_SENDER_DOMAIN. Your platform mail then gets the same delivery pipeline, tracking and logs as customer mail.

Through your own SMTP server. Set SMTP_HOST, plus SMTP_USER and SMTP_PASSWORD if it requires auth. Useful on day one, before any domain is verified, and for shops that already relay everything through Postmark, SES or an internal server. Leave RELOOP_API_KEY empty.

SMTP_HOST=smtp.postmarkapp.com
SMTP_PORT=587
SMTP_USER=your-token
SMTP_PASSWORD=your-token

Signing in before email works

A fresh install has no sending domain, so it cannot email you a code to log in and verify one. Self-host breaks that loop at /dashboard/setup: the installer prints a one-time setup key, and the wizard creates the first administrator with a password, so no email is needed. See VPS install.

DEFAULT_OTP is a development helper and is left empty on new installations. Installations upgraded from an older installer keep the fixed code they were given; remove it and restart once a transport above works.

Branding system email

APP_NAME sets the name used across every system email: the sender name, the subject line and the message body. Any value other than Reloop is shown as Self-hosted Reloop × <APP_NAME>, so mail carries both your name and the fact that it runs on Reloop.

APP_NAME=Contoso Mail
EmailSender with APP_NAME=Contoso Mail
Login codeSelf-hosted Reloop × Contoso Mail <auth@…>
New sign-inSelf-hosted Reloop × Contoso Mail Security <security@…>
BillingSelf-hosted Reloop × Contoso Mail Billing <billing@…>
Quota warningSelf-hosted Reloop × Contoso Mail Support <support@…>
InvitationDana via Self-hosted Reloop × Contoso Mail <invites@…>

Leave it unset, or set it to Reloop, and everything stays plain Reloop, so existing deployments are unaffected. The installer defaults it to Reloop and preserves your value across re-runs.

The sign-in alert previously sent as Security Alert, which was the only sender that did not carry the product name. It is now <APP_NAME> Security, matching the Billing and Support senders.

[!NOTE] The email logo is loaded from BASE_URL, not from Reloop, so replace that asset in your own deployment if you rebrand.

[!NOTE] The Reloop attribution cannot be configured away. The shared footer also keeps Reloop's help link, copyright line and postal address regardless of APP_NAME.

Commands

CommandDescription
bun be:email:devStart dev server with hot reloading
bun run --filter=be-email buildCompile production bundle
bun run --filter=be-email startRun compiled production build
bun run --filter=be-email check-typesTypeScript type-check

Architecture

LayerDetail
TriggersSubscribes to NATS subjects (auth.*, billing.*, etc.)
DeliverySends via the mail pipeline using RELOOP_API_KEY when set
Schemaemail_log in packages/db/src/schema/email.ts

Next

Was this page helpful?

Edit this page