Email Delivery & Team Invites
How Hyperswitch sends auth emails (team invites, signup verification, magic links, password reset), how to wire any SMTP provider, and how to invite teammates — from zero to working in production.
What Email Enables
Without an email provider, the Control Center still works — but team onboarding is manual: an admin invites a teammate and the router generates a password that gets downloaded as a file to share out of band. Configuring email switches the flow to fully self-service:
- Team invites — admin adds a teammate's email + role, the router emails an invite link they click to set their password.
- Signup verification & magic links — email-based login/verification.
- Password reset — users reset their own passwords.
Pick Any SMTP Provider
Hyperswitch only needs standard SMTP credentials — so any provider works. Same config shape, different values:
| Provider | SMTP host | Port | username | password |
|---|---|---|---|---|
| Resend ↗ | smtp.resend.com | 587 | your Resend API key | your Resend API key |
| Amazon SES ↗ | email-smtp.<region>.amazonaws.com | 587 | SES SMTP username | SES SMTP password |
| Postmark ↗ | smtp.postmarkapp.com | 587 | Postmark SMTP token | any value |
| Brevo ↗ | smtp-relay.brevo.com | 587 | Brevo SMTP login | Brevo SMTP key |
| SendGrid ↗ | smtp.sendgrid.net | 587 | apikey | SendGrid API key |
| Mailchimp (Mandrill) ↗ | smtp.mandrillapp.com | 587 | your Mandrill SMTP username | your Mandrill API key |
| Mailgun ↗ | smtp.mailgun.org | 587 | postmaster@yourdomain.com | Mailgun SMTP password |
Every provider requires verifying a domain you own (DNS records: SPF, DKIM, optionally DMARC) before it will send. This is done in the provider's dashboard — the sender address you configure below must sit on that verified domain.
Configure It in One Place
Edit the root .env — the Docker Compose file already maps these into the Hyperswitch router. For local development, start the bundled MailHog (catches every email in a web UI at http://localhost:8025):
# Dev (MailHog): catches emails locally, no provider needed
EMAIL_ACTIVE_CLIENT=SMTP
EMAIL_SENDER_EMAIL=no-reply@yourdomain.com
EMAIL_SMTP_HOST=mailhog
EMAIL_SMTP_PORT=1025
EMAIL_SMTP_CONNECTION=plaintext
EMAIL_SMTP_TIMEOUT=10
# MailHog accepts ANY credentials, but they must NOT be empty
# (an empty username/password crashes the router at startup)
EMAIL_SMTP_USERNAME=mailhog
EMAIL_SMTP_PASSWORD=mailhog
# PRODUCTION (Resend example) — uncomment & fill:
# EMAIL_SMTP_HOST=smtp.resend.com
# EMAIL_SMTP_PORT=587
# EMAIL_SMTP_CONNECTION=start_tls
# EMAIL_SMTP_USERNAME=re_xxxxxxxxxxxxxxxxxx
# EMAIL_SMTP_PASSWORD=re_xxxxxxxxxxxxxxxxxx
# Public URLs (MUST be public in production, not localhost)
HYPERSWITCH_DASHBOARD_URL=https://dashboard.yourdomain.com
HYPERSWITCH_PUBLIC_API_URL=https://api.yourdomain.com
# Turn on email-based team invites in the Control Center UI
HYPER_EMAIL_ENABLED=trueCase-sensitive: EMAIL_SMTP_CONNECTION must be the literal plaintext (dev/MailHog) or start_tls (production) — not "starttls".
Required: EMAIL_SMTP_USERNAME and EMAIL_SMTP_PASSWORD must be non-empty. Even MailHog (which ignores credentials) will crash the router if they are blank — use any placeholder value like mailhog / mailhog.
⚠️ The “Invalid Link or session expired” gotchas:
- Empty database. The email-enabled router image needs ~493 tables. If you never ran
make migrate-db, the DB has zero tables and every login/signup fails with this exact error. Run the migration once and it stays fixed. - Wrong image. The
juspaydotin/hyperswitch-router:standaloneimage has the email feature compiled out (email routes 404; no email is ever sent). Use the full build, e.g.juspaydotin/hyperswitch-router:v1.125.0. - Localhost link. If
HYPERSWITCH_DASHBOARD_URLis left aslocalhost, every email link points atlocalhost— unreachable from any inbox. Set it to the public URL in production.
Apply the Change
# 1. Start the database first (the migration script needs core-postgres up):
docker compose up -d postgres
# 2. First run only: apply the Hyperswitch DB migrations.
# The email-enabled router image needs ~493 tables (users, merchant_account, ...).
# Without them every login fails with "Invalid Link or session expired".
./scripts/migrate-hyperswitch-db.sh # or: make migrate-db (if make is installed)
# 3. Dev: start MailHog + the full stack
docker compose --profile core --profile dev up -d
# Production: no MailHog — just restart the router & control center
docker compose up -d --force-recreate hyperswitch hyperswitch-control-center
# See MailHog's caught emails at http://localhost:8025How to Invite a Teammate
- Open the Control Center at http://localhost:9000 and sign in as the org admin (the first account created).
- Go to Settings → Team → Invite New Users.
- Enter the teammate's email and assign a role (Organization Admin, Merchant Developer, View-Only, or a custom role).
- Submit. With email configured, the teammate receives an invite email with a link. They click it, set a password, and are in.
- Without email configured, the router instead lets you download the credentials file (email + generated password) to share manually — same outcome, less self-service.
Team Login Options
- Password — email + password (set during invite acceptance).
- Magic Link — email a one-time login link (requires email config).
- SSO / OIDC — Google/GitHub/Okta via the router's
[oidc]config. - 2FA (TOTP) — authenticator-app codes, optional per deployment (
force_two_factor_auth).
Full Email Env Reference
| Env var | Maps to | Purpose |
|---|---|---|
EMAIL_ACTIVE_CLIENT | ROUTER__EMAIL__ACTIVE_EMAIL_CLIENT | SMTP or SES |
EMAIL_SENDER_EMAIL | ROUTER__EMAIL__SENDER_EMAIL | From: address (must be on the verified domain) |
EMAIL_SMTP_HOST | ROUTER__EMAIL__SMTP__HOST | Provider SMTP host |
EMAIL_SMTP_PORT | ROUTER__EMAIL__SMTP__PORT | Usually 587 (start_tls) or 1025 (MailHog) |
EMAIL_SMTP_CONNECTION | ROUTER__EMAIL__SMTP__CONNECTION | plaintext (dev) or start_tls (production) |
EMAIL_SMTP_TIMEOUT | ROUTER__EMAIL__SMTP__TIMEOUT | Seconds before timeout (default 10) |
EMAIL_SMTP_USERNAME | ROUTER__EMAIL__SMTP__USERNAME | Provider SMTP username/API key |
EMAIL_SMTP_PASSWORD | ROUTER__EMAIL__SMTP__PASSWORD | Provider SMTP password/API key |
HYPERSWITCH_DASHBOARD_URL | ROUTER__USER__BASE_URL | Public dashboard URL baked into email links |
HYPERSWITCH_PUBLIC_API_URL | default__config__api_url / sdk_url (Control Center) | Public router URL the Control Center calls from the browser |
HYPER_EMAIL_ENABLED | default__features__email (Control Center) | true = email-based invites in the UI |