Replaces Payload's email adapter with one that sends mail via Cloudflare's native send_email Workers binding, so no API token or REST API is needed. It is meant for Payload apps deployed to Cloudflare Workers and requires the sender domain to be onboarded for Cloudflare Email Sending. It is tested with Payload 3.84.
Cloudflare Email Service adapter for Payload CMS
Installation
npm i payload-cloudflare-email-adapter3
Stars
1
Forks
0
Open Issues
1 months ago
Last Updated
1.9k
Weekly Downloads
v1.1.0
npm Version
40.6 KB
Unpacked Size
0
Dependencies
Trends appear after 7 days of data. Collecting since Oct 22 — 2 daily snapshots so far.
Trends appear after 7 days of data. Collecting since Oct 22 — 2 daily snapshots so far.
Each point is npm's rolling 7-day download count.
Data as of Oct 23, 2026
Health Score
Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.
Badge for your README
[](https://payloaddirectory.dev/plugins/1266644715-root)<a href="https://payloaddirectory.dev/plugins/1266644715-root"><img src="https://payloaddirectory.dev/badge/1266644715-root.svg" alt="Payload Directory"></a>README
Payload email adapter that sends transactional email through the Cloudflare Email Service Workers binding.
Built for Payload applications deployed to Cloudflare Workers. It uses Cloudflare's native
send_email binding, so it does not require an API token or use the Cloudflare REST API.
Tested with Payload 3.84.
pnpm add payload-cloudflare-email-adapter
Add cloudflareEmailAdapter() to your Payload config file src/payload.config.ts:
// src/payload.config.ts
import { getCloudflareContext } from '@opennextjs/cloudflare'
import { buildConfig } from 'payload'
import { cloudflareEmailAdapter } from 'payload-cloudflare-email-adapter'
const { env } = await getCloudflareContext({ async: true })
export default buildConfig({
email: cloudflareEmailAdapter({
binding: env.EMAIL,
defaultFromAddress: '[email protected]',
defaultFromName: 'Example',
}),
})
Enable Email Sending for the sender domain:
pnpm exec wrangler email sending enable example.com
Add the binding to wrangler.jsonc:
{
"send_email": [
{
"name": "EMAIL",
"allowed_sender_addresses": ["[email protected]"],
},
],
}
Generate the Worker environment types after adding the binding:
pnpm exec wrangler types
Cloudflare Email Service requires the sender domain to use Cloudflare DNS and be onboarded for Email Sending.
Activation usually takes 5–15 minutes but can take up to 24 hours.
For your project:
example.comEMAIL[email protected]Deploy the Payload Worker after onboarding the domain. Wrangler creates the EMAIL binding from
your wrangler.jsonc.
Configure Payload Form Builder emails with:
Email From: [email protected]
Reply To: {{email}}
The Email From address must remain under the onboarded domain. Reply To can use the customer's submitted email address.
This Workers binding setup does not require:
noreply mailboxCloudflare Email Sending is for transactional messages, including password resets, email verification, and form notifications.
Source: Cloudflare Email Sending setup
The adapter requires a Cloudflare Email Service binding. It does not read environment variables, API tokens, or account IDs.
The binding name must match the value passed to the adapter:
{
"send_email": [
{
"name": "EMAIL",
"allowed_sender_addresses": ["[email protected]"],
},
],
}
For local development, proxy the binding to Cloudflare:
{
"send_email": [
{
"name": "EMAIL",
"remote": true,
"allowed_sender_addresses": ["[email protected]"],
},
],
}
Remote local bindings send real email. Use recipient addresses you control.
The sender domain must be enabled for Cloudflare Email Sending. When
allowed_sender_addresses is configured, Payload's Email From address and the adapter's
defaultFromAddress must be included in that list.
payload.sendEmail() deliveryto, cc, bcc, from, and replyToName <[email protected]> address valuesBuffer, or Node.js Readablecid fieldThe adapter does not add Payload collections, fields, endpoints, admin components, or database schema.
// src/payload.config.ts
cloudflareEmailAdapter({
binding: env.EMAIL,
defaultFromAddress: '[email protected]',
defaultFromName: 'Example',
})
Options:
binding: Cloudflare Email Service Workers binding from the runtime environment.defaultFromAddress: sender address used when Payload does not provide from.defaultFromName: sender name used with the default sender address.maxMessageBytes: adapter-side byte limit for the complete structured message payload. Defaults
to 5 MiB; use 25 MiB only when every destination address is verified.The package exports CloudflareEmailBinding for applications that do not directly use generated
Wrangler types:
import type { CloudflareEmailBinding } from 'payload-cloudflare-email-adapter'
type CloudflareRuntime = {
env: {
EMAIL: CloudflareEmailBinding
}
}
The Workers binding rejects with an Error whose code is a machine-readable string such as
E_RATE_LIMIT_EXCEEDED; it is not an HTTP status. The adapter converts documented Cloudflare
errors to Payload APIError instances and preserves that string at error.data.code. Validation
and configuration errors map to 400, oversized content to 413, rate and daily limits to 429,
delivery and internal service failures to 502, and unknown Cloudflare codes to 500. Errors
without a string code are rethrown unchanged.
Each attachment requires filename, contentType, and content. File-system and URL path
attachments are not supported; load their data into content first.
Strings with encoding: 'base64' are decoded to raw bytes, while other strings are encoded as
UTF-8. Decoding is required because the Cloudflare Workers binding treats string content as raw
bytes rather than base64. The adapter buffers Node.js Readable streams as Uint8Array values before sending the
message. While attachments are mapped, their aggregate content is capped at maxMessageBytes so
streams stop before consuming unbounded Worker memory. After the complete message object is built,
the adapter measures its serialized metadata and attachment buffers together and applies the same
limit to the entire payload. Cloudflare separately applies its authoritative
complete-message limit
after MIME encoding and transport overhead are added.
Install dependencies and run the focused checks:
pnpm install
pnpm test
pnpm typecheck
pnpm lint
Adapter source is in src/. Tests are in tests/.
The package's prepack lifecycle runs tests, type checking, linting, and the release build before
an npm tarball is created.
For release validation, test the packed artifact instead of a live source-folder dependency:
pnpm pack
Then in the external Payload application:
pnpm add /path/payload-cloudflare-email-adapter-*.tgz
payload usesend
Email adapter that sends Payload emails through the open-source, self-hostable useSend API
83457/wk8v3
groundmail payload
Email adapter that sends Payload auth mail and sendEmail messages through the Groundmail HTTP API
822.8k/wk0v?
payload order emails
Transactional emails on Payload order creation and status changes, with overridable templates
570/wk1v3
eusend payload
Payload email adapter that sends transactional email through the eusend REST API from the EU
543/wk1v3 v4
payload plugin
Email advocacy and contact management with broadcasts, forms and letter campaigns inside the Payload admin
515/wk1v3
payload email ahasend
Email adapter that sends Payload CMS emails through the AhaSend API, with support for API v1 and v2
371/wk1v3