Skip to main content

Email Configuration

Flo has two independent email paths, each with its own provider configuration:

PathUsed forProvider selection
TransactionalActivation, OTP, password recovery, booking and subscription notifications, e-invoice forwardingEMAIL_SENDER_EMAIL_PROVIDER environment variable
BulkNewsletters and campaigns (send queue)NewsletterSettings.BulkProvider in the database (Communication > settings), with the env default as fallback

Transactional sending is gated by the enable_email_sender feature flag (see Feature Flags).

Transactional Providers​

Cloudflare Email (API-based)​

Sends through the Cloudflare Email Service API, not SMTP. Requires:

EMAIL_SENDER_EMAIL_PROVIDER=Cloudflare
CF_EMAIL_ACCOUNT_ID=your-cloudflare-account-id
CF_EMAIL_API_TOKEN=your-cloudflare-api-token
EMAIL_SENDER_EMAIL_ALIAS=newsletter@yourdomain.com

The token is verified against /user/tokens/verify during the provider probe; it must be active. The same credentials are reused by the Cloudflare bulk sender.

Gmail with Alias​

Use Gmail's "Send As" feature to send from a custom domain.

Prerequisites:

  • 2FA enabled on the Gmail account
  • App Password generated (not the regular password)
  • Gmail "Send As" alias configured and verified
EMAIL_SENDER_EMAIL_PROVIDER=Gmail
EMAIL_SENDER_EMAIL_USERNAME=info@gmail.com
EMAIL_SENDER_EMAIL_ALIAS=newsletter@yourdomain.com
EMAIL_SENDER_EMAIL_PSW=your-gmail-app-password

Setting up Gmail "Send As":

  1. Gmail Settings > Accounts and Import > "Send mail as" > Add another email address
  2. Enter the alias email, uncheck "Treat as an alias"
  3. Complete email or DNS verification
  4. Optionally set as default sender

Outlook​

Named provider preconfigured in appsettings.json (smtp.office365.com:587). Set EMAIL_SENDER_EMAIL_PROVIDER=Outlook plus username/password/alias.

Custom SMTP (BYO relay)​

When EMAIL_SENDER_SMTP_HOST is set, the provider is built directly from environment variables instead of the named list. This is how Amazon SES, Mailgun, Postmark or any custom relay is configured per tenant:

EMAIL_SENDER_EMAIL_PROVIDER=Smtp
EMAIL_SENDER_SMTP_HOST=smtp.example.com
EMAIL_SENDER_SMTP_PORT=587
EMAIL_SENDER_SMTP_SSL=false
EMAIL_SENDER_SMTP_REQUIRE_TLS=true
EMAIL_SENDER_EMAIL_USERNAME=your-username
EMAIL_SENDER_EMAIL_ALIAS=sender@yourdomain.com
EMAIL_SENDER_EMAIL_PSW=your-password

EMAIL_SENDER_SMTP_REQUIRE_TLS is opt-out: TLS is required unless explicitly set to false (for an internal relay or a mail catcher).

In development, if EMAIL_SENDER_EMAIL_PROVIDER is empty the backend runs in console mode: messages are logged instead of sent.

Bulk (Newsletter) Providers​

The newsletter send queue drains through one of four senders. The operator picks the provider in the Communication settings UI; the resolver reads the DB choice at drain time and falls back to the env default when the selected provider's credentials are missing.

ProviderCredentialsNotes
Defaultnone extraRides the transactional SMTP dispatcher; no metered tier
CloudflareCF_EMAIL_ACCOUNT_ID, CF_EMAIL_API_TOKENCloudflare Email Service bulk API
Amazon SESSES_REGION, SES_ACCESS_KEY_ID, SES_SECRET_ACCESS_KEY (optional SES_CONFIGURATION_SET)SES v2; daily-quota exhaustion parks the job until the next UTC midnight
SweegoSWEEGO_API_KEYEuropean provider; one message per recipient via POST https://api.sweego.io/send

Additional variables:

  • BULK_EMAIL_SENDER_ALIAS — from-address used only by the bulk queue (falls back to EMAIL_SENDER_EMAIL_ALIAS). Needed when the bulk provider verifies a different domain than the transactional transport.
  • EMAIL_SENDER_EMAIL_PROVIDER=cloudflare keeps the historical behaviour of defaulting the queue to the Cloudflare bulk sender.

All bulk senders honour the test-email sink (Communication settings): when the recipient allow-list is non-empty, every message is redirected to those addresses only.

Email Templates​

Templates are MJML files rendered through Handlebars. They are brand-specific folders under Flo.BE/Services/EmailSender/Templates/:

FolderBrand
flo/Default Flo brand (EMAIL_TEMPLATE_BRAND unset)
pf/Pietro Franceschini
rs/RespiraStudio

Each transactional template ships in Italian and English (*.it.mjml, *.en.mjml); newsletters also have a *.neutral.mjml variant. Shared header/footer/css partials are inlined at render time.

Coverage includes (flo folder): activation, email-confirmation, password-recovery, otp-verification, booking-confirmation, booking-cancellation, booking-cancellation-closure, subscription-purchase, subscription-reminder, client-subscription-created/suspended/reactivated/cancelled, commerce-payment-failed, commerce-order-refunded, einvoice-forward, immobile-access-confirmation, newsletter-confirmation, newsletter-double-opt-in, blog-newsletter, real-estate-newsletter, element-newsletter, custom-newsletter.

Brand variables resolved at render time: AppUrl, FEAppUrl, CustomerNameShort (uppercased for the logo), CustomerNameFull, year, and the legal links below.

VariableEffect
EMAIL_LEGAL_BASE_URLBase for /privacy-policy and /terms-conditions links (default FEAppUrl)
EMAIL_PRIVACY_POLICY_URLFull privacy-policy URL override
EMAIL_TERMS_URLFull terms URL override

Delivery Log & Replay​

Every transactional and bulk send writes an EmailDeliveryLog row (provider, channel, status, from/to, subject, error, optional MIME payload). Rows are pruned after 180 days.

  • Viewer: Settings > Sent emails, SuperAdmin-only (GET /api/v1/email-delivery-logs). The grid merges the local log with live Cloudflare analytics (dataset emailSendingAdaptive, 31-day retention) when CF_EMAIL_ZONE_ID and an analytics token are configured; without them it still runs on the local log.
  • Replay: POST /api/v1/maintenance/email/replay-failed, authenticated with the X-Maintenance-Key header (Maintenance__ApiKey). It replays failed transactional messages:
    • exact mode re-dispatches the stored MIME payload with a new Message-Id;
    • legacy mode reconstructs messages that predate payload storage (password recovery, password creation, email confirmation) from current data, minting a new token only at send time.
    • Filters: from/to, recipients, userIds, limit; dryRun reports what would be replayed; requires enable_email_sender to actually send.

Testing​

Verify Configuration on VPS​

# Check environment variables
sudo docker exec flo env | grep EMAIL

# Restart container after changes
sudo docker restart flo

# Monitor logs for email sending
sudo docker logs -f flo

Expected Email Headers​

From: newsletter@yourdomain.com
Reply-To: newsletter@yourdomain.com

Troubleshooting​

IssueCauseFix
Emails show Gmail address as senderGmail alias not verifiedComplete "Send As" verification
SMTP authentication failedWrong app password or 2FA not enabledGenerate a new app password with 2FA
Container ignores new env varsEnv loaded at container startRestart: sudo docker restart flo
Newsletter falls back to another providerSelected bulk provider's credentials are missingSet the provider's env vars or re-select in the UI
Bulk job parkedProvider quota exhausted (429/402)Wait for the next UTC midnight; check the provider plan
Delivery log has no live Cloudflare rowsNo CF_EMAIL_ZONE_ID / analytics token, or token lacks Account Analytics: ReadConfigure the analytics token; the local log still works
Replay answers 503Replay service unavailable (no email provider resolvable)Configure a transactional provider
Replay refuses to sendenable_email_sender is offEnable the flag (it returns errors.emailDeliveryLog.replayEmailDisabled)

Code Reference​

FilePurpose
Flo.BE/Services/EmailSender/EmailSenderService.csTransactional sending, template rendering, delivery logging
Flo.BE/Services/EmailSender/Core/EmailProviderResolver.csProvider selection (named, API-based, BYO SMTP, console)
Flo.BE/Services/EmailSender/Core/CredentialsProvider.csCredentials and alias resolution
Flo.BE/Services/EmailSender/Core/CloudflareEmailDispatcher.csCloudflare Email Service API dispatcher
Flo.BE/Services/EmailSender/Bulk/BulkEmailSenderResolver.csDB-backed bulk provider resolution
Flo.BE/Services/EmailSender/Bulk/*Cloudflare / SES / Sweego / legacy bulk senders
Flo.BE/Services/EmailSender/Core/EmailReplayService.csFailed-message replay
Flo.BE/Services/EmailSender/Core/EmailTemplateRenderer.csMJML + Handlebars rendering, brand folders, legal links
Flo.BE/Services/EmailSender/Templates/{flo,pf,rs}/Brand template sets