FlaresendDocs

Deploy

Set up Flaresend in your Cloudflare account, from an empty account to your first API key.

This is the one-time setup. Run the commands from apps/mailer unless a step says otherwise. Replace acme.com, <ZONE_ID> and mailer.example.com with your own values.

Log in to Cloudflare

npx wrangler login

Create the resources

npx wrangler d1 create flaresend
npx wrangler r2 bucket create flaresend-payloads
npx wrangler r2 bucket lifecycle add flaresend-payloads --prefix payloads/ --expire-days 30
npx wrangler queues create flaresend-send
npx wrangler queues create flaresend-events
npx wrangler queues create flaresend-dlq
npx wrangler queues create flaresend-webhooks

wrangler d1 create prints a database_id. Paste it into d1_databases[0].database_id in both wrangler.jsonc and wrangler.dev.jsonc.

The lifecycle rule deletes email bodies from R2 after 30 days. After that, GET /v1/emails/:id/content returns 404 content_expired and the email can no longer be resent. The metadata in D1 stays.

Onboard each sending domain

Repeat this for every domain a project will send from. On zones that use Cloudflare DNS, it adds the SPF and DKIM records for you.

npx wrangler email sending enable acme.com
npx wrangler email sending dns get acme.com   # confirm the records
npx wrangler email sending list

Add a DMARC record for the domain too, unless the onboarding already added one:

_dmarc.acme.com  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@acme.com"

Once the mailer is deployed with a CF_API_TOKEN that has the Edit permissions listed in Configuration, the dashboard can do this step and the next one for you: Add domain onboards a new domain, and Set up in Cloudflare does it for a domain already on the project. The CLI and API equivalent is Set up a domain.

Send delivery events to the events queue

One subscription per sending domain. --zone-id is the zone that contains the domain.

npx wrangler queues subscription create flaresend-events \
  --source email.sending \
  --events message.delivered,message.deferred,message.bounced,message.failed,message.rejected,message.complained \
  --zone-id <ZONE_ID> --domain acme.com \
  --name flaresend-acme-com

Without this subscription emails still send, but they stay at sent forever: no delivered, bounced or complained status, and no automatic suppressions.

Set the secrets

npx wrangler secret put ADMIN_API_KEY     # generate with: openssl rand -base64 32
npx wrangler secret put TRACKING_SECRET   # open/click tracking and unsubscribe links: openssl rand -base64 32
npx wrangler secret put CF_API_TOKEN      # optional: domain status and domain setup

If you set CF_API_TOKEN, also set the CF_ACCOUNT_ID var in wrangler.jsonc. Configuration lists the token's permissions.

Set the public URL

In wrangler.jsonc, set vars.PUBLIC_BASE_URL to the URL the mailer is reachable at, for example https://mailer.example.com. Tracking pixels, click links and unsubscribe links point there.

To serve the mailer on your own hostname instead of workers.dev, add a route:

apps/mailer/wrangler.jsonc
"routes": [{ "pattern": "mailer.example.com", "custom_domain": true }]

Migrate and deploy

npx wrangler d1 migrations apply flaresend --remote
npx wrangler deploy

Check that it answers. /health runs a D1 query and returns { "ok": true }:

curl https://mailer.example.com/health

From the repo root, pnpm run deploy does both steps (migrate:remote, then deploy in apps/mailer).

Create the first project and API key

curl -X POST https://mailer.example.com/v1/admin/projects \
  -H "Authorization: Bearer $FLARESEND_ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{"slug":"acme","name":"Acme","defaultFrom":"Acme <hello@acme.com>","allowedDomains":["acme.com"]}'

curl -X POST https://mailer.example.com/v1/admin/projects/acme/api-keys \
  -H "Authorization: Bearer $FLARESEND_ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{"name":"production","mode":"live"}'

The second call returns the full key once. Store it in your app's secrets: Flaresend keeps only the first 12 characters and a SHA-256 hash.

Or with the CLI:

export FLARESEND_BASE_URL=https://mailer.example.com FLARESEND_ADMIN_KEY=...
flaresend projects create --slug acme --name Acme --domains acme.com --default-from "Acme <hello@acme.com>"
flaresend keys create --project acme --name production --mode live

Send a test email

curl -X POST https://mailer.example.com/v1/emails \
  -H "Authorization: Bearer $FLARESEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"you@your-inbox.com","subject":"It works","text":"Sent through Flaresend."}'

from can be left out because the project has a defaultFrom. Then follow the Quickstart.

Optional next steps

  • Deploy the dashboard.
  • Set up CI so pushes to main migrate and deploy.
  • Let other Workers send over RPC: add a service binding to flaresend, entrypoint MailerRpc. See Cloudflare Workers.

On this page