# Cloudflare Email Workers: run code when an email arrives

> How to receive and process incoming email with Cloudflare Email Workers, parse it with postal-mime, and forward or act on it.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-07-03 | Topics: [Cloudflare](https://flaviocopes.com/tags/cloudflare/) | Canonical: https://flaviocopes.com/cloudflare-email-workers/

We usually think about *sending* email. But sometimes you want to *receive* it and do something. Turn a support email into a ticket, post it to Slack, or pull data out of an attachment.

**Email Workers** let you run code whenever an email arrives at an address you own. The email becomes the trigger, the same way an HTTP request triggers a normal Worker.

## How it fits together

First you set up **Email Routing** on your domain in the Cloudflare dashboard. That's what lets Cloudflare receive mail for you. Then you route an address to a Worker instead of forwarding it to an inbox.

While you're in the DNS settings, it's a good moment to check the records you need for *sending* mail from the domain too. I made a tool that generates and explains [SPF, DKIM and DMARC records](https://flaviocopes.com/tools/email-dns/).

Once that's done, every email to that address calls your Worker's `email` handler.

## The email handler

A normal Worker has a `fetch` handler. An Email Worker has an `email` handler:

```js
export default {
  async email(message, env, ctx) {
    console.log('From:', message.from)
    console.log('To:', message.to)
    console.log('Subject:', message.headers.get('subject'))
  },
}
```

The `message` gives you the basics right away: `from`, `to`, and the headers. For the subject and other header fields, you read them from `message.headers`, just like a normal Headers object.

## Read the body with postal-mime

The basics are easy, but the actual body of an email is messy. Real emails are MIME: multiple parts, text and HTML versions, different encodings, attachments. You don't want to parse that by hand.

The standard tool is [postal-mime](https://www.npmjs.com/package/postal-mime). Install it:

```bash
npm install postal-mime
```

Then parse the raw message:

```js
import PostalMime from 'postal-mime'

export default {
  async email(message, env, ctx) {
    const email = await PostalMime.parse(message.raw)

    console.log('Subject:', email.subject)
    console.log('Text:', email.text)
    console.log('HTML:', email.html)
    console.log('Attachments:', email.attachments)
  },
}
```

`message.raw` is the full email. `PostalMime.parse` turns it into a clean object with `subject`, `text`, `html`, and `attachments` ready to use.

## Forwarding

The simplest action is to forward the email somewhere:

```js
export default {
  async email(message, env, ctx) {
    await message.forward('team@example.com')
  },
}
```

The destination has to be a verified address in your Email Routing setup.

## A real example: route by recipient

Here's a pattern I like. One Worker handles several addresses and routes based on who the mail was sent to:

```js
export default {
  async email(message, env, ctx) {
    if (message.to.includes('support@')) {
      await message.forward('support-team@example.com')
    } else if (message.to.includes('sales@')) {
      await message.forward('sales-team@example.com')
    } else {
      await message.forward('hello@example.com')
    }
  },
}
```

Swap the forwards for whatever you need: write a row to D1, drop a job on a Queue, post to Slack.

## Test it locally

You don't need to send real email to test. Run `wrangler dev`, and it exposes a local endpoint you can POST a raw email to:

```bash
curl -X POST 'http://localhost:8787/cdn-cgi/handler/email' \
  --url-query 'from=sender@example.com' \
  --url-query 'to=recipient@example.com' \
  --header 'Content-Type: application/json' \
  --data-raw 'From: sender@example.com
To: recipient@example.com
Subject: Testing
Message-ID: <test@example.com>

Hello there'
```

That fires your `email` handler with the message, so you can build and debug without sending anything for real.

## Where this shines

Email Workers turn your inbox into an API. Anything that arrives by email can kick off code: support automation, parsing receipts, handling replies, catching bounces.

Pair it with the rest of the platform, a Queue for the slow work, D1 to store results, and you've got a real email pipeline with no mail server to run. The full reference is in the [Email Workers docs](https://developers.cloudflare.com/email-routing/email-workers/).
