1---
2title: Email
3description: Cloudflare Email Routing turns a zone into a mail endpoint — enable routing, verify destinations, forward with rules and a catch-all, hand mail to a Worker, and send from Workers with the send_email binding.
4---
5
6Email Routing turns a zone you own into a mail endpoint: rules match
7inbound mail and forward it, drop it, or hand it to a Worker — and
8once routing is enabled, Workers can also *send* mail from the domain
9through a `send_email` binding.
10
11You need a zone first — see [Domains & DNS](/cloudflare/networking/domains).
12Receiving needs a verified destination address (or a Worker); sending
13needs routing enabled on the sender's domain. The category also
14covers sending subdomains and the enterprise Email Security suite,
15both below.
16
17## Enable routing on the zone
18
19`Email.Routing` is the prerequisite for both directions:
20
21```typescript
22const routing = yield* Cloudflare.Email.Routing("Routing", {
23 zone: "example.com",
24});
25// routing.zoneId, routing.enabled, routing.status
26```
27
28`zone` accepts a zone id, a zone name, or a `{ zoneId }` object;
29moving zones is a replacement, and destroy disables Email Routing on
30the zone.
31
32## Verify a destination address
33
34Rules can only forward to addresses Cloudflare has verified:
35
36```typescript
37const inbox = yield* Cloudflare.Email.Address("Inbox", {
38 email: "you@personal.example",
39});
40// inbox.verified, inbox.verifiedAt
41```
42
43Cloudflare emails a verification link on first create — until it's
44clicked, forwards don't deliver; addresses are account-scoped, and
45changing `email` is a replacement.
46
47## Forward an address
48
49`Email.Rule` matches inbound mail on the zone and applies actions:
50
51```typescript
52const rule = yield* Cloudflare.Email.Rule("InboxRule", {
53 zone: "example.com",
54 name: "Forward inbox to destination",
55 matchers: [{ type: "literal", field: "to", value: "inbox@example.com" }],
56 actions: [{ type: "forward", value: ["you@personal.example"] }],
57});
58```
59
60Mail that matches no rule falls through to the zone's catch-all;
61lower `priority` numbers run first (default `0`).
62
63## Catch everything else
64
65The catch-all is a per-zone singleton — `Email.CatchAll` puts the
66desired configuration and restores the prior state on destroy:
67
68```typescript
69yield* Cloudflare.Email.CatchAll("CatchAll", {
70 zone: routing.zoneId,
71 actions: [{ type: "forward", value: ["you@personal.example"] }],
72});
73```
74
75To silently discard unmatched mail instead:
76
77```typescript
78yield* Cloudflare.Email.CatchAll("DropTheRest", {
79 zone: routing.zoneId,
80 actions: [{ type: "drop" }],
81});
82```
83
84## Hand mail to a Worker
85
86Both `Rule` and `CatchAll` accept a `worker` action naming a deployed
87Worker script (its `workerName` attribute):
88
89```typescript
90yield* Cloudflare.Email.CatchAll("CatchAllWorker", {
91 zone: routing.zoneId,
92 actions: [{ type: "worker", value: ["my-email-worker"] }],
93});
94```
95
96Matched messages are delivered to that Worker's `email` handler
97instead of a mailbox.
98
99## Declare the send_email binding
100
101`Email.SendEmail` is a binding descriptor, not a cloud resource — it
102names the binding and pins sender/destination restrictions Cloudflare
103enforces at send time:
104
105```typescript
106// src/email.ts
107import * as Cloudflare from "alchemy/Cloudflare";
108
109export const Email = Cloudflare.Email.SendEmail("Email", {
110 allowedSenderAddresses: ["bot@example.com"],
111 destinationAddress: "you@personal.example",
112});
113```
114
115`destinationAddress` pins one verified destination,
116`allowedDestinationAddresses` allows a list (the two are mutually
117exclusive); omit all three to send to any verified destination on the
118account.
119
120## Send from a Worker
121
122Bind the descriptor with `Email.Send` in the Worker's Construction phase and
123provide `Email.SendBinding` as the implementation layer:
124
125```typescript
126// src/notifier.ts
127import * as Cloudflare from "alchemy/Cloudflare";
128import * as Effect from "effect/Effect";
129import * as HttpServerResponse from "effect/http/HttpServerResponse";
130import { Email } from "./email.ts";
131
132export default Cloudflare.Worker(
133 "Notifier",
134 { main: import.meta.url },
135 Effect.gen(function* () {
136 const email = yield* Cloudflare.Email.Send(Email);
137
138 return {
139 fetch: Effect.gen(function* () {
140 yield* email.send({
141 from: "bot@example.com",
142 to: "you@personal.example",
143 subject: "Hello from a Worker",
144 text: "Sent via the send_email binding.",
145 });
146 return yield* HttpServerResponse.json({ ok: true });
147 }).pipe(
148 Effect.catchTag("SendEmailError", (error) =>
149 Effect.succeed(
150 HttpServerResponse.text(error.message, { status: 502 }),
151 ),
152 ),
153 ),
154 };
155 }).pipe(Effect.provide(Cloudflare.Email.SendBinding)),
156);
157```
158
159Rejections (an unverified destination, a sender outside the allow
160list) surface as a typed `SendEmailError`; the client also exposes
161`sendRaw` for a pre-built `EmailMessage` and `raw` for the underlying
162runtime binding. The `from:` address must be on a domain with routing
163enabled, and the `to:` address must be a verified destination.
164
165## Send from a subdomain
166
167`Email.SendingSubdomain` registers e.g. `mail.example.com` as a
168sender domain without touching the apex:
169
170```typescript
171const sending = yield* Cloudflare.Email.SendingSubdomain("Mail", {
172 zoneId: zone.zoneId,
173 name: "mail.example.com",
174});
175// sending.enabled — true once DNS records validated
176```
177
178For zones not on Cloudflare DNS, fetch the expected records via
179`emailSending.getSubdomainDns` and create them at your host —
180`enabled` flips to `true` once they validate.
181
182## Email Security (enterprise)
183
184The Email Security add-on filters inbound mail before delivery;
185accounts without the entitlement get a typed
186`EmailSecurityNotEntitled` error:
187
188```typescript
189yield* Cloudflare.Email.BlockSender("KnownPhisher", {
190 pattern: "phisher@malicious.example.com",
191 patternType: "EMAIL",
192});
193```
194
195The rest of the suite:
196
197- [Domain](/providers/cloudflare/email/domain) — per-domain
198 dispositions and TLS enforcement.
199- [AllowPolicy](/providers/cloudflare/email/allowpolicy) —
200 acceptable, trusted, and exempt senders.
201- [TrustedDomain](/providers/cloudflare/email/trusteddomain) —
202 suppress lookalike and new-domain detections.
203- [ImpersonationRegistryEntry](/providers/cloudflare/email/impersonationregistryentry)
204 — protect display names from BEC.
205
206## Where next
207
208The full walkthrough:
209
210- [Send & receive email](/cloudflare/email/send-and-receive) — wire both
211 directions on one zone, end to end.
212
213Related:
214
215- [Workers](/cloudflare/compute/workers) — the runtime that sends and
216 receives.
217- [Domains & DNS](/cloudflare/networking/domains) — the zone mail routes
218 through.
219- [Custom domains & routes](/cloudflare/networking/custom-domains) — get
220 or adopt the zone first.
221
222Reference:
223
224- [Routing API reference](/providers/cloudflare/email/routing)
225- [Address API reference](/providers/cloudflare/email/address)
226- [Rule API reference](/providers/cloudflare/email/rule)
227- [CatchAll API reference](/providers/cloudflare/email/catchall)
228- [Send API reference](/providers/cloudflare/email/send)
229- [SendingSubdomain API reference](/providers/cloudflare/email/sendingsubdomain)
230- [Domain API reference](/providers/cloudflare/email/domain)
231- [BlockSender API reference](/providers/cloudflare/email/blocksender)
232- [AllowPolicy API reference](/providers/cloudflare/email/allowpolicy)
233- [TrustedDomain API reference](/providers/cloudflare/email/trusteddomain)
234- [ImpersonationRegistryEntry API reference](/providers/cloudflare/email/impersonationregistryentry)