Native Shell
Native Shell: AI-generated UI, native in your app, on every platform. Apps on iOS, Android, macOS, Windows and Linux, GPUI apps and web pages load signed, sandboxed TSX modules at run time and draw th
Self-hosted transactional email over plain SMTP, plus opt-in Web Push, for Cloudflare Workers
Translation pending
Developer toolsTypeScript · JavaScriptGitHub @yintong-zhouOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Email Sender API, deploy in your Free Cloudflare Accont to user like a service
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗A self-hosted transactional email service for Cloudflare Workers. Your backends call one authenticated REST API, and notify-flow renders the email from a template and sends it over plain SMTP to Gmail, Microsoft 365 or any other SMTP server. It does not depend on Resend, SendGrid, Mailgun or similar platforms. It can also send Web Push notifications to your users' phones and computers, opt-in per client.
[!IMPORTANT] There is no shared public instance. Each person or team deploys their own notify-flow, in their own Cloudflare account, with their own D1 database, queue and SMTP mailbox, and connects their own apps to it. The author's deployment is private and is not offered as a service: don't point your apps at it. The AI integration prompt walks you through your own deploy and the integration into your site.
[!NOTE] The MVP is implemented: emails are sent by template name or by event, synchronously or asynchronously through Cloudflare Queues, and data lives in Cloudflare D1. The Roadmap lists what comes next.
[!TIP] Adding email to your web app? Follow the web app integration guide: a step-by-step walkthrough with backend code samples. Or paste the AI integration prompt into a coding agent and let it do the work, then the AI management page prompt to get an admin page with statistics and template editing. For push notifications only, without email, use the AI Web Push prompt.
Every app ends up sending the same emails: welcome, email verification, password reset, login alerts. notify-flow keeps those emails in one place:
it, en and pt-BR.Frontend ──► Your backend ──► notify-flow (Worker) ──► SMTP server ──► Recipient
│ │
│ ├─ authenticate the client (API key)
│ ├─ validate payload and rate limit
│ ├─ pick the template (by name or event), render subject, HTML, text
│ ├─ send over TLS / STARTTLS, now or from a queue, retrying transient errors
│ └─ log the delivery in D1
└─ owns auth logic (tokens, reset URLs, user checks)
notify-flow only delivers mail. Security logic such as generating reset tokens stays in your backend.
You need Node.js 20+ and your own Cloudflare account (sign up): every deployment is independent, so the commands below create the Worker, database and queue in your account.
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 dev # admin dashboard: http://localhost:8787/admin/ (not 127.0.0.1)
To deploy:
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
npx wrangler secret put ADMIN_SETUP_TOKEN # long random string, used once to register the /admin account
npm run deploy
npm run client:create -- my-app --remote
For development:
npm test # vitest inside workerd, with a local D1
npm run typecheck # regenerates worker types, then tsc
A web app sends email through your own deployment (see Getting started) in three steps:
npm run client:create -- <app-name> --remote once per app, then store the key and your Worker's URL as secrets in the app's backend.POST /v1/email/send with a template, a recipient and the template's variables, or POST /v1/notifications with an event instead of a template. The frontend never calls notify-flow.429 and 500 with the same Idempotency-Key. Treat 4xx as integration bugs.The web app integration guide covers the details: architecture, Node.js and Python clients, password reset and sign-up flows, custom templates, retry rules, asynchronous sending and a production checklist.
Web Push is optional and opt-in per client: notify-flow can also notify a user's phone or computer while your app is closed, with a title and a short body from a built-in template, never user-written text. The operator sets the VAPID keys once and switches push on per client (setup). Your backend then registers each device and calls POST /v1/push/send. The Web Push guide has the browser, service worker and backend code.
The Worker also serves a monitoring page at /admin/: delivery stats and search, a log of errors and rejected requests, a SQL console on the D1 database, and a Clients panel with the per-client Web Push switch. There is one admin account, registered on the first visit with the ADMIN_SETUP_TOKEN secret and kept separate from client API keys. The admin dashboard guide covers setup, upgrades of an existing deployment, and what each section shows.
Every /v1/* route needs an API key, sent as Authorization: Bearer <key> or X-API-Key: <key>. A send is one call:
POST /v1/email/send
Authorization: Bearer <API_KEY>
Idempotency-Key: password-reset:user-123:request-456
Content-Type: application/json
{
"template": "password-reset",
"to": "[email protected]",
"locale": "it",
"variables": {
"appName": "Acme",
"name": "Mario",
"resetUrl": "https://app.example.com/reset-password?token=..."
}
}
{ "id": "msg_…", "status": "submitted" }
The API Reference documents every endpoint, field rule, error code and status: sending, events, templates and health, and the opt-in Web Push.
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; needs an app password |
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).
Idempotency-Key never sends a duplicate email or push./v1/push/*, a separate budget), RECIPIENT_LIMIT_PER_HOUR (default 5) emails per recipient and template for each client, and PUSH_LIMIT_PER_HOUR (default 60) pushes per user and conversation. Failed sends do not count.src/
├── index.ts # Worker entry point and router
├── admin/ # dashboard API: setup, login, sessions, stats, logs, SQL, clients
├── push/ # Web Push: VAPID and encryption, subscriptions, sender
├── routes/ # email, notifications, events, templates, health
├── security/ # auth, payload validation, rate limiting
├── services/ # email-service (idempotency, retries, delivery log), queue-consumer (async sends), logger (app_logs)
├── templates/ # renderer, built-in defaults, built-in events
├── smtp/ # SMTP client + provider presets
└── types/
public/admin/ # dashboard pages, served as static assets
migrations/ # D1 schema
scripts/ # create-client.mjs, vapid-keys.mjs
test/ # vitest, runs inside workerd
docs/ # docs site (VitePress): guides, API Reference; specs and plans are not published
The docs/ folder doubles as a VitePress site: Home, Documentation and API Reference, built from these Markdown files (specs, plans and handoffs are not published).
npm run docs:dev # local site with hot reload
npm run docs:build # static build in docs/.vitepress/dist; fails on dead links
npm run docs:preview # serve the build
To publish it, connect the repository to Cloudflare Pages with these settings:
| Setting | Value |
|---|---|
| Project name | notify-flow-docs |
| Production branch | main |
| Build command | npm run docs:build |
| Build output directory | docs/.vitepress/dist |
| Environment variable | NODE_VERSION=22 |
These items come from the spec and are planned after the MVP:
X-Client-Id, X-Timestamp, X-Signature)