Tenant Email Settings
Configure per-tenant email branding for invite and notification messages. Platform SMTP sends mail; tenants customize display name, reply-to, and logo.
What are tenant email settings
Email settings control outbound email behavior for a tenant—notification toggles, reply-to, and locale. Display name and logo come from org branding (Tenant.branding); this endpoint merges branding into the response as fromName and logoUrl.
The platform sends mail via shared SMTP (or a provider configured in server env vars). Tenants customize branding in v1. For custom SMTP delivery, see Email Transport.
HTML and text bodies are maintained as Handlebars templates under src/email/templates/ (one folder per template id, e.g. user.invited/). Shared layout and partials live in layouts/ and partials/.
Setting fields
| Field | Description |
|---|---|
| enabled | Master switch for outbound email for this tenant (default: true) |
| fromName | Display name in the From header (sourced from org branding; prefer PATCH /tenants/:id/branding) |
| replyTo | Reply-To address for recipient responses |
| logoUrl | Optional logo URL in HTML templates (sourced from org branding; prefer PATCH /tenants/:id/branding) |
| locale | Template language hint (default: en) |
| notifications.userInvites | Send invite and assignment emails (default: true) |
| notifications.syncFailures | Reserved for future sync-failure alerts |
| notifications.jobFailures | Reserved for future job-failure alerts |
Invite flow
- Admin calls
POST /users(orPOST /users/:id/resend-invite). - Service creates a one-time invite token (48h TTL by default) and queues
user.invitedemail. - Recipient opens
${APP_PUBLIC_URL}/accept-invite?token=...in the SPA. - SPA calls
POST /auth/invite/verify, thenPOST /auth/invite/acceptwith a new password. - User signs in via Keycloak.
Assigning an existing user (POST /tenants/:tenantId/users) sends user.tenant_assigned with a login link—no password reset.
Operations
| Operation | Method | Endpoint |
|---|---|---|
| Get branding | GET | /tenants/:id/branding |
| Update branding | PATCH | /tenants/:id/branding |
| Get email settings | GET | /tenants/:id/email-settings |
| Update email settings | PATCH | /tenants/:id/email-settings |
| Send test email | POST | /tenants/:id/email-settings/test |
| Get email transport | GET | /tenants/:id/email-settings/transport |
| Update email transport | PATCH | /tenants/:id/email-settings/transport |
| Test SMTP connection | POST | /tenants/:id/email-settings/transport/test |
Environment variables (platform)
| Variable | Purpose |
|---|---|
APP_PUBLIC_URL | Base URL for invite links and login (SPA) |
EMAIL_ENABLED | Global kill switch (true / false) |
SMTP_HOST, SMTP_PORT | SMTP server (use Mailpit localhost:1025 in dev) |
SMTP_SECURE | Use implicit TLS. Usually true for port 465, false for 587/Mailpit |
SMTP_REQUIRE_TLS | Require STARTTLS. Usually true for port 587 real providers |
SMTP_USER, SMTP_PASSWORD | Optional SMTP credentials for real providers |
EMAIL_FROM, EMAIL_FROM_NAME | Default sender when tenant has no fromName |
INVITE_TOKEN_TTL_HOURS | Invite link validity (default: 48) |
TENANT_SMTP_ENCRYPTION_KEY | Min 32 chars; encrypts per-tenant SMTP passwords (required for custom SMTP) |
Local vs real delivery
For local development, use Mailpit:
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false
SMTP_REQUIRE_TLS=false
Mailpit captures messages in its UI (http://localhost:8025) and does not deliver to real inboxes.
For staging or production, point the app at a real SMTP relay:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_REQUIRE_TLS=true
SMTP_USER=your-smtp-user
SMTP_PASSWORD=your-smtp-password
EMAIL_FROM=noreply@your-domain.example
Port 465 providers usually require SMTP_SECURE=true instead.
Related
- Branding — Org display name and logo
- Email Transport — Per-tenant SMTP delivery
- Users — User provisioning and invites
- Managing Tenants — Tenant lifecycle
- Infrastructure — Mailpit and SMTP in Docker