Skip to content
BoringStack
Star

Cloudflare Email Service

3 min read

The apps/api ships Cloudflare Email Service as the default outbound mail provider. It’s bundled into Workers Paid with no documented per-message charge, making it usually the cheapest transactional path at non-trivial volume among the providers wired into this template.

ProviderCost DynamicsSetup PathPreconditions
Cloudflare EmailWorkers Paid bundle ($5/mo flat; zero per-message cost)Auto-provisioned SPF/DKIM/DMARCDNS zone must be managed on Cloudflare
ResendFree tier, then pay-per-message usageManual DNS TXT records validationNone (vendor-neutral API key)
SendGridFree tier, then pay-per-message usageManual DNS TXT records validationNone (vendor-neutral API key)

Pricing tiers, free-tier limits, and per-message rates evolve. See Cloudflare, Resend, and SendGrid for current numbers.

If you’re already paying for Workers, Cloudflare Email has no per-message cost. BoringStack defaults to it because the marginal cost is zero.

The API app’s email layer is pluggable: one IEmailService interface, five concrete implementations (Cloudflare, Resend, SendGrid, SMTP, noop). The Cloudflare provider POSTs to an account-scoped endpoint on api.cloudflare.com with a bearer token. Retries, structured logging, and the shared retryWithBackoff wrapper apply equally to all providers; Cloudflare gets the same reliability treatment as Resend. For local template iteration, set EMAIL_PROVIDER=smtp with SMTP_HOST=mailpit and the Mailpit overlay catches everything for inspection.

See Email for the abstraction shape and how dispatch works.

By default, dev with no Cloudflare keys uses the noop provider, which logs the rendered payload to stdout instead of sending.

To actually send from dev, inject Cloudflare credentials and a verified sender into compose/.env:

Terminal window
echo 'EMAIL_PROVIDER=cloudflare' >> compose/.env
echo 'EMAIL_FROM=noreply@yourdomain.com' >> compose/.env
echo 'CLOUDFLARE_ACCOUNT_ID=your_account_id' >> compose/.env
echo 'CLOUDFLARE_EMAIL_API_TOKEN=your_scoped_api_token' >> compose/.env

The step-by-step walkthrough lives in the Cloudflare Email setup runbook: enable Workers Paid, enable Email Service on your domain, capture the account ID, scope a token, smoke-test.

Change env vars to switch providers. For Resend:

Terminal window
echo 'EMAIL_PROVIDER=resend' >> compose/.env
echo 'RESEND_API_KEY=re_your_resend_api_key' >> compose/.env

For SendGrid:

Terminal window
echo 'EMAIL_PROVIDER=sendgrid' >> compose/.env
echo 'SENDGRID_API_KEY=SG.your_sendgrid_key' >> compose/.env

The env validator refuses to boot in production if the matching key is missing. Switching providers is a one-redeploy operation, not a code change.

  • Cloudflare Email Service is in beta. Pricing may evolve; daily limits are variable and account-scoped.
  • DNS must be on Cloudflare. Auto-provisioned SPF/DKIM/DMARC are how deliverability works; you can’t skip the step.
  • New accounts can only send to verified addresses until upgraded. The runbook covers the unlock.
  • No outbound webhook for bounce / complaint events. Cloudflare maintains its own suppression list and the send API rejects suppressed recipients directly; the API template mirrors that verdict into a local table so the next send short-circuits without the round-trip. Historical event detail lives in Cloudflare’s GraphQL Analytics API (emailSendingAdaptive) and the Email Logs dashboard. See Bounce handling for the per-provider model.