Set up outbound email and validate your sender domain
Outbound email is how your engine reaches a person: alert notifications, licence expiry warnings, the role-invite for a new member, and the first-run set-up link that claims the first Owner. It is optional. Nothing in the backup or recovery path depends on it, so you can configure it now, later, or never.
It is also one of the few parts of a deploy that cannot be finished from a terminal. Cloudflare makes you onboard and verify the sending domain in the dashboard, because sending mail from a domain is an assertion about that domain and it has to be proven with DNS. This page walks the whole thing, then shows you how to prove it works rather than assume it.
Email is fail-open, so a quiet inbox is not a fault
Until the sending domain is onboarded, every send is a clean, logged no-op rather than an error: notifications simply skip the email channel and the run carries on. So do not read silence after a first deploy as a broken deployment. It is the default until you complete the steps below.
What you need
| Requirement | Why |
|---|---|
| A Cloudflare account on Workers Paid | The send-email binding is a paid-plan feature, and the deploy already requires Workers Paid for its raised CPU limit. |
| A domain you control, on this account | You send from this domain, so Cloudflare makes you prove you hold it. The engine refuses a workers.dev sender at send time. |
| The engine deployed at least once | You are adding configuration to an engine that already exists. If you have not deployed yet, start with the first deploy. |
Configure and verify, step by step
Confirm the send-email binding is present
The engine ships with the binding already declared in
wrangler.toml, so in most cases there is nothing to add:[[send_email]] name = "EMAIL" remote = trueThis is the binding the engine sends through. If you have trimmed your
wrangler.toml, put it back: without it the engine has no way to send at all, and the test send in step 5 reports the binding as not configured.Replace the shipped sender address with your own
EMAIL_FROMis the address alerts and expiry warnings are sent from. The engine ships a placeholder you do not own:# engine/wrangler.toml ships this. It is OUR domain, not yours. EMAIL_FROM = "no-reply@downpipes.io"Edit that line in the existing
[vars]table to an address on the domain you are about to onboard. Do not paste a second[vars]table: TOML rejects a duplicate table, and the deploy will fail.EMAIL_FROM = "alerts@your-domain.example"Be aware that the shipped default is shaped like a valid sender, so the engine accepts it and preflight reports email as configured. It only fails at the platform, at send time, because you do not own that domain. That is exactly why the test send in step 5 is the thing to trust.
INVITE_EMAIL_FROMis optional and separate. It is the sender for the role-invite, which the engine sends only when this is set, so leaving it unset lets you run alert and expiry email without emailing on every role grant. The first-run set-up link uses it when present and otherwise falls back toEMAIL_FROM, so the emailed first-Owner path needs onlyEMAIL_FROM.INVITE_EMAIL_FROM = "invites@your-domain.example"Any address you use must be on a domain you onboard in the next step.
Onboard the sending domain in the Cloudflare dashboard
This is the browser step, and it is the one people miss.
- Open the Cloudflare dashboard and select the account the engine is deployed into.
- Go to Compute, then Email Service, then Email Sending.
- Onboard the domain that
EMAIL_FROMuses (andINVITE_EMAIL_FROM, if you set it). - Cloudflare adds the required DNS records for you when the domain is on this account. Let it finish and wait for the domain to report as verified.
If Email Service itself is not yet enabled on the account, complete its set-up on that same screen first. Until the domain shows as verified, the platform refuses the sender and the engine records the refusal rather than pretending it sent.
Deploy the engine so the sender takes effect
EMAIL_FROMis a deploy-time variable, so it only reaches the running engine on a deploy:cd engine npm run deployAlways use
npm run deploy, never a barewrangler deploy: the npm script reconciles your live bindings first and refuses to ship a config that would drop a source you attached from the console. Do not setDOWNPIPE_ALLOW_BINDING_RESET, which would let a failed reconcile drop them anyway. The reasoning is on deploy safety and bindings.Prove it works with a test send
Do not assume. In the console, open Settings, then Support, and use Send a test email. The engine performs a real send to your own signed-in address and reports honestly what happened, including the platform’s own error code, and the console translates it:
What you see What it means What to do Sent The send was accepted. Check the inbox of your signed-in address, then its spam folder. If nothing arrives, re-check the domain’s onboarding state. You are signed in with the bare admin token, which has no email address There is nowhere to send to. This is the normal state right after a deploy that bootstrapped with the admin token. Claim an Owner identity first (a passkey, or Cloudflare Access), then sign in as that person and test again. Email is not configured on this engine The [[send_email]]binding is missing.Restore the binding from step 1 and redeploy. The sending domain is not onboarded for Email Sending The platform refused the sender. Finish step 3: onboard the EMAIL_FROMdomain under Compute, Email Service, Email Sending.EMAIL_FROMis unset or not a custom-domain addressThe engine has no valid sender. Set EMAIL_FROMto an address on your onboarded domain and redeploy (steps 2 and 4).Email Service may not be enabled on this account The account has not completed Email Service set-up. Open Compute, then Email Service, complete set-up, then test again. Preflight proves presence, not delivery
The engine’s
GET /admin/preflightchecks only that theEMAILbinding is bound andEMAIL_FROMis set, and it says so rather than claiming more. It does not attempt a send, so it will happily report email as configured while the shippedno-reply@downpipes.iodefault is still in place. The test send is the only thing that proves delivery.
Where this fits
Email is configuration you add to a deployed engine, so it sits alongside the first deploy and the self-host runbook. Once sending works, the channels that use it are set up on the notifications page, and the attributable first-run path that emails a set-up link is covered in identity and access. If you would rather not enable email at all, claim the first Owner with the one-time admin token instead, which the first deploy walks through.
Last updated .