Getting started
notify-flow runs in your own Cloudflare account: one Worker, one D1 database and one queue.
You need Node.js 20+ and a Cloudflare account.
bash
npm install
cp wrangler.jsonc.example wrangler.jsonc # git-ignored: fill in your database_id before deploying
npm run db:migrate:local
npm run client:create -- my-app # prints the API key once
cp .dev.vars.example .dev.vars # fill in your SMTP credentials
npm run devTo deploy:
bash
npx wrangler d1 create notify-flow # copy the database_id into wrangler.jsonc
npm run db:migrate:remote # on upgrades too: migrate before npm run deploy
npx wrangler queues create notify-flow-emails # once: the async send queue
npx wrangler secret put SMTP_USERNAME # repeat for SMTP_PASSWORD, SMTP_FROM_EMAIL, SMTP_FROM_NAME
npm run deploy
npm run client:create -- my-app --remoteNOTE
Gmail needs an app password when 2-step verification is on.
For development:
bash
npm test # vitest inside workerd, with a local D1
npm run typecheck # regenerates worker types, then tscSMTP configuration
Credentials are stored as Cloudflare Secrets, never in the repository or in code.
| Name | Where | Value |
|---|---|---|
SMTP_PROVIDER | vars in wrangler.jsonc | gmail, microsoft or generic |
SMTP_HOST, SMTP_PORT, SMTP_SECURITY | vars, optional | Override the preset (SMTP_SECURITY is tls or starttls) |
SMTP_AUTH_TYPE | vars, optional | plain or login (default: per preset) |
SMTP_USERNAME, SMTP_PASSWORD | secret | SMTP account credentials |
SMTP_FROM_EMAIL, SMTP_FROM_NAME | secret | Sender identity |
| Preset | Host | Port, security and auth |
|---|---|---|
gmail | smtp.gmail.com | 465 with TLS, AUTH PLAIN |
microsoft | smtp.office365.com | 587 with STARTTLS, AUTH LOGIN |
generic | set SMTP_HOST | 587 with STARTTLS, AUTH PLAIN |
WARNING
Microsoft 365 requires SMTP AUTH to be enabled on the sending mailbox, and Security Defaults block it. Microsoft is also retiring basic authentication for SMTP AUTH. If you use this preset, plan for OAuth2 (XOAUTH2 is on the roadmap).
Security
- Per-client API keys, stored only as SHA-256 hashes.
- Idempotency: retrying a request with the same
Idempotency-Keynever sends a duplicate email. - Rate limits: 300 requests per minute per IP (checked before the API key), 60 per minute per client, and
RECIPIENT_LIMIT_PER_HOUR(default 5) emails per recipient and template for each client. Failed sends do not count. - No secrets in logs: delivery records and logs never contain variables, tokens, reset URLs or SMTP credentials.
Next
Wire your app to it with the integration guide, or look up an endpoint in the API Reference.