Email at the edge: a send_email binding to send, email() to receive

env.EMAIL.send({ to, from, subject, html, text }) sends from a verified domain; Email Routing can hand inbound mail to a Worker's email() handler.

Workers

· Chapter

43

·

3

min read

The answer. Two separate products share the word "email". Sending is a binding: declare a send_email binding named EMAIL, verify the sending domain in Email Service, and call env.EMAIL.send() with plain fields; it returns a messageId. For a raw MIME message (attachments, custom headers) build it with mimetext and send an EmailMessage from cloudflare:email. Receiving is Email Routing: point an address or a catch-all at a Worker, and the runtime calls email(message, env, ctx) with the envelope, headers and a raw stream you can parse with postal-mime. From there message.forward() relays to a verified destination and message.reply() answers in-thread. The 2024 rule that "Cloudflare does not send mail" no longer holds; what remains true is that there are no mailboxes, only routing and sending.

The pattern.

{ "send_email": [{ "name": "EMAIL", "remote": true }] }   // remote: true = real sends during wrangler dev
export default {
  async fetch(request, env) {
    const r = await env.EMAIL.send({ to: "reader@example.org", from: "guide@example.com",
      subject: "Your guide", text: "Link inside.", html: "<p>Link inside.</p>" });
    return Response.json({ id: r.messageId });
  },
  async email(message, env, ctx) {                              // inbound via Email Routing
    if (message.headers.get("subject")?.startsWith("[bounce]")) return message.setReject("no");
    await message.forward("inbox@example.org");
  },
} satisfies ExportedHandler<Env>;

Watch out.

  • reply() refuses unless the inbound mail passed DMARC, the reply goes back to the sender, and the sending domain is the one that received it. It is for auto-replies, not outreach.
  • Local wrangler dev simulates sends (writes the MIME to a temp file) unless the binding has remote: true; then it really sends.
  • Deliverability is still your job: SPF, DKIM and DMARC on the sending domain, and a queue with retries in front of sends that must not be lost.

Related: queues-batches-acks-and-retries · rate-limiting-binding-counts-per-key-in-the-worker · provider-balance-checkin-cron-after-card-lapse