SKILL.md
$28
Before writing any email code, verify the basics are in place:
- Domain onboarded? Run
npx wrangler email sending listto see which domains have email sending enabled. If the domain isn't listed, runnpx wrangler email sending enable userdomain.comor see cli-and-mcp.md for full setup instructions.
- Binding configured? Look for
send_emailinwrangler.jsonc(for Workers)
- postal-mime installed? Run
npm ls postal-mime(only needed for receiving/parsing emails)
What Do You Need?
Start here. Find your situation, then follow the link for full details.
I want to...
Path
Reference
Send emails from a Cloudflare Worker
Workers binding (no API keys needed)
Send emails from an AI agent built with Cloudflare Agents SDK
onEmail() + replyToEmail() in Agent class
Send emails from an external app or agent (Node.js, Go, Python, etc.)
REST API with Bearer token
Send emails from a coding agent (Claude Code, Cursor, Copilot, etc.)
MCP tools, wrangler CLI, or REST API
Receive and process incoming emails (Email Routing)
Workers email() handler
Set up Email Sending or Email Routing
wrangler email sending enable / wrangler email routing enable, or Dashboard
Improve deliverability, avoid spam folders
Authentication, content, compliance
Quick Start — Workers Binding
Add the binding to wrangler.jsonc, then call env.EMAIL.send(). The from domain must be onboarded via npx wrangler email sending enable yourdomain.com.
// wrangler.jsonc
{ "send_email": [{ "name": "EMAIL" }] }
const response = await env.EMAIL.send({
to: "user@example.com",
from: { email: "welcome@yourdomain.com", name: "My App" },
subject: "Welcome!",
html: "<h1>Welcome!</h1>",
text: "Welcome!",
});
The binding is recommended for Workers — no API keys needed. If a user specifically requests the REST API from within a Worker (e.g., they already have an API token workflow), that works too — see rest-api.md.
See sending.md for the full API, batch sends, attachments, custom headers, restricted bindings, and Agents SDK integration.
Quick Start — REST API
For apps outside Workers, or within Workers if the user explicitly requests it. Key differences from the Workers binding:
- Endpoint:
POST https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/send
fromobject usesaddress(notemail):{ "address": "...", "name": "..." }
replyToisreply_to(snake_case)
- Response returns
{ delivered: [], permanent_bounces: [], queued: [] }(notmessageId)
See rest-api.md for curl examples, response format, and error handling.
Common Mistakes
Mistake
Why It Happens
Fix
Forgetting send_email binding in wrangler config
Email Service uses a binding, not an API key
Add "send_email": [{ "name": "EMAIL" }] to wrangler.jsonc
Sending from an unverified domain
Domain must be onboarded onto Email Sending before first send
Run wrangler email sending enable yourdomain.com or onboard in Dashboard
Reading message.raw twice in email handler
The raw stream is single-use — second read returns empty
Buffer first: const raw = await new Response(message.raw).arrayBuffer()
Missing text field (HTML only)
Some email clients only show plain text; also helps spam scores
Always include both html and text versions
Using email for marketing/bulk sends
Email Service is for transactional email only
Use a dedicated marketing email platform for newsletters and campaigns
Forwarding to unverified destinations
message.forward() only works with verified addresses
Run wrangler email routing addresses create user@gmail.com or add in Dashboard
Testing with fake addresses
Bounces from non-existent addresses hurt sender reputation
Use real addresses you control during development
Hardcoding API tokens in source code
Tokens in code get committed and leaked
Use environment variables or Cloudflare secrets
Ignoring the from domain requirement
The from address must use a domain onboarded to Email Service
Verify the domain first, then send from anything@that-domain.com
Using email key in REST API from object
REST API uses address not email for from object
Use { "address": "...", "name": "..." } for REST, { "email": "...", "name": "..." } for Workers
Using replyTo in REST API
REST API uses snake_case field names
Use reply_to for REST API, replyTo for Workers binding
References
Read the reference that matches your situation. You don't need all of them.
- references/sending.md — Workers binding API, attachments, Agents SDK email. For Workers or Agents SDK.
- references/rest-api.md — REST endpoint, curl examples, error handling. For apps NOT on Workers.
- references/routing.md — Inbound
email()handler, forwarding, replying, parsing. For receiving emails.
- references/cli-and-mcp.md — Domain setup, wrangler commands, MCP tools. For first-time setup.
- references/deliverability.md — SPF/DKIM/DMARC, bounces, suppressions, best practices.