Cloudflare Email Sending
Send through Cloudflare's Email Sending REST API or Worker binding with plain-address recipients and a 50-recipient cap.
Capabilities
| Repeated headers | Idempotency | Scheduling | Personalized |
|---|---|---|---|
| No | none | No | expanded |
These values come from the adapter's exported capabilities declaration. The field support matrix covers normalized message fields.
The Cloudflare adapter supports both Cloudflare Worker send_email bindings and Cloudflare's Email Sending REST API. It is the natural pick when your sending domain lives in Cloudflare or when sending directly from a Cloudflare Worker. The trade-off is strict recipient rules: plain addresses only, 50 recipients max.
Configure
Worker binding (Cloudflare Workers)
In a Cloudflare Worker, bind Email Sending in your wrangler.json (or wrangler.toml) and pass the binding object (env.SEB) directly to the adapter.
import { createEmailClient } from "@opencoredev/email-sdk";
import {
cloudflare,
type CloudflareSendEmailBinding,
} from "@opencoredev/email-sdk/cloudflare";
export interface Env {
SEB: CloudflareSendEmailBinding;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const email = createEmailClient({
adapters: [
cloudflare({
binding: env.SEB,
}),
],
});
// ...
},
};REST API
Enable Email Sending for your account and domain, then create an API token with Email Sending permission. You also need your account ID.
import { createEmailClient } from "@opencoredev/email-sdk";
import { cloudflare } from "@opencoredev/email-sdk/cloudflare";
export const email = createEmailClient({
adapters: [
cloudflare({
apiToken: process.env.CLOUDFLARE_API_TOKEN!,
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
}),
],
});Prop
Type
Send
Cloudflare maps cc, bcc, one replyTo, headers, and attachments. There are no tags or metadata.
const result = await email.send({
from: "Acme Alerts <alerts@acme.com>",
to: "oncall@example.com",
cc: "ops@example.com",
subject: "Disk usage above 90% on db-1",
text: "db-1 crossed the 90% disk threshold at 14:02 UTC.",
headers: [{ name: "X-Alert-ID", value: "alrt_8841" }],
});
console.log(result.accepted); // delivered + queued recipientsCloudflare REST API does not return a message id; instead result.accepted lists delivered and queued recipients and result.rejected lists permanent bounces. Worker bindings return the message ID if provided by the binding. Total recipients across to, cc, and bcc are capped at 50.
Plain recipient addresses only
to, cc, and bcc must be bare addresses like "user@example.com"; a display name throws an
EmailValidationError before any request is made. Only from and replyTo keep display names.
Verify from the CLI
CLOUDFLARE_API_TOKEN="..." CLOUDFLARE_ACCOUNT_ID="..." npx email-sdk doctor --adapter cloudflarenpx email-sdk send \
--adapter cloudflare \
--api-token "$CLOUDFLARE_API_TOKEN" \
--account-id "$CLOUDFLARE_ACCOUNT_ID" \
--from "Acme <hello@acme.com>" \
--to user@example.com \
--subject "Cloudflare smoke test" \
--text "It works" \
--dry-runDrop --dry-run for one real send; only Cloudflare can prove the domain and account are cleared to deliver.
