Repository navigation
Add Cloudflare Email Sending as a sending provider - #2
Merged
Merged
Conversation
Cloudflare Email Sending sends from the same Cloudflare account that
already handles inbound routing and DNS, so connecting it needs no second
credential: the adapter uses the stored API token and account id.
- providers/cloudflare.ts sends through POST /accounts/{id}/email/sending/send
and maps Cloudflare's error codes (not entitled, sending disabled, missing
permission) to readable messages. Message-ID is generated by Cloudflare and
rejected in headers, so replies thread through References like Resend.
- Cloudflare exposes no API for onboarding a sending domain, so the provider
has hasDomainApi: false, the same shape SMTP already uses; the UI says to
enable Email Sending in the dashboard for each domain.
- Adds the optional Email Sending token permission (and its probe), a
migration widening the provider type CHECK, a mock send endpoint and tests.
Requires the Workers Paid plan on the account, which the UI and README say.
Cloudflare and SMTP cannot verify a domain over an API, so they never get a verified row in provider_domains. The Domains card read that as "Not sending" and the health check added a "No sending provider set up" warning, even though those providers send for any domain. Both now treat a connected provider without a domain API as sending, and the health item names it instead of warning.
Contributor
Author
|
End-to-end test completed on a fresh deploy from this branch, Workers Paid account, real domain on Cloudflare:
The test instance was deployed before the second commit (the "Not sending" badge fix), so that one is covered by tests only. The first bare |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1. Adds Cloudflare Email Sending as an optional outbound provider, so a domain that already receives through Email Routing can also send from the same account without a second vendor.
How it sends.
src/worker/providers/cloudflare.tsposts toPOST /accounts/{account_id}/email/sending/sendwith the token and account id setup already stores, so connecting is one click and no key to paste. It uses the REST API rather than thesend_emailbinding, which would mean adding a binding towrangler.jsoncfor every deploy and every existing instance.A few things differ from the other adapters:
Message-IDand rejects it inheaders, so the adapter drops it and relies on the References the send pipeline appends — the same path Resend already uses.10105says the account needs Workers Paid,10203says sending is not enabled for the domain,10102says the token is missing the Email Sending permission.wrangler email sending enableand the dashboard only), so the provider ishasDomainApi: false, matching how SMTP already works. The Integrations card says to enable Email Sending per domain in the dashboard, and the connect flow no longer tries to walk every domain through a provider domain API. Domains that use it skip the "verified" check when picking a sender, same as SMTP.TOKEN_PERMISSIONSand to the token link, with a probe incheckPermissions.10105counts as permission present, since that is an account state.0003widens theproviders.typeCHECK. SQLite cannot alter it, so the table is rebuilt; rows inprovider_domainsandquota_usageand theprovider_idoverrides ondomainsandmessagesare copied aside and restored in the same transaction, because droppingproviderscascades.The provider row defaults to a monthly cap of 3,000, which is what Workers Paid includes.
Testing.
npm run lint,npm run typecheck,npm test(56 passed) andnpm run test:e2epass.test/integration/cloudflare-provider.test.tscovers connecting, a send, threading of the reply, the Workers Paid message, and retry after a failure, against a new mock for the send endpoint. I also ran the migration against a copy of the schema with rows inproviders,provider_domains,quota_usage,domainsandmessages; everything survives and the new type inserts.Also tested end to end on a Workers Paid account with a real domain: inbound via Email Routing, connecting the provider, a send through Cloudflare Email Sending that Gmail scored SPF/DKIM/DMARC pass with
header.daligned, and the reply threading into the same conversation. Details in the comment below.