The surface. Using Email is the same ground with the code you write around it.
Config options
| Option | Default | Meaning |
|---|---|---|
fromAddress | noreply@example.com | Must use a domain onboarded onto Email Service |
fromName | Pithy | What recipients see. A recognizable name improves deliverability |
baseUrl | PUBLIC_ORIGIN | What tracking and unsubscribe links are built against |
basePath | /email | Where the management routes mount. Does not affect the recipient callbacks |
theme | saffron | One of four, or fine-tuned with a custom theme object |
schedulerEnabled | true | Whether the every-minute scheduler runs |
devDelivery | remote | Under pithy dev: remote sends real mail; simulator logs and writes to disk |
The recipient callbacks keep a fixed prefix and are unaffected by the base path, because those URLs are already minted into mail nobody can recall.
Templates
Kit copy, translated with the kit: magicLink · otp · welcome · securityAlert · invite · passwordChanged · leadCapture
Payload-carrying, only as localized as your own copy: testerNudge · supportReply · operationalNotice · newsletter · marketingCampaign
The registry is closed. A template is a kit artifact rather than something you add — which is what lets the send Worker carry every one of them in its own bundle in every language.
Job statuses
| Status | Inputs kept | Why |
|---|---|---|
sent | Dropped | The one status a retry is already refused for — retrying a delivered job is a duplicate email to a real person |
failed | Kept | The retry route exists for exactly this row, and re-renders from the payload |
suppressed, bounced | Kept | Nothing was delivered. A manual block can be lifted |
pending, scheduled, sending | Kept | The send has not happened yet |
undispatched | Kept | Not until a host Worker exists to claim it |
undispatched is a truthful status, never a grave. The day the host is deployed, its first tick claims those rows.
Suppression
| Event | Effect |
|---|---|
hard_bounce | Block |
complaint | Block |
manual | Block |
unsubscribe | Blocks marketing, not sign-in |
One suppression database per project, not per environment: an unsubscribe in production has to stop staging too.
Management routes
Every one is control-plane and default-denied. With the seam uncomposed, all six answer 403.
| Method | Path | Scope |
|---|---|---|
| GET | /email/jobs | email:jobs:read |
| GET | /email/jobs/:id | email:jobs:read |
| POST | /email/jobs/:id/retry | email:jobs:retry |
| GET | /email/suppressions | email:suppressions:read |
| POST | /email/suppressions | email:suppressions:write |
| POST | /email/suppressions/remove | email:suppressions:delete |
Every call is audited, reads included. A block is silent to everyone it affects, so the trail is the only record it happened.
Recipient callbacks
Three, at a fixed prefix, signed, and public because the token is the credential: click, open, unsubscribe.
Every token carries its key version, so a link in a months-old email still verifies after a rotation.
Bindings
| Binding | Type | Written by |
|---|---|---|
DB | d1 | pithy add |
EMAIL_SUPPRESSIONS | d1 | pithy add — the project-wide list |
EMAIL_SENDER | workflow | pithy add, naming the host across scripts |
The Workflow entry is complete before the host exists, so local dev binds it either way.
Secrets
email-link-signing-key — minted, arbitrary, and rotatable: every token carries its version, so a rotation does not break links already in inboxes.
Provisioning
All of it is pithy email provision.
pithy email provision creates the shared suppression database, migrates it, and deploys the per-environment host.
The three routing flags are all or nothing. Enabling Email Routing on a zone points its MX at Cloudflare — so use a subdomain, never your apex.
Peer capabilities
Required: secrets.
Composed with i18n, the shell follows each job’s recorded locale.
What is not here
No segment builder, no A/B testing, no drip editor. There are campaign templates and a suppression list, and that is the extent of the marketing surface.
No inbox. Inbound mail is handled for bounces and complaints; reading and answering customer mail is support.
No deliverability guarantee. SPF, DKIM, DMARC and reputation decide that, and what you can do about each is a page of its own.