---
description: Send transactional email and route incoming email with Cloudflare Email Service. Arbitrary-recipient sending requires Workers Paid, while routing supports Workers Free and Paid.
title: Cloudflare Email Service
image: https://developers.cloudflare.com/email-service/og.png?v=9f1e4a6a5ef85e5b
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Cloudflare Email Service

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Send transactional email and route incoming email with Cloudflare Email Service. Arbitrary-recipient sending requires Workers Paid, while routing supports Workers Free and Paid.

Cloudflare Email Service sends transactional email and handles incoming email.

- **Email Sending** Beta sends outbound transactional emails. Available on Workers Paid plan
- **Email Routing** processes incoming emails with Workers or forwards them to destination addresses. Available on Free and Paid plans

Note

Sending to [verified destination addresses](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#destination-addresses) in your account is free on Workers Free and Paid plans, even when only Email Routing is configured.

Use Email Sending for account emails, order confirmations, and alerts. Use Email Routing to process or forward incoming messages.

Send email through the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/), or [authenticated SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/).

[Get started](https://developers.cloudflare.com/email-service/get-started/)

---

## Features

[Email Sending](https://developers.cloudflare.com/email-service/get-started/send-emails/)

Send transactional emails from Workers or other applications.

Use Email Sending

[Email Routing](https://developers.cloudflare.com/email-service/get-started/route-emails/)

Route incoming emails to custom addresses, Workers, or external destinations.

Use Email Routing

[Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/)

Review email authentication and sender reputation factors.

Use Deliverability

[Analytics & Observability](https://developers.cloudflare.com/email-service/observability/)

Review sending and routing activity, metrics, and analytics.

Use Analytics & Observability

[API](https://developers.cloudflare.com/email-service/api/)

Send and route emails using the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/), [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), or [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/).

Use API

---

## Related products

[Workers](https://developers.cloudflare.com/workers/)

Send and process email from Workers.

[Queues](https://developers.cloudflare.com/queues/)

Send Email Sending events to a queue.

[Analytics Engine](https://developers.cloudflare.com/analytics/)

Store custom metrics from email Workers.

---

## More resources

### [Platform limits](https://developers.cloudflare.com/email-service/platform/limits/)

Review Email Service limits and quotas.

### [Pricing](https://developers.cloudflare.com/email-service/platform/pricing/)

Review Email Service pricing and plans.

### [Examples](https://developers.cloudflare.com/email-service/examples/)

View Email Service code examples.

### [Discord](https://discord.cloudflare.com)

Ask questions and discuss Email Service with other developers.

### [Twitter](https://x.com/cloudflaredev)

Follow product announcements and developer updates.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"WebPage","@id":"https://developers.cloudflare.com/email-service/#page","headline":"Cloudflare Email Service","description":"Send transactional email and route incoming email with Cloudflare Email Service. Arbitrary-recipient sending requires Workers Paid, while routing supports Workers Free and Paid.","url":"https://developers.cloudflare.com/email-service/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/og.png?v=9f1e4a6a5ef85e5b","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
title: Email Routing
---

[Skip to content](#_top)

[API Reference](https://developers.cloudflare.com/api)

Copy Markdown

Open in **Claude**Open in **ChatGPT**Open in **Cursor**

---

**Copy Markdown****View as Markdown**

# Email Routing

##### [Get Email Routing settings](https://developers.cloudflare.com/api/resources/email_routing/methods/get)

GET/zones/{zone\_id}/email/routing

##### [Update Email Routing settings](https://developers.cloudflare.com/api/resources/email_routing/methods/edit)

PATCH/zones/{zone\_id}/email/routing

##### [Apply Email Routing settings](https://developers.cloudflare.com/api/resources/email_routing/methods/update)

PUT/zones/{zone\_id}/email/routing

##### [Disable Email Routing](https://developers.cloudflare.com/api/resources/email_routing/methods/disable)

Deprecated

POST/zones/{zone\_id}/email/routing/disable

##### [Enable Email Routing](https://developers.cloudflare.com/api/resources/email_routing/methods/enable)

Deprecated

POST/zones/{zone\_id}/email/routing/enable

##### [Unlock Email Routing](https://developers.cloudflare.com/api/resources/email_routing/methods/unlock)

Deprecated

POST/zones/{zone\_id}/email/routing/unlock

##### ModelsExpand Collapse

<details>

<summary>

Settings object {id, enabled, name, 6 more }

</summary>

id: string

Email Routing settings identifier.

maxLength32

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

enabled: trueor false

State of the zone settings for Email Routing.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

name: string

Domain of your zone.

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

created: optional string

The date and time the settings have been created.

formatdate-time

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

modified: optional string

The date and time the settings have been modified.

formatdate-time

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

<details>

<summary>

skip\_wizard: optional trueor false

Flag to check if the user skipped the configuration wizard.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20skip_wizard%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20skip_wizard%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20skip_wizard">Link to this property</a>

<details>

<summary>

status: optional "ready"or "unconfigured"or "misconfigured"or 2 more

Show the state of your account, and the type or configuration error.

</summary>

One of the following:

"ready"

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status%20%3E%20(member)%200">Link to this property</a>

"unconfigured"

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status%20%3E%20(member)%201">Link to this property</a>

"misconfigured"

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status%20%3E%20(member)%202">Link to this property</a>

"misconfigured/locked"

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status%20%3E%20(member)%203">Link to this property</a>

"unlocked"

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status%20%3E%20(member)%204">Link to this property</a>

</details>

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20status">Link to this property</a>

<details>

<summary>

support\_subaddress: optional trueor false

Whether subaddressing (plus-addressing) is honored when matching incoming mail against routing rules.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20support_subaddress%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20support_subaddress%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20support_subaddress">Link to this property</a>

Deprecatedtag: optional string

Email Routing settings tag. (Deprecated, replaced by Email Routing settings identifier)

maxLength32

<a href="#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing%20%3E%20(model)%20settings%20%3E%20(schema)>)

#### Email RoutingDNS

##### [Email Routing - DNS settings](https://developers.cloudflare.com/api/resources/email_routing/subresources/dns/methods/get)

GET/zones/{zone\_id}/email/routing/dns

##### [Enable Email Routing](https://developers.cloudflare.com/api/resources/email_routing/subresources/dns/methods/create)

POST/zones/{zone\_id}/email/routing/dns

##### [Unlock Email Routing DNS records](https://developers.cloudflare.com/api/resources/email_routing/subresources/dns/methods/edit)

PATCH/zones/{zone\_id}/email/routing/dns

##### [Disable Email Routing](https://developers.cloudflare.com/api/resources/email_routing/subresources/dns/methods/delete)

DELETE/zones/{zone\_id}/email/routing/dns

##### ModelsExpand Collapse

<details>

<summary>

DNSRecord object {content, name, priority, 2 more }

List of records needed to enable an Email Routing zone.

</summary>

content: optional string

DNS record content.

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20content">Link to this property</a>

name: optional string

DNS record name (or @ for the zone apex).

maxLength255

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

priority: optional number

Required for MX, SRV and URI records. Unused by other record types. Records with lower priorities are preferred.

maximum65535

minimum0

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20priority">Link to this property</a>

<details>

<summary>

ttl: optional numberor 1

Time to live, in seconds, of the DNS record. Must be between 60 and 86400, or 1 for ‘automatic’.

</summary>

One of the following:

number

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl%20%3E%20(variant)%200">Link to this property</a>

1

Time to live, in seconds, of the DNS record. Must be between 60 and 86400, or 1 for ‘automatic’.

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl">Link to this property</a>

<details>

<summary>

type: optional "A"or "AAAA"or "CNAME"or 15 more

DNS record type.

</summary>

One of the following:

"A"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"AAAA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"CNAME"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

"HTTPS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%203">Link to this property</a>

"TXT"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%204">Link to this property</a>

"SRV"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%205">Link to this property</a>

"LOC"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%206">Link to this property</a>

"MX"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%207">Link to this property</a>

"NS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%208">Link to this property</a>

"CERT"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%209">Link to this property</a>

"DNSKEY"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2010">Link to this property</a>

"DS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2011">Link to this property</a>

"NAPTR"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2012">Link to this property</a>

"SMIMEA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2013">Link to this property</a>

"SSHFP"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2014">Link to this property</a>

"SVCB"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2015">Link to this property</a>

"TLSA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2016">Link to this property</a>

"URI"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2017">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)>)

<details>

<summary>

DNSGetResponse = array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)">DNSRecord</a> { content, name, priority, 2 more }

</summary>

content: optional string

DNS record content.

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20content">Link to this property</a>

name: optional string

DNS record name (or @ for the zone apex).

maxLength255

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

priority: optional number

Required for MX, SRV and URI records. Unused by other record types. Records with lower priorities are preferred.

maximum65535

minimum0

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20priority">Link to this property</a>

<details>

<summary>

ttl: optional numberor 1

Time to live, in seconds, of the DNS record. Must be between 60 and 86400, or 1 for ‘automatic’.

</summary>

One of the following:

number

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl%20%3E%20(variant)%200">Link to this property</a>

1

Time to live, in seconds, of the DNS record. Must be between 60 and 86400, or 1 for ‘automatic’.

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20ttl">Link to this property</a>

<details>

<summary>

type: optional "A"or "AAAA"or "CNAME"or 15 more

DNS record type.

</summary>

One of the following:

"A"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"AAAA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"CNAME"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

"HTTPS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%203">Link to this property</a>

"TXT"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%204">Link to this property</a>

"SRV"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%205">Link to this property</a>

"LOC"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%206">Link to this property</a>

"MX"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%207">Link to this property</a>

"NS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%208">Link to this property</a>

"CERT"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%209">Link to this property</a>

"DNSKEY"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2010">Link to this property</a>

"DS"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2011">Link to this property</a>

"NAPTR"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2012">Link to this property</a>

"SMIMEA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2013">Link to this property</a>

"SSHFP"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2014">Link to this property</a>

"SVCB"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2015">Link to this property</a>

"TLSA"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2016">Link to this property</a>

"URI"

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%2017">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.dns%20%3E%20(model)%20dns_record%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.dns%20%3E%20(model)%20dns_get_response%20%3E%20(schema)>)

#### Email RoutingRules

##### [List account or zone routing rules](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/methods/list)

GET/{accounts\_or\_zones}/{account\_or\_zone\_id}/email/routing/rules

##### [Get routing rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/methods/get)

GET/zones/{zone\_id}/email/routing/rules/{rule\_identifier}

##### [Create routing rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/methods/create)

POST/zones/{zone\_id}/email/routing/rules

##### [Update routing rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/methods/update)

PUT/zones/{zone\_id}/email/routing/rules/{rule\_identifier}

##### [Delete routing rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/methods/delete)

DELETE/zones/{zone\_id}/email/routing/rules/{rule\_identifier}

##### ModelsExpand Collapse

<details>

<summary>

Action object {type, value }

Actions pattern.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of supported action.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)>)

<details>

<summary>

EmailRoutingRule object {id, actions, enabled, 5 more }

</summary>

id: optional string

Routing rule identifier.

maxLength32

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

actions: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)">Action</a> { type, value }

List actions patterns.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of supported action.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20actions">Link to this property</a>

<details>

<summary>

enabled: optional trueor false

Routing rule status.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

<details>

<summary>

matchers: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)">Matcher</a> { type, field, value }

Matching patterns to forward to your actions.

</summary>

<details>

<summary>

type: "all"or "literal"

Type of matcher.

</summary>

One of the following:

"all"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"literal"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

field: optional "to"

Field for type matcher.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20field">Link to this property</a>

value: optional string

Value for matcher.

maxLength90

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20matchers">Link to this property</a>

name: optional string

Routing rule name.

maxLength256

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

priority: optional number

Priority of the routing rule.

minimum0

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20priority">Link to this property</a>

<details>

<summary>

source: optional "api"or "wrangler"

Who manages the rule. <code>api</code> covers dashboard, generic API, and Terraform; <code>wrangler</code> means the rule is managed by a Worker’s wrangler.jsonc. Defaults to <code>api</code> when omitted on write.

</summary>

One of the following:

"api"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%200">Link to this property</a>

"wrangler"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20source">Link to this property</a>

Deprecatedtag: optional string

Routing rule tag. (Deprecated, replaced by routing rule identifier)

maxLength32

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules%20%3E%20(model)%20email_routing_rule%20%3E%20(schema)>)

<details>

<summary>

Matcher object {type, field, value }

Matching pattern to forward your actions.

</summary>

<details>

<summary>

type: "all"or "literal"

Type of matcher.

</summary>

One of the following:

"all"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"literal"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

field: optional "to"

Field for type matcher.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20field">Link to this property</a>

value: optional string

Value for matcher.

maxLength90

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)>)

#### Email RoutingRulesCatch Alls

##### [Get catch-all rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/subresources/catch_alls/methods/get)

GET/zones/{zone\_id}/email/routing/rules/catch\_all

##### [Update catch-all rule](https://developers.cloudflare.com/api/resources/email_routing/subresources/rules/subresources/catch_alls/methods/update)

PUT/zones/{zone\_id}/email/routing/rules/catch\_all

##### ModelsExpand Collapse

<details>

<summary>

CatchAllAction object {type, value }

Action for the catch-all routing rule.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of action for catch-all rule.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)>)

<details>

<summary>

CatchAllMatcher object {type }

Matcher for catch-all routing rule.

</summary>

type: "all"

Type of matcher. Default is ‘all’.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)>)

<details>

<summary>

CatchAllGetResponse object {id, actions, enabled, 4 more }

</summary>

id: optional string

Routing rule identifier.

maxLength32

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

actions: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)">CatchAllAction</a> { type, value }

List actions for the catch-all routing rule.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of action for catch-all rule.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20actions">Link to this property</a>

<details>

<summary>

enabled: optional trueor false

Routing rule status.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

<details>

<summary>

matchers: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)">CatchAllMatcher</a> { type }

List of matchers for the catch-all routing rule.

</summary>

type: "all"

Type of matcher. Default is ‘all’.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20matchers">Link to this property</a>

name: optional string

Routing rule name.

maxLength256

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

<details>

<summary>

source: optional "api"or "wrangler"

Who manages the rule. <code>api</code> covers dashboard, generic API, and Terraform; <code>wrangler</code> means the rule is managed by a Worker’s wrangler.jsonc. Defaults to <code>api</code> when omitted on write.

</summary>

One of the following:

"api"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%200">Link to this property</a>

"wrangler"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20source">Link to this property</a>

Deprecatedtag: optional string

Routing rule tag. (Deprecated, replaced by routing rule identifier)

maxLength32

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_get_response%20%3E%20(schema)>)

<details>

<summary>

CatchAllUpdateResponse object {id, actions, enabled, 4 more }

</summary>

id: optional string

Routing rule identifier.

maxLength32

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

actions: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)">CatchAllAction</a> { type, value }

List actions for the catch-all routing rule.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of action for catch-all rule.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20actions">Link to this property</a>

<details>

<summary>

enabled: optional trueor false

Routing rule status.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

<details>

<summary>

matchers: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)">CatchAllMatcher</a> { type }

List of matchers for the catch-all routing rule.

</summary>

type: "all"

Type of matcher. Default is ‘all’.

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20matchers">Link to this property</a>

name: optional string

Routing rule name.

maxLength256

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

<details>

<summary>

source: optional "api"or "wrangler"

Who manages the rule. <code>api</code> covers dashboard, generic API, and Terraform; <code>wrangler</code> means the rule is managed by a Worker’s wrangler.jsonc. Defaults to <code>api</code> when omitted on write.

</summary>

One of the following:

"api"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%200">Link to this property</a>

"wrangler"

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20source">Link to this property</a>

Deprecatedtag: optional string

Routing rule tag. (Deprecated, replaced by routing rule identifier)

maxLength32

<a href="#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.rules.catch_alls%20%3E%20(model)%20catch_all_update_response%20%3E%20(schema)>)

#### Email RoutingAccount Rules

##### [List account or zone routing rules](https://developers.cloudflare.com/api/resources/email_routing/subresources/account_rules/methods/list)

GET/{accounts\_or\_zones}/{account\_or\_zone\_id}/email/routing/rules

##### ModelsExpand Collapse

<details>

<summary>

AccountRule object {id, actions, enabled, 6 more }

</summary>

id: optional string

Routing rule identifier.

maxLength32

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

actions: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)">Action</a> { type, value }

List actions patterns.

</summary>

<details>

<summary>

type: "drop"or "forward"or "worker"

Type of supported action.

</summary>

One of the following:

"drop"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"forward"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

"worker"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%202">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

value: optional array of string

List of values for the action. Currently limited to a single value.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20action%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20actions">Link to this property</a>

<details>

<summary>

enabled: optional trueor false

Routing rule status.

</summary>

One of the following:

true

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%200">Link to this property</a>

false

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20enabled%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

<details>

<summary>

matchers: optional array of <a href="https://developers.cloudflare.com/api/resources/email_routing#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)">Matcher</a> { type, field, value }

Matching patterns to forward to your actions.

</summary>

<details>

<summary>

type: "all"or "literal"

Type of matcher.

</summary>

One of the following:

"all"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%200">Link to this property</a>

"literal"

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20type">Link to this property</a>

field: optional "to"

Field for type matcher.

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20field">Link to this property</a>

value: optional string

Value for matcher.

maxLength90

<a href="#(resource)%20email_routing.rules%20%3E%20(model)%20matcher%20%3E%20(schema)%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20matchers">Link to this property</a>

name: optional string

Routing rule name.

maxLength256

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

priority: optional number

Priority of the routing rule.

minimum0

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20priority">Link to this property</a>

<details>

<summary>

source: optional "api"or "wrangler"

Who manages the rule. <code>api</code> covers dashboard, generic API, and Terraform; <code>wrangler</code> means the rule is managed by a Worker’s wrangler.jsonc. Defaults to <code>api</code> when omitted on write.

</summary>

One of the following:

"api"

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%200">Link to this property</a>

"wrangler"

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20source%20%3E%20(member)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20source">Link to this property</a>

Deprecatedtag: optional string

Routing rule tag. (Deprecated, replaced by routing rule identifier)

maxLength32

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

<details>

<summary>

zone: optional object {name, tag }

Zone information for the routing rule.

</summary>

name: optional string

Zone name.

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20zone%20%3E%20(property)%20name">Link to this property</a>

tag: optional string

Zone tag.

maxLength32

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20zone%20%3E%20(property)%20tag">Link to this property</a>

</details>

<a href="#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)%20%3E%20(property)%20zone">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.account_rules%20%3E%20(model)%20account_rule%20%3E%20(schema)>)

#### Email RoutingAddresses

##### [List destination addresses](https://developers.cloudflare.com/api/resources/email_routing/subresources/addresses/methods/list)

GET/accounts/{account\_id}/email/routing/addresses

##### [Get a destination address](https://developers.cloudflare.com/api/resources/email_routing/subresources/addresses/methods/get)

GET/accounts/{account\_id}/email/routing/addresses/{destination\_address\_identifier}

##### [Create a destination address](https://developers.cloudflare.com/api/resources/email_routing/subresources/addresses/methods/create)

POST/accounts/{account\_id}/email/routing/addresses

##### [Update destination address](https://developers.cloudflare.com/api/resources/email_routing/subresources/addresses/methods/edit)

PATCH/accounts/{account\_id}/email/routing/addresses/{destination\_address\_identifier}

##### [Delete destination address](https://developers.cloudflare.com/api/resources/email_routing/subresources/addresses/methods/delete)

DELETE/accounts/{account\_id}/email/routing/addresses/{destination\_address\_identifier}

##### ModelsExpand Collapse

<details>

<summary>

Address object {id, created, email, 3 more }

</summary>

id: optional string

Destination address identifier.

maxLength32

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

created: optional string

The date and time the destination address has been created.

formatdate-time

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

email: optional string

The contact email address of the user.

maxLength90

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20email">Link to this property</a>

modified: optional string

The date and time the destination address was last modified.

formatdate-time

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

Deprecatedtag: optional string

Destination address tag. (Deprecated, replaced by destination address identifier)

maxLength32

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

verified: optional string

The date and time the destination address has been verified. Null means not verified yet.

formatdate-time

<a href="#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)%20%3E%20(property)%20verified">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_routing.addresses%20%3E%20(model)%20address%20%3E%20(schema)>)
---

---
title: Email Sending
---

[Skip to content](#_top)

[API Reference](https://developers.cloudflare.com/api)

Copy Markdown

Open in **Claude**Open in **ChatGPT**Open in **Cursor**

---

**Copy Markdown****View as Markdown**

# Email Sending

##### [Send an email](https://developers.cloudflare.com/api/resources/email_sending/methods/send)

POST/accounts/{account\_id}/email/sending/send

##### [Send a raw MIME email](https://developers.cloudflare.com/api/resources/email_sending/methods/send_raw)

POST/accounts/{account\_id}/email/sending/send\_raw

##### ModelsExpand Collapse

<details>

<summary>

EmailSendingSendResponse object {delivered, message\_id, permanent\_bounces, 2 more }

</summary>

delivered: array of string

Email addresses to which the message was delivered immediately.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)%20%3E%20(property)%20delivered">Link to this property</a>

message\_id: string

Message ID of the sent email.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)%20%3E%20(property)%20message_id">Link to this property</a>

permanent\_bounces: array of string

Email addresses that permanently bounced.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)%20%3E%20(property)%20permanent_bounces">Link to this property</a>

queued: array of string

Email addresses for which delivery was queued for later.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)%20%3E%20(property)%20queued">Link to this property</a>

suppressed\_recipients: array of string

Email addresses dropped because they are on the suppression list. Returned when suppressed-recipient dropping is enabled for the sending subdomain; otherwise the request fails instead.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)%20%3E%20(property)%20suppressed_recipients">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_response%20%3E%20(schema)>)

<details>

<summary>

EmailSendingSendRawResponse object {delivered, message\_id, permanent\_bounces, 2 more }

</summary>

delivered: array of string

Email addresses to which the message was delivered immediately.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)%20%3E%20(property)%20delivered">Link to this property</a>

message\_id: string

Message ID of the sent email.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)%20%3E%20(property)%20message_id">Link to this property</a>

permanent\_bounces: array of string

Email addresses that permanently bounced.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)%20%3E%20(property)%20permanent_bounces">Link to this property</a>

queued: array of string

Email addresses for which delivery was queued for later.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)%20%3E%20(property)%20queued">Link to this property</a>

suppressed\_recipients: array of string

Email addresses dropped because they are on the suppression list. Returned when suppressed-recipient dropping is enabled for the sending subdomain; otherwise the request fails instead.

<a href="#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)%20%3E%20(property)%20suppressed_recipients">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending%20%3E%20(model)%20email_sending_send_raw_response%20%3E%20(schema)>)

#### Email SendingSuppressions

##### [List account Email Sending suppressions](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/list)

GET/accounts/{account\_id}/email/sending/suppressions

##### [Get account Email Sending suppression](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/get)

GET/accounts/{account\_id}/email/sending/suppressions/{suppression\_id}

##### [Create account Email Sending suppression](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/create)

POST/accounts/{account\_id}/email/sending/suppressions

##### [Update account Email Sending suppression](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/edit)

PATCH/accounts/{account\_id}/email/sending/suppressions/{suppression\_id}

##### [Delete account Email Sending suppression](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/delete)

DELETE/accounts/{account\_id}/email/sending/suppressions/{suppression\_id}

##### [Bulk import account Email Sending suppressions](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/import)

POST/accounts/{account\_id}/email/sending/suppressions/bulk

##### ModelsExpand Collapse

<details>

<summary>

SuppressionListResponse object {id, created\_at, email, 5 more }

</summary>

id: string

Unique identifier for this suppression.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

created\_at: string

When the suppression was created.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20created_at">Link to this property</a>

email: string

The suppressed email address.

formatemail

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20email">Link to this property</a>

expires\_at: string

When the suppression expires. Null for a permanent suppression.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20expires_at">Link to this property</a>

read\_only: boolean

Whether clients may mutate this suppression. This is determined by the server and must not be inferred from <code>reason</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20read_only">Link to this property</a>

reason: string

Why the address is suppressed: <code>manual</code>, <code>complaint</code>, <code>hard_bounce</code>, <code>soft_bounce</code>, or <code>policy</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20reason">Link to this property</a>

note: optional string

Advisory note for this suppression, if any.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20note">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)%20%3E%20(property)%20scope">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_list_response%20%3E%20(schema)>)

<details>

<summary>

SuppressionGetResponse object {id, created\_at, email, 5 more }

</summary>

id: string

Unique identifier for this suppression.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

created\_at: string

When the suppression was created.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20created_at">Link to this property</a>

email: string

The suppressed email address.

formatemail

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20email">Link to this property</a>

expires\_at: string

When the suppression expires. Null for a permanent suppression.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20expires_at">Link to this property</a>

read\_only: boolean

Whether clients may mutate this suppression. This is determined by the server and must not be inferred from <code>reason</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20read_only">Link to this property</a>

reason: string

Why the address is suppressed: <code>manual</code>, <code>complaint</code>, <code>hard_bounce</code>, <code>soft_bounce</code>, or <code>policy</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20reason">Link to this property</a>

note: optional string

Advisory note for this suppression, if any.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20note">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)%20%3E%20(property)%20scope">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_get_response%20%3E%20(schema)>)

<details>

<summary>

SuppressionCreateResponse object {id, scope }

</summary>

id: string

The suppression’s identifier.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)%20%3E%20(property)%20scope">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_create_response%20%3E%20(schema)>)

<details>

<summary>

SuppressionEditResponse object {id, created\_at, email, 5 more }

</summary>

id: string

Unique identifier for this suppression.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

created\_at: string

When the suppression was created.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20created_at">Link to this property</a>

email: string

The suppressed email address.

formatemail

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20email">Link to this property</a>

expires\_at: string

When the suppression expires. Null for a permanent suppression.

formatdate-time

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20expires_at">Link to this property</a>

read\_only: boolean

Whether clients may mutate this suppression. This is determined by the server and must not be inferred from <code>reason</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20read_only">Link to this property</a>

reason: string

Why the address is suppressed: <code>manual</code>, <code>complaint</code>, <code>hard_bounce</code>, <code>soft_bounce</code>, or <code>policy</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20reason">Link to this property</a>

note: optional string

Advisory note for this suppression, if any.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20note">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)%20%3E%20(property)%20scope">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_edit_response%20%3E%20(schema)>)

<details>

<summary>

SuppressionDeleteResponse object {id, scope }

</summary>

id: string

The suppression’s identifier.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20id">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)%20%3E%20(property)%20scope">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_delete_response%20%3E%20(schema)>)

<details>

<summary>

SuppressionImportResponse object {deduplicated, errors, invalid, 4 more }

</summary>

deduplicated: number

Number of items dropped because their email address and scope repeated an earlier item in this request. Counted once and excluded from <code>items</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20deduplicated">Link to this property</a>

errors: number

Number of items that failed to import due to an unexpected error.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20errors">Link to this property</a>

invalid: number

Number of items with an invalid email address or sending domain.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20invalid">Link to this property</a>

<details>

<summary>

items: array of object {index, status, id, 3 more }

Per-item results, in the same order as the request body.

</summary>

index: number

Zero-based index of this item in the request body.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20index">Link to this property</a>

<details>

<summary>

status: "processed"or "invalid"or "error"or "skipped"

Outcome for this item.

</summary>

One of the following:

"processed"

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20status%20%3E%20(member)%200">Link to this property</a>

"invalid"

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20status%20%3E%20(member)%201">Link to this property</a>

"error"

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20status%20%3E%20(member)%202">Link to this property</a>

"skipped"

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20status%20%3E%20(member)%203">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20status">Link to this property</a>

id: optional string

The created or promoted suppression’s identifier. Present when <code>status</code> is <code>processed</code>.

formatuuid

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20id">Link to this property</a>

email: optional string

The submitted email address for this item.

formatemail

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20email">Link to this property</a>

error: optional string

Human-readable error message. Present when <code>status</code> is <code>invalid</code>, <code>error</code>, or <code>skipped</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20error">Link to this property</a>

<details>

<summary>

scope: optional object {type } or object {type, value }

Where the suppression applies: <code>account</code> for every sending domain of the account, or <code>sending_domain</code> for one envelope MAIL FROM domain.

</summary>

One of the following:

<details>

<summary>

Type object {type }

</summary>

type: "account"

Blocks the recipient for every sending domain of the account.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope%20%3E%20(variant)%200%20%3E%20(property)%20type">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope%20%3E%20(variant)%200">Link to this property</a>

<details>

<summary>

object {type, value }

</summary>

type: "sending\_domain"

Blocks the recipient only for mail whose envelope MAIL FROM uses <code>value</code>.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20type">Link to this property</a>

value: string

The sending domain: the domain part of the envelope MAIL FROM, lowercase, without a trailing dot.

maxLength1024

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope%20%3E%20(variant)%201%20%3E%20(property)%20value">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope%20%3E%20(variant)%201">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items%20%3E%20(items)%20%3E%20(property)%20scope">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20items">Link to this property</a>

processed: number

Number of items successfully created or promoted.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20processed">Link to this property</a>

skipped: number

Number of items skipped because the existing suppression is not customer-managed (for example, a read-only policy suppression).

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20skipped">Link to this property</a>

total: number

Total number of items in the request body, including duplicates.

<a href="#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)%20%3E%20(property)%20total">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.suppressions%20%3E%20(model)%20suppression_import_response%20%3E%20(schema)>)

#### Email SendingSubdomains

##### [List sending subdomains](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/methods/list)

GET/zones/{zone\_id}/email/sending/subdomains

##### [Get a sending subdomain](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/methods/get)

GET/zones/{zone\_id}/email/sending/subdomains/{subdomain\_id}

##### [Create a sending subdomain](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/methods/create)

POST/zones/{zone\_id}/email/sending/subdomains

##### [Update a sending subdomain](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/methods/edit)

PATCH/zones/{zone\_id}/email/sending/subdomains/{subdomain\_id}

##### [Delete a sending subdomain](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/methods/delete)

DELETE/zones/{zone\_id}/email/sending/subdomains/{subdomain\_id}

##### ModelsExpand Collapse

<details>

<summary>

SubdomainListResponse object {enabled, name, tag, 6 more }

</summary>

enabled: boolean

Whether Email Sending is enabled on this subdomain.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

name: string

The exact domain name or a leftmost wildcard such as <code>*.example.com</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

tag: string

Sending subdomain identifier.

maxLength32

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

created: optional string

The date and time the destination address has been created.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

dkim\_selector: optional string

The DKIM selector used for email signing. Wildcard rows publish the selector and sign with <code>d=&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20dkim_selector">Link to this property</a>

drop\_suppressed\_recipients: optional boolean

Whether a send request that includes a recipient suppressed on this subdomain drops that recipient and still delivers to the rest, instead of failing the entire request.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20drop_suppressed_recipients">Link to this property</a>

modified: optional string

The date and time the destination address was last modified.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

preview\_enabled: optional boolean

Whether sent messages from this subdomain can be previewed in the activity log.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20preview_enabled">Link to this property</a>

return\_path\_domain: optional string

The return-path domain used for bounce handling. Wildcard rows use <code>cf-bounce.&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)%20%3E%20(property)%20return_path_domain">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_list_response%20%3E%20(schema)>)

<details>

<summary>

SubdomainGetResponse object {enabled, name, tag, 6 more }

</summary>

enabled: boolean

Whether Email Sending is enabled on this subdomain.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

name: string

The exact domain name or a leftmost wildcard such as <code>*.example.com</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

tag: string

Sending subdomain identifier.

maxLength32

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

created: optional string

The date and time the destination address has been created.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

dkim\_selector: optional string

The DKIM selector used for email signing. Wildcard rows publish the selector and sign with <code>d=&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20dkim_selector">Link to this property</a>

drop\_suppressed\_recipients: optional boolean

Whether a send request that includes a recipient suppressed on this subdomain drops that recipient and still delivers to the rest, instead of failing the entire request.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20drop_suppressed_recipients">Link to this property</a>

modified: optional string

The date and time the destination address was last modified.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

preview\_enabled: optional boolean

Whether sent messages from this subdomain can be previewed in the activity log.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20preview_enabled">Link to this property</a>

return\_path\_domain: optional string

The return-path domain used for bounce handling. Wildcard rows use <code>cf-bounce.&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)%20%3E%20(property)%20return_path_domain">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_get_response%20%3E%20(schema)>)

<details>

<summary>

SubdomainCreateResponse object {enabled, name, tag, 6 more }

</summary>

enabled: boolean

Whether Email Sending is enabled on this subdomain.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

name: string

The exact domain name or a leftmost wildcard such as <code>*.example.com</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

tag: string

Sending subdomain identifier.

maxLength32

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

created: optional string

The date and time the destination address has been created.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

dkim\_selector: optional string

The DKIM selector used for email signing. Wildcard rows publish the selector and sign with <code>d=&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20dkim_selector">Link to this property</a>

drop\_suppressed\_recipients: optional boolean

Whether a send request that includes a recipient suppressed on this subdomain drops that recipient and still delivers to the rest, instead of failing the entire request.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20drop_suppressed_recipients">Link to this property</a>

modified: optional string

The date and time the destination address was last modified.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

preview\_enabled: optional boolean

Whether sent messages from this subdomain can be previewed in the activity log.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20preview_enabled">Link to this property</a>

return\_path\_domain: optional string

The return-path domain used for bounce handling. Wildcard rows use <code>cf-bounce.&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)%20%3E%20(property)%20return_path_domain">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_create_response%20%3E%20(schema)>)

<details>

<summary>

SubdomainEditResponse object {enabled, name, tag, 6 more }

</summary>

enabled: boolean

Whether Email Sending is enabled on this subdomain.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20enabled">Link to this property</a>

name: string

The exact domain name or a leftmost wildcard such as <code>*.example.com</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20name">Link to this property</a>

tag: string

Sending subdomain identifier.

maxLength32

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20tag">Link to this property</a>

created: optional string

The date and time the destination address has been created.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20created">Link to this property</a>

dkim\_selector: optional string

The DKIM selector used for email signing. Wildcard rows publish the selector and sign with <code>d=&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20dkim_selector">Link to this property</a>

drop\_suppressed\_recipients: optional boolean

Whether a send request that includes a recipient suppressed on this subdomain drops that recipient and still delivers to the rest, instead of failing the entire request.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20drop_suppressed_recipients">Link to this property</a>

modified: optional string

The date and time the destination address was last modified.

formatdate-time

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20modified">Link to this property</a>

preview\_enabled: optional boolean

Whether sent messages from this subdomain can be previewed in the activity log.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20preview_enabled">Link to this property</a>

return\_path\_domain: optional string

The return-path domain used for bounce handling. Wildcard rows use <code>cf-bounce.&lt;base&gt;</code>.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)%20%3E%20(property)%20return_path_domain">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_edit_response%20%3E%20(schema)>)

<details>

<summary>

SubdomainDeleteResponse object {errors, messages, success }

</summary>

<details>

<summary>

errors: array of object {code, message, documentation\_url, source }

</summary>

code: number

minimum1000

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors%20%3E%20(items)%20%3E%20(property)%20code">Link to this property</a>

message: string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors%20%3E%20(items)%20%3E%20(property)%20message">Link to this property</a>

documentation\_url: optional string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors%20%3E%20(items)%20%3E%20(property)%20documentation_url">Link to this property</a>

<details>

<summary>

source: optional object {pointer }

</summary>

pointer: optional string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors%20%3E%20(items)%20%3E%20(property)%20source%20%3E%20(property)%20pointer">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors%20%3E%20(items)%20%3E%20(property)%20source">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20errors">Link to this property</a>

<details>

<summary>

messages: array of object {code, message, documentation\_url, source }

</summary>

code: number

minimum1000

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages%20%3E%20(items)%20%3E%20(property)%20code">Link to this property</a>

message: string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages%20%3E%20(items)%20%3E%20(property)%20message">Link to this property</a>

documentation\_url: optional string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages%20%3E%20(items)%20%3E%20(property)%20documentation_url">Link to this property</a>

<details>

<summary>

source: optional object {pointer }

</summary>

pointer: optional string

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages%20%3E%20(items)%20%3E%20(property)%20source%20%3E%20(property)%20pointer">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages%20%3E%20(items)%20%3E%20(property)%20source">Link to this property</a>

</details>

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20messages">Link to this property</a>

success: true

Whether the API call was successful.

<a href="#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)%20%3E%20(property)%20success">Link to this property</a>

</details>

[Link to this property](<#(resource)%20email_sending.subdomains%20%3E%20(model)%20subdomain_delete_response%20%3E%20(schema)>)

#### Email SendingSubdomainsDNS

##### [Get sending subdomain DNS records](https://developers.cloudflare.com/api/resources/email_sending/subresources/subdomains/subresources/dns/methods/get)

GET/zones/{zone\_id}/email/sending/subdomains/{subdomain\_id}/dns
---

---
description: Process incoming emails with the email() handler in Cloudflare Workers to forward, reply, or reject messages.
title: Workers API
image: https://developers.cloudflare.com/email-service/api/route-emails/email-handler/og.png?v=bce7e41c11423abf
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Workers API

Process incoming emails with the email() handler in Cloudflare Workers. Forward, reply, reject, or process emails programmatically.

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Process incoming emails using the `email()` handler in your Cloudflare Workers. This allows you to programmatically handle email routing with custom logic.

## Email handler syntax

Add the `email` handler function to your Worker's exported handlers:

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		// Process incoming email
		await message.forward("destination@example.com");
	},
} satisfies ExportedHandler<Env>;
```

```python
from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def email(self, message, env, ctx):
        await message.forward("destination@example.com")
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `message` | `ForwardableEmailMessage` | The incoming email message |
| `env` | `object` | Worker environment bindings (KV, EMAIL, etc.) |
| `ctx` | `object` | Execution context with `waitUntil` function |

## `ForwardableEmailMessage` interface

The `message` parameter provides access to the incoming email:

```ts
interface ForwardableEmailMessage {
	readonly from: string; // Sender email address (envelope MAIL FROM)
	readonly to: string; // Recipient email address (envelope RCPT TO)
	readonly headers: Headers; // Email headers (Subject, Message-ID, etc.)
	readonly raw: ReadableStream; // Raw MIME email content stream
	readonly rawSize: number; // Size of raw email in bytes
	readonly canBeForwarded: boolean; // Whether the message can be forwarded

	// Actions
	setReject(reason: string): void;
	forward(rcptTo: string, headers?: Headers): Promise<EmailSendResult>;
	reply(message: EmailMessage): Promise<EmailSendResult>;
}
```

### Properties

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		// Access email metadata
		console.log(`From: ${message.from}`);
		console.log(`To: ${message.to}`);
		console.log(`Size: ${message.rawSize} bytes`);

		// Access headers
		const subject = message.headers.get("subject");
		const date = message.headers.get("date");
		const messageId = message.headers.get("message-id");

		console.log(`Subject: ${subject}`);
		console.log(`Date: ${date}`);
		console.log(`Message-ID: ${messageId}`);
	},
};
```

Use [`postal-mime` ↗︎](https://www.npmjs.com/package/postal-mime) to parse the MIME structure of an incoming email. The parser handles multipart boundaries, transfer encodings, and character sets correctly.

```ts
import PostalMime from "postal-mime";

export default {
	async email(message, env, ctx): Promise<void> {
		const email = await PostalMime.parse(message.raw);

		console.log(`Subject: ${email.subject}`);
		console.log(`Text: ${email.text}`);
		console.log(`HTML: ${email.html}`);
	},
};
```

## Email actions

### Forward emails

Forward incoming emails to verified destination addresses:

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		// Forward to a single address
		await message.forward("team@example.com");
	},
};
```

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		const recipient = message.to;
		const subject = message.headers.get("subject") || "";

		// Route based on recipient
		if (recipient.includes("support@")) {
			await message.forward("support-team@example.com");
		} else if (recipient.includes("sales@")) {
			await message.forward("sales-team@example.com");
		} else if (subject.toLowerCase().includes("urgent")) {
			await message.forward("urgent@example.com");
		} else {
			// Default routing
			await message.forward("general@example.com");
		}
	},
};
```

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		const subject = message.headers.get("subject") || "";

		if (subject.toLowerCase().includes("security")) {
			// Forward to multiple addresses for security issues
			await Promise.all([
				message.forward("security@example.com"),
				message.forward("admin@example.com"),
				message.forward("ciso@example.com"),
			]);
		} else {
			await message.forward("general@example.com");
		}
	},
};
```

### Forward with custom headers

Add custom headers when forwarding. Only headers with an `X-` prefix can be added through `forward()`. Other headers are removed.

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		// Create custom headers
		const customHeaders = new Headers();
		customHeaders.set("X-Processed-By", "Email-Worker");
		customHeaders.set("X-Processing-Time", new Date().toISOString());
		customHeaders.set("X-Original-Recipient", message.to);
		customHeaders.set("X-Spam-Score", "0.1"); // Example spam score

		// Forward with custom headers
		await message.forward("processed@example.com", customHeaders);
	},
};
```

### Reply to emails

Send automatic replies with `message.reply()`. Replies built this way are threaded with the original message and pass through the same SMTP session, so they preserve the original `Message-ID` chain.

Replies through the Workers API must satisfy the following requirements, otherwise `reply()` throws an exception:

- The incoming email must have a valid DMARC result.
- An email can only be replied to once per `EmailMessage` event.
- The recipient in the reply must match the sender of the incoming email.
- The outgoing sender domain must match the domain that received the email.
- The reply is rejected if the incoming email has more than 100 entries in its `References` header, to prevent reply loops and abuse.

The reply payload is an `EmailMessage` built from a raw MIME string. The examples below use [`mimetext` ↗︎](https://www.npmjs.com/package/mimetext) to build the MIME body. The `mimetext` package requires the [`nodejs_compat`](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) compatibility flag.

```ts
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

export default {
	async email(message, env, ctx): Promise<void> {
		const subject = message.headers.get("subject") || "";
		const messageId = message.headers.get("Message-ID");

		const reply = createMimeMessage();
		if (messageId) {
			reply.setHeader("In-Reply-To", messageId);
			reply.setHeader("References", messageId);
		}
		reply.setSender(message.to);
		reply.setRecipient(message.from);
		reply.setSubject(`Re: ${subject}`);
		reply.addMessage({
			contentType: "text/plain",
			data: "Thank you for your message. We have received your email and will respond shortly.",
		});
		reply.addMessage({
			contentType: "text/html",
			data: "<h1>Thank you for your message</h1><p>We have received your email and will respond shortly.</p>",
		});

		await message.reply(
			new EmailMessage(message.to, message.from, reply.asRaw()),
		);

		// Also forward to human team
		await message.forward("team@example.com");
	},
};
```

```ts
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

export default {
	async email(message, env, ctx): Promise<void> {
		const sender = message.from;
		const recipient = message.to;
		const subject = message.headers.get("subject") || "";
		const messageId = message.headers.get("Message-ID");

		// Don't reply to automated emails
		if (
			sender.includes("noreply") ||
			sender.includes("no-reply") ||
			subject.toLowerCase().includes("automated")
		) {
			await message.forward("team@example.com");
			return;
		}

		// Customized auto-reply based on recipient
		let html = "";

		if (recipient.includes("support@")) {
			html = `
                <h1>Support Request Received</h1>
                <p>Thank you for contacting support. Your request has been assigned ticket #${Date.now()}.</p>
                <p>Expected response time: 2-4 hours during business hours.</p>
            `;
		} else if (recipient.includes("sales@")) {
			html = `
                <h1>Sales Inquiry Received</h1>
                <p>Thank you for your interest in our products.</p>
                <p>A sales representative will contact you within 24 hours.</p>
            `;
		} else {
			html = `
                <h1>Message Received</h1>
                <p>Thank you for your message. We will respond within 2 business days.</p>
            `;
		}

		const reply = createMimeMessage();
		if (messageId) {
			reply.setHeader("In-Reply-To", messageId);
			reply.setHeader("References", messageId);
		}
		reply.setSender(recipient);
		reply.setRecipient(sender);
		reply.setSubject(`Re: ${subject}`);
		reply.addMessage({
			contentType: "text/plain",
			data: html.replace(/<[^>]*>/g, ""),
		});
		reply.addMessage({ contentType: "text/html", data: html });

		await message.reply(new EmailMessage(recipient, sender, reply.asRaw()));

		// Forward to appropriate team
		await message.forward("team@example.com");
	},
};
```

### Reject emails

Reject emails with a permanent SMTP error:

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		const sender = message.from;

		// Block specific senders
		const blockedDomains = ["spam.com", "unwanted.net"];
		const senderDomain = sender.split("@")[1];

		if (blockedDomains.includes(senderDomain)) {
			message.setReject("Sender domain not allowed");
			return;
		}

		// Continue processing
		await message.forward("inbox@example.com");
	},
};
```

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		const subject = message.headers.get("subject") || "";

		// Reject based on subject content
		const spamKeywords = ["buy now", "limited time", "act fast", "urgent"];
		const containsSpam = spamKeywords.some((keyword) =>
			subject.toLowerCase().includes(keyword),
		);

		if (containsSpam) {
			message.setReject("Message appears to be spam");
			return;
		}

		// Check message size
		if (message.rawSize > 25 * 1024 * 1024) {
			// 25 MiB limit (inbound message size)
			message.setReject("Message too large");
			return;
		}

		// Continue processing
		await message.forward("inbox@example.com");
	},
};
```

## Error handling

Handle errors gracefully in email processing:

```ts
export default {
	async email(message, env, ctx): Promise<void> {
		try {
			// Main email processing logic
			await processEmail(message, env);
		} catch (error) {
			console.error("Email processing failed:", error);

			// Log error for monitoring
			if (env.ERROR_LOGS) {
				await env.ERROR_LOGS.put(
					`error-${Date.now()}`,
					JSON.stringify({
						error: error.message,
						stack: error.stack,
						from: message.from,
						to: message.to,
						timestamp: new Date().toISOString(),
					}),
				);
			}

			// Fallback: forward to admin
			try {
				await message.forward("admin@example.com");
			} catch (fallbackError) {
				console.error("Fallback forwarding failed:", fallbackError);
				// Last resort: reject the email
				message.setReject("Internal processing error");
			}
		}
	},
};

async function processEmail(message, env) {
	// Your main email processing logic here
	const recipient = message.to;

	if (recipient.includes("support@")) {
		await message.forward("support@example.com");
	} else if (recipient.includes("sales@")) {
		await message.forward("sales@example.com");
	} else {
		await message.forward("general@example.com");
	}
}
```

## Next steps

- Test locally: [Email routing development](https://developers.cloudflare.com/email-service/observability/local-development/routing/)
- Manage rules and addresses programmatically with the [Email Routing REST API](https://developers.cloudflare.com/email-service/api/email-routing-rest-api/)
- Set up [email routing configuration](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/)
- Refer to [email routing examples](https://developers.cloudflare.com/email-service/examples/email-routing/) for advanced email processing
- Filter spam with Workers using the [spam filtering example](https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/)

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/route-emails/email-handler/#page","headline":"Workers API","description":"Process incoming emails with the email() handler in Cloudflare Workers to forward, reply, or reject messages.","url":"https://developers.cloudflare.com/email-service/api/route-emails/email-handler/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/api/route-emails/email-handler/og.png?v=bce7e41c11423abf","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Sending REST API requests, responses, authentication, and errors.
title: REST API
image: https://developers.cloudflare.com/email-service/api/send-emails/rest-api/og.png?v=a3dd3c9e3f6f70ef
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# REST API

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

The REST API allows you to send emails from any application using a standard HTTP request to `POST /accounts/{account_id}/email/sending/send`. Use it from any backend, serverless function, or CI/CD pipeline — no Cloudflare Workers binding is required.

For the full OpenAPI specification, refer to the [Email Sending API reference](https://developers.cloudflare.com/api/resources/email_sending/methods/send/).

Cloudflare also provides official SDKs for the REST API: [Node](https://developers.cloudflare.com/api/node/), [Python](https://developers.cloudflare.com/api/python/), and [Go](https://developers.cloudflare.com/api/go/).

## Authentication

Authenticate with a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) that has permission to send emails. Include it in the `Authorization` header:

```txt
Authorization: Bearer <API_TOKEN>
```

## Request example

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "recipient@example.com",
    "from": "welcome@yourdomain.com",
    "subject": "Welcome to our service!",
    "html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
    "text": "Welcome! Thanks for signing up."
  }'
```

For multiple recipients, CC/BCC, and named addresses, refer to [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/).

## Attachments

Send files by including base64-encoded content in the `attachments` array. The total message size must not exceed **5 MiB** (including attachments).

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "customer@example.com",
    "from": "invoices@yourdomain.com",
    "subject": "Your Invoice",
    "html": "<h1>Invoice attached</h1><p>Please find your invoice attached.</p>",
    "attachments": [
      {
        "content": "JVBERi0xLjQKJeLjz9MK...",
        "filename": "invoice-12345.pdf",
        "type": "application/pdf",
        "disposition": "attachment"
      }
    ]
  }'
```

For inline images and file uploads, refer to [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/).

## Custom headers

Set custom headers for threading, list management, or tracking. Refer to the [email headers reference](https://developers.cloudflare.com/email-service/reference/headers/) for the full list of allowed headers.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "user@example.com",
    "from": "notifications@yourdomain.com",
    "subject": "Your weekly digest",
    "html": "<h1>Weekly Digest</h1>",
    "headers": {
      "List-Unsubscribe": "<https://yourdomain.com/unsubscribe?id=abc123>",
      "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
      "X-Campaign-ID": "weekly-digest-2026-03"
    }
  }'
```

## Response

A successful response returns the delivery status for each recipient:

```json
{
	"success": true,
	"errors": [],
	"messages": [],
	"result": {
		"delivered": ["recipient@example.com"],
		"permanent_bounces": [],
		"queued": []
	}
}
```

- `delivered` - Email addresses to which the message was delivered immediately
- `permanent_bounces` - Email addresses that permanently bounced
- `queued` - Email addresses for which delivery was queued for later

Workers binding vs REST API responses

The REST API returns recipient-grouped delivery status. The [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) returns a single `messageId` per `send()` call instead.

## Error responses

The REST API returns standard Cloudflare API error responses. A failed request returns an `errors` array with numeric error codes and machine-readable messages:

```json
{
	"success": false,
	"errors": [
		{
			"code": 10001,
			"message": "email.sending.error.invalid_request_schema"
		}
	],
	"messages": [],
	"result": null
}
```

The REST API returns the following error codes:

| HTTP status | Code | Message | Description |
| --- | --- | --- | --- |
| 400 | 10001 | `email.sending.error.invalid_request_schema` | Invalid request format |
| 400 | 10200 | `email.sending.error.email.too_big` | Email exceeds size limit |
| 400 | 10201 | `email.sending.error.email.no_content_length` | Missing content length |
| 400 | 10202 | `email.sending.error.email.invalid` | Invalid email content |
| 401 | 10101 | `email.sending.error.authentication.unauthorized` | Missing or invalid API token |
| 401 | 10103 | `email.sending.error.authentication.bad_token_type` | Wrong token type for this endpoint |
| 403 | 10102 | `email.sending.error.authentication.forbidden` | Token lacks permission to send |
| 403 | 10105 | `email.sending.error.authentication.not_entitled` | Account not entitled to use Email Sending |
| 403 | 10203 | `email.sending.error.email.sending_disabled` | Sending disabled for this zone or account |
| 404 | 10000 | `email.sending.error.not_found` | Resource not found |
| 429 | 10004 | `email.sending.error.throttled` | Rate limit exceeded |
| 500 | 10002 | `email.sending.error.internal_server` | Internal server error |
| 500 | 10003 | `email.sending.error.not_implemented` | Operation not implemented |
| 503 | 10100 | `email.sending.error.authentication.upstream` | Authentication service temporarily unavailable |

Workers binding vs REST API errors

The REST API returns standard Cloudflare API numeric error codes, while the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) throws errors with string codes (for example, `E_SENDER_NOT_VERIFIED`). Refer to the [Workers API error codes table](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#error-codes) for the string error codes.

## Related resources

- Refer to the [Email Sending API reference](https://developers.cloudflare.com/api/resources/email_sending/methods/send/) for the full request and response schemas.
- Refer to the [Email Sending Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) for sending emails directly from Cloudflare Workers using bindings.
- Refer to [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) for sending from any SMTP-capable application or mail client.
- Review [email headers](https://developers.cloudflare.com/email-service/reference/headers/) for threading, list management, and custom tracking headers.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/rest-api/#page","headline":"REST API","description":"Email Sending REST API requests, responses, authentication, and errors.","url":"https://developers.cloudflare.com/email-service/api/send-emails/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/api/send-emails/rest-api/og.png?v=a3dd3c9e3f6f70ef","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Service SMTP endpoint, authentication, limits, responses, and errors.
title: SMTP
image: https://developers.cloudflare.com/email-service/api/send-emails/smtp/og.png?v=13757de7404f5225
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# SMTP

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/api/send-emails/smtp/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Cloudflare Email Service exposes an authenticated SMTP submission endpoint so you can send emails from any application, framework, or off-the-shelf mail client that speaks SMTP. Use SMTP when the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) and the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) are not a good fit — for example, when integrating an existing application that already speaks SMTP, or a language-native SMTP library (Nodemailer, `smtplib`, PHPMailer, JavaMail).

Emails submitted over SMTP enter the same delivery pipeline as the REST API and the Workers binding: they are subject to the same [limits](https://developers.cloudflare.com/email-service/platform/limits/), receive the same DKIM and ARC signing, and produce the same delivery logs.

## Endpoint

```txt
smtp.mx.cloudflare.net:465
```

| Setting | Value |
| --- | --- |
| Host | `smtp.mx.cloudflare.net` |
| Port | `465` |
| Security | Implicit TLS (also called SMTPS) |
| SMTP `AUTH` | `PLAIN` or `LOGIN` |
| Username | The literal string `api_token` |
| Password | A Cloudflare API token (refer to [Prerequisites](#prerequisites)) |

Cloudflare only offers SMTP submission on port `465` with implicit TLS. Plaintext SMTP, opportunistic `STARTTLS` on port `587`, and unauthenticated relay on port `25` are not supported for outbound submission. Port `25` is reserved for inbound mail to [Email Routing](https://developers.cloudflare.com/email-service/api/route-emails/).

## Prerequisites

Before you can send emails over SMTP, you need:

1. An account with [Email Sending](https://developers.cloudflare.com/email-service/) enabled.
2. At least one [domain onboarded](https://developers.cloudflare.com/email-service/configuration/domains/) under **Email Service > Email Sending** in the Cloudflare dashboard.
3. A [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the **Email Sending: Edit** permission. Both account-owned (recommended) and user-owned tokens are accepted; the token is used as the SMTP password.

Treat this token as a credential. Anyone with it can send email from any onboarded domain on the matching account.

## Quickstart

Send an email with a single `curl` command. Replace `<API_TOKEN>` with a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) that has the **Email Sending: Edit** permission, and replace the `--mail-from` and `--mail-rcpt` addresses with your own.

```sh
cat > mail.txt <<EOF
From: welcome@yourdomain.com
To: recipient@example.com
Subject: Welcome to our service!

Thanks for signing up.
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "welcome@yourdomain.com" \
  --mail-rcpt "recipient@example.com" \
  --upload-file mail.txt
```

The sender domain (`welcome@yourdomain.com`) must be onboarded for [Email Sending](https://developers.cloudflare.com/email-service/configuration/domains/) on the account that owns the API token.

## Authentication

Cloudflare's SMTP endpoint supports two SASL mechanisms, both defined by [RFC 4954 ↗︎](https://datatracker.ietf.org/doc/html/rfc4954):

- `AUTH PLAIN` — preferred. Single round trip, [RFC 4616 ↗︎](https://datatracker.ietf.org/doc/html/rfc4616).
- `AUTH LOGIN` — legacy [draft-murchison-sasl-login ↗︎](https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00). Supported for compatibility with older clients.

In both cases, the username is the literal string `api_token` and the password is your Cloudflare API token.

### Construct an `AUTH PLAIN` payload

`AUTH PLAIN` sends `\0api_token\0<API_TOKEN>` encoded as base64:

```sh
printf '\0api_token\0%s' "<API_TOKEN>" | base64
```

### Raw SMTP transcript

The following transcript shows a complete authenticated submission using `openssl s_client`. Lines beginning with `>` are sent by the client.

```txt
$ openssl s_client -quiet -connect smtp.mx.cloudflare.net:465 -crlf
220 mx.cloudflare.net Cloudflare Email ESMTP Service ready
> EHLO client.example.com
250-mx.cloudflare.net greets client.example.com
250-AUTH PLAIN LOGIN
250-SIZE 5242880
250-8BITMIME
250 ENHANCEDSTATUSCODES
> AUTH PLAIN AGFwaV90b2tlbgBpd0RQLi5oZWw=
235 2.7.0 Authentication successful
> MAIL FROM:<welcome@yourdomain.com>
250 2.1.0 Ok
> RCPT TO:<recipient@example.com>
250 2.1.5 Ok
> DATA
354 Start mail input; end with <CR><LF>.<CR><LF>
From: welcome@yourdomain.com
To: recipient@example.com
Subject: Welcome

Thanks for signing up.
.
250 2.0.0 Ok <jZTWt0pQO4p2LG7ByfkeSYUvT62k85Q12nCA@yourdomain.com>
> QUIT
221 mx.cloudflare.net Cloudflare Email ESMTP Service closing transmission channel
```

A `250 2.0.0 Ok` response after the message body normally includes the assigned Message-ID. Use it to correlate the submission with delivery logs in the dashboard.

When **Drop suppressed recipients** is on and all recipients are suppressed, SMTP may return `250 2.0.0 Ok` without a Message-ID and deliver nothing. Refer to [Suppressed recipients](#suppressed-recipients).

## Examples

For language-specific examples — curl, Nodemailer, Python `smtplib`, and PHPMailer — refer to [Send email over SMTP](https://developers.cloudflare.com/email-service/examples/email-sending/smtp/).

## Limits

The following per-session limits apply to SMTP submission:

| Limit | Value |
| --- | --- |
| `RCPT TO` recipients | 50 per session |
| `SIZE` advertised in `EHLO` | 5 MiB |
| `AUTH` command timeout | 30 seconds |
| `DATA` command timeout | 300 seconds |

Account-wide quotas (daily sending limits, content limits, header limits) are shared with the REST API and the Workers binding. Refer to [Limits](https://developers.cloudflare.com/email-service/platform/limits/) for the full list.

## Response codes

Cloudflare's SMTP server returns standard [RFC 5321 ↗︎](https://datatracker.ietf.org/doc/html/rfc5321) reply codes alongside [RFC 3463 ↗︎](https://datatracker.ietf.org/doc/html/rfc3463) enhanced status codes.

| Code | Meaning |
| --- | --- |
| `220` | Service ready (greeting after the TLS handshake). |
| `235 2.7.0` | Authentication succeeded. |
| `250` | `EHLO`, `MAIL FROM`, `RCPT TO`, or `DATA` completed successfully. |
| `354` | Ready to receive the message body — terminate with `<CR><LF>.<CR><LF>`. |
| `421` | Service temporarily unavailable. Retry later. |
| `451 4.3.0` | Local error — the message was accepted but deferred. Retry later. |
| `452 4.5.3` | Too many recipients in this session. Open a new session for the rest. |
| `500` / `501` | Syntax error in command or arguments. |
| `503` | Bad sequence of commands (for example, `MAIL FROM` before `AUTH`). |
| `530 5.7.0` | Authentication required. |
| `535 5.7.8` | Authentication failed. Refer to [Troubleshooting](#troubleshooting). |
| `550 5.7.1` | Sender or relay denied — usually the `MAIL FROM` domain is not onboarded. |
| `552 5.3.4` | Message exceeds the 5 MiB `SIZE` limit. |
| `554` | Transaction failed — content rejected by policy. |

## Suppressed recipients

SMTP accepts a syntactically valid recipient with `250 2.1.5 Ok` during `RCPT TO`. Email Service checks the [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/) for the account after receiving the message body.

Behavior depends on the per-sending-domain [**Drop suppressed recipients** setting](https://developers.cloudflare.com/email-service/configuration/domains/#drop-suppressed-recipients). The setting is off by default.

When the setting is off, any suppressed recipient causes SMTP to reject the entire message. When the setting is on, Email Service removes suppressed recipients and continues processing the remaining recipients.

If all recipients are suppressed while dropping is on, SMTP may return `250 2.0.0 Ok` without a Message-ID. It delivers nothing in this case.

Use [Email sending logs](https://developers.cloudflare.com/email-service/observability/logs/) to confirm delivery. Suppressed recipients appear with a **Rejected** result.

Suppression produces a `message.rejected` event in [Email Sending event subscriptions](https://developers.cloudflare.com/email-service/reference/event-subscriptions/) with `rejection.reason` set to `suppressed`.

## Troubleshooting

### `535 5.7.8 Authentication failed`

Possible causes:

- The username is not the literal string `api_token`. The API token goes in the **password** field.
- The token does not have the **Email Sending: Edit** permission.
- The token has been revoked or has expired.
- For a user-owned token, the domain in `MAIL FROM` does not belong to an account the token can act on.

### `550 5.7.1 Sender denied`

The address in `MAIL FROM` is on a domain that is not onboarded for Email Sending under the account that owns the API token. Onboard the domain under **Email Service > Email Sending** in the dashboard, or change the sender address.

### `552 5.3.4 Message too big`

The message body (including attachments after MIME encoding) is larger than 5 MiB. Reduce the attachment size or split the message.

### TLS handshake failures

Cloudflare's SMTP endpoint requires TLS from connect (implicit TLS). Make sure your client is configured for SSL/TLS on port `465`, not `STARTTLS` on port `587` (which is not supported).

For authentication problems related to SPF, DKIM, or DMARC on the recipient side, refer to [Troubleshoot SPF, DKIM and DMARC](https://developers.cloudflare.com/email-service/reference/troubleshooting/).

## Related resources

- [Send email over SMTP](https://developers.cloudflare.com/email-service/examples/email-sending/smtp/) — examples for curl, Nodemailer, Python, and PHP.
- [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) — send emails over HTTPS.
- [Email Sending Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — send emails from a Cloudflare Worker using bindings.
- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — onboard a domain for Email Sending.
- [MTA-STS](https://developers.cloudflare.com/email-service/configuration/mta-sts/) — enforce TLS for incoming mail.
- [Email headers](https://developers.cloudflare.com/email-service/reference/headers/) — supported headers and threading hints.
- [Limits](https://developers.cloudflare.com/email-service/platform/limits/) — account, message, and session limits.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/smtp/#page","headline":"SMTP","description":"Email Service SMTP endpoint, authentication, limits, responses, and errors.","url":"https://developers.cloudflare.com/email-service/api/send-emails/smtp/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/api/send-emails/smtp/og.png?v=13757de7404f5225","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Sending Workers API methods, types, binding, and errors.
title: Email Sending Workers API
image: https://developers.cloudflare.com/email-service/api/send-emails/workers-api/og.png?v=84b629fb5504662d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email Sending Workers API

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

The Workers API provides native email sending capabilities directly from your Cloudflare Workers through bindings. If you are not using Workers, you can send emails using the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) instead.

## Email binding

Configure a `send_email` binding in your Wrangler configuration file to enable email sending:

```jsonc
{
	"send_email": [{ "name": "EMAIL" }],
}
```

```toml
[[send_email]]
name = "EMAIL"
```

You can restrict which senders and recipients a binding may use. Refer to [send bindings](https://developers.cloudflare.com/email-service/configuration/send-bindings/) for the available restriction attributes and examples.

## `send()` method

Send a single email using the `send()` method on your email binding.

### Interface

```ts
interface SendEmail {
	send(message: EmailMessage | EmailMessageBuilder): Promise<EmailSendResult>;
}

interface EmailAddress {
	email: string;
	name?: string;
}

// Structured email builder (recommended)
interface EmailMessageBuilder {
	to: string | EmailAddress | (string | EmailAddress)[]; // Max 50 recipients
	from: string | EmailAddress;
	subject: string;
	html?: string;
	text?: string;
	cc?: string | EmailAddress | (string | EmailAddress)[];
	bcc?: string | EmailAddress | (string | EmailAddress)[];
	replyTo?: string | EmailAddress;
	attachments?: Attachment[];
	// Custom headers. See /email-service/reference/headers/
	headers?: { [key: string]: string };
	// The combined number of addresses in `to`, `cc`, and `bcc` must not
	// exceed 50. See /email-service/platform/limits/ for all limits.
}

interface Attachment {
	content: string | ArrayBuffer | ArrayBufferView; // Base64 string or binary content
	filename: string;
	type: string; // MIME type
	disposition: "attachment" | "inline";
	contentId?: string; // For inline attachments
}

interface EmailSendResult {
	messageId: string; // Unique email ID
}

// Errors are thrown as standard Error objects with a `code` property
// try { await env.EMAIL.send(...) } catch (e) { console.log(e.code, e.message) }
```

Local development with binary attachments

When using `wrangler dev` without [remote bindings](https://developers.cloudflare.com/workers/local-development/#remote-bindings), `ArrayBuffer` and `ArrayBufferView` content in attachments cannot be serialized by the local simulator. Refer to [local development for email sending](https://developers.cloudflare.com/email-service/observability/local-development/sending/#known-limitations).

### Basic usage

```ts
const response = await env.EMAIL.send({
	to: "recipient@example.com",
	from: "welcome@yourdomain.com",
	subject: "Welcome to our service!",
	html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
	text: "Welcome! Thanks for signing up.",
});
```

For multiple recipients, CC/BCC, and named addresses, refer to [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/).

### Attachments

Send files by including base64-encoded content in the `attachments` array. The total message size must not exceed 5 MiB (including attachments).

```ts
const response = await env.EMAIL.send({
	to: "customer@example.com",
	from: "invoices@yourdomain.com",
	subject: "Your Invoice",
	html: "<h1>Invoice attached</h1><p>Please find your invoice attached.</p>",
	attachments: [
		{
			content: "JVBERi0xLjQKJeLjz9MKMSAwIG9iag...", // Base64 PDF content
			filename: "invoice-12345.pdf",
			type: "application/pdf",
			disposition: "attachment",
		},
	],
});
```

For inline images and file uploads, refer to [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/).

## Error handling

Handle email sending errors gracefully:

```ts
export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		try {
			const response = await env.EMAIL.send({
				to: "user@example.com",
				from: "noreply@yourdomain.com",
				subject: "Test Email",
				text: "This is a test email.",
			});

			return new Response(
				JSON.stringify({
					success: true,
					emailId: response.messageId,
				}),
			);
		} catch (error) {
			// Error has .code and .message properties
			console.error("Email sending failed:", error.code, error.message);

			// Handle specific error types
			switch (error.code) {
				case "E_SENDER_NOT_VERIFIED":
					return new Response(
						JSON.stringify({
							success: false,
							error: "Please verify your sender domain first",
						}),
						{ status: 400 },
					);

				case "E_RATE_LIMIT_EXCEEDED":
					return new Response(
						JSON.stringify({
							success: false,
							error: "Rate limit exceeded. Please try again later",
						}),
						{ status: 429 },
					);

				default:
					return new Response(
						JSON.stringify({
							success: false,
							error: error.message,
						}),
						{ status: 500 },
					);
			}
		}
	},
};
```

## Error codes

The following error codes may be returned when sending emails:

| Error code | Description | Common causes |
| --- | --- | --- |
| `E_VALIDATION_ERROR` | Validation error in the payload | Invalid email format, missing required fields, malformed data |
| `E_FIELD_MISSING` | Required field is missing | Missing `to`, `from`, or `subject` fields |
| `E_TOO_MANY_RECIPIENTS` | Too many recipients in to/cc/bcc arrays | Combined recipients exceed 50 limit |
| `E_TOO_MANY_ATTACHMENTS` | Too many attachments in `attachments` array | `attachments` array exceeds 32 entries |
| `E_SENDER_NOT_VERIFIED` | Sender domain not verified | Attempting to send from unverified domain |
| `E_RECIPIENT_NOT_ALLOWED` | Recipient not in allowed list | Recipient address not in `allowed_destination_addresses` |
| `E_RECIPIENT_SUPPRESSED` | Suppressed recipient while dropping is off | At least one recipient is suppressed and **Drop suppressed recipients** is off |
| `E_SENDER_DOMAIN_NOT_AVAILABLE` | Domain not available for sending | Domain not onboarded to Email Service |
| `E_CONTENT_TOO_LARGE` | Email content exceeds size limit | Total message size exceeds the maximum |
| `E_DELIVERY_FAILED` | Could not deliver the email | SMTP delivery failure, recipient server rejection |
| `E_RATE_LIMIT_EXCEEDED` | Rate limit exceeded | Sending rate limit reached |
| `E_DAILY_LIMIT_EXCEEDED` | Daily limit exceeded | Daily sending quota reached |
| `E_INTERNAL_SERVER_ERROR` | Internal service error | Email Service temporarily unavailable |
| `E_HEADER_NOT_ALLOWED` | Header not allowed | Header is platform-controlled or not on the [allowlist](https://developers.cloudflare.com/email-service/reference/headers/) |
| `E_HEADER_USE_API_FIELD` | Must use API field | Header like `From` must be set via the dedicated API field |
| `E_HEADER_VALUE_INVALID` | Header value invalid | Malformed value, empty, or incorrect format |
| `E_HEADER_VALUE_TOO_LONG` | Header value too long | Value exceeds 2,048 byte limit |
| `E_HEADER_NAME_INVALID` | Header name invalid | Invalid characters or exceeds 100 byte limit |
| `E_HEADERS_TOO_LARGE` | Headers payload too large | Total custom headers exceed 16 KB limit |
| `E_HEADERS_TOO_MANY` | Too many headers | More than 20 allowlisted (non-X) custom headers |

**Drop suppressed recipients** is off by default. When you [turn on the setting](https://developers.cloudflare.com/email-service/configuration/domains/#drop-suppressed-recipients), Email Service removes suppressed recipients and processes the remaining recipients.

## Legacy `EmailMessage` API

The `EmailMessage` API remains supported for backward compatibility. Use it when you already have a raw [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) MIME message to send. For new code, prefer the structured [`send()` method](#send-method) above.

```ts
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const msg = createMimeMessage();
		msg.setSender({ name: "Sender", addr: "sender@yourdomain.com" });
		msg.setRecipient("recipient@example.com");
		msg.setSubject("Legacy Email");
		msg.addMessage({
			contentType: "text/html",
			data: "<h1>Hello from legacy API</h1>",
		});

		const message = new EmailMessage(
			"sender@yourdomain.com",
			"recipient@example.com",
			msg.asRaw(),
		);

		await env.EMAIL.send(message);
		return new Response("Legacy email sent");
	},
};
```

---

## Next steps

- Refer to the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) for sending emails without Workers
- Refer to [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) for sending from any SMTP-capable application or mail client
- Refer to [email sending examples](https://developers.cloudflare.com/email-service/examples/email-sending/) for common patterns
- Refer to the [Email Routing API](https://developers.cloudflare.com/email-service/api/route-emails/) for handling incoming emails
- Explore [email authentication](https://developers.cloudflare.com/email-service/concepts/email-authentication/) for better deliverability

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#page","headline":"Email Sending Workers API","description":"Email Sending Workers API methods, types, binding, and errors.","url":"https://developers.cloudflare.com/email-service/api/send-emails/workers-api/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/api/send-emails/workers-api/og.png?v=84b629fb5504662d","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Understand bounce handling and reputation management for optimal email delivery with Email Service.
title: Email deliverability
image: https://developers.cloudflare.com/email-service/concepts/deliverability/og.png?v=6445422fd87b5279
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email deliverability

Understand bounce handling and reputation management for optimal email delivery.

Last updated Sep 16, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/concepts/deliverability/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

When you send an email, there is no guarantee it reaches the recipient's inbox. Inbox providers like Gmail, Yahoo, Outlook, and iCloud invest heavily in filtering out unwanted email. If you send poorly targeted emails, have high bounce rates, or trigger spam complaints, these providers may flag your domain as untrustworthy. Once that happens, even your legitimate emails can end up in spam or be blocked outright.

This concept is referred to as email deliverability: maintaining a healthy sending reputation so that inbox providers trust your emails. Cloudflare Email Service helps with this by automatically handling bounces, managing suppression lists, and authenticating your emails through SPF, DKIM, and DMARC.

## Bounces

Bounces occur when emails cannot be delivered to recipients. There are two types of bounces: **hard bounces** and **soft bounces**.

### Hard bounces

Hard bounces are permanent delivery failures that occur when:

- The recipient address does not exist.
- The recipient domain does not exist.
- The receiving server permanently rejects the recipient.

**Hard bounces are never retried** because the failure is permanent. Emails that hard bounce will generate a bounce notification to the sender address and can be monitored through [analytics](https://developers.cloudflare.com/email-service/observability/metrics-analytics/).

Email Service adds eligible recipient-side hard bounces to your [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/). Suppressions have no expiration when the mailbox or domain does not exist.

They also have no expiration when the recipient remains unavailable across repeated delivery attempts. Other eligible hard-bounce suppressions last seven days.

### Soft bounces

Soft bounces are temporary failures that may succeed if retried:

- Recipient mailbox is full
- Email server temporarily down
- Rate limiting or greylisting

Cloudflare automatically retries soft bounces with exponential backoff. Eligible recipient-side failures create a 24-hour suppression.

## Reputation management

Cloudflare automatically manages:

- **IP reputation**: Managed sending infrastructure optimized for deliverability
- **Domain authentication**: DKIM signing, SPF alignment, DMARC compliance
- **Feedback processing**: ISP complaint handling and suppression list management

### Best practices

#### Content and list hygiene

Avoid content that can trigger spam-detection or can be perceived as unwanted content:

- Avoid spam trigger words (FREE, URGENT, GUARANTEED)
- Include both HTML and plain text versions
- Use legitimate URLs and clear sender identification

Ensure that your email lists are clean and contain intended recipients:

- Validate emails before sending
- Implement double opt-in for subscriptions
- Remove hard bounced addresses immediately

Ensure that your deliverability stays above key metrics to avoid affecting your email sending reputation:

- Delivery rate >95%
- Hard bounce rate < 2%
- Complaint rate < 0.1%

#### Use separate domains for separate purposes

Each domain builds its own deliverability reputation with inbox providers. Use separate domains or subdomains for different types of email so that one category does not affect the reputation of another. For example:

- `notifications.yourdomain.com` for transactional emails (order confirmations, password resets)
- `marketing.yourdomain.com` for marketing and promotional emails
- `yourdomain.com` for important account-related communications

This way, if marketing emails generate higher complaint rates, your transactional email deliverability is not impacted. Each domain can be onboarded separately through [domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/).

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/deliverability/#page","headline":"Email deliverability","description":"Understand bounce handling and reputation management for optimal email delivery with Email Service.","url":"https://developers.cloudflare.com/email-service/concepts/deliverability/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/concepts/deliverability/og.png?v=6445422fd87b5279","dateModified":"2026-09-16","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: SPF, DKIM, and DMARC authentication for secure and deliverable email sending with Email Service.
title: Email authentication
image: https://developers.cloudflare.com/email-service/concepts/email-authentication/og.png?v=3c92dd4aa6787073
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email authentication

Learn about SPF, DKIM, and DMARC for secure and deliverable email sending.

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/concepts/email-authentication/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email authentication verifies sender identity and improves deliverability. **Cloudflare Email Service handles authentication automatically**, but understanding these concepts helps troubleshoot issues.

## SPF (Sender Policy Framework)

SPF ensures that no one else can send emails with your domain by authorizing which mail servers are allowed to send on your behalf.

Email Service configures separate SPF records for sending and routing:

- **Email Sending** SPF record on `cf-bounce.yourdomain.com`:

  ```txt
  TXT cf-bounce.yourdomain.com "v=spf1 include:_spf.mx.cloudflare.net ~all"
  ```

- **Email Routing** SPF record on the root domain:

  ```txt
  TXT yourdomain.com "v=spf1 include:_spf.mx.cloudflare.net ~all"
  ```

SPF works by:

1. Publishing authorized IP addresses in DNS
2. Recipient servers checking your SPF record
3. Comparing the sending IP against authorized IPs
4. Passing or failing based on the result

## DKIM (DomainKeys Identified Mail)

DKIM ensures that emails have not been tampered during transit by cryptographically signing them with your domain's private key.

**How DKIM works:**

1. Email headers and body are signed with a private key
2. DKIM-Signature header is added to the email
3. Public key is published in DNS
4. Recipients use the public key to verify the signature

Email Service uses separate DKIM selectors for sending and routing:

- **Email Sending**: `cf-bounce._domainkey.yourdomain.com`
- **Email Routing**: `cf2024-1._domainkey.yourdomain.com`

Cloudflare automatically generates and manages DKIM keys. You add the provided DNS records from the dashboard.

## DMARC (Domain-based Message Authentication, Reporting & Conformance)

DMARC ensures that emails claiming to be from your domain actually pass SPF and DKIM checks, telling recipients what to do with emails that fail authentication.

**DMARC record example:**

```txt
TXT _dmarc.yourdomain.com "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"
```

Note

If the `rua` mailto address is on a different domain than the DMARC record (common when using a DMARC aggregator), the receiving domain must publish a `_report._dmarc.yourdomain.com` TXT record to authorize reports. Refer to [RFC 7489 §7.1 ↗︎](https://datatracker.ietf.org/doc/html/rfc7489#section-7.1).

**DMARC policies:**

- `p=none` - Monitor only (recommended to start)
- `p=quarantine` - Quarantine suspicious emails
- `p=reject` - Reject unauthenticated emails

**Deployment strategy:**

1. Start with `p=none` to monitor authentication
2. Gradually increase to `p=quarantine`
3. Finally implement `p=reject` after confirming legitimate mail authenticates

## Key benefits

Email authentication provides:

- **Deliverability**: Improves inbox placement
- **Security**: Protects your domain from spoofing
- **Reputation**: Maintains good sender reputation with ISPs

Cloudflare Email Service handles authentication automatically, but you need to configure the DNS records for SPF, DKIM, and DMARC as provided in your dashboard. Email Sending and Email Routing use separate DNS records -- refer to [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) for the full details.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/email-authentication/#page","headline":"Email authentication","description":"SPF, DKIM, and DMARC authentication for secure and deliverable email sending with Email Service.","url":"https://developers.cloudflare.com/email-service/concepts/email-authentication/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/concepts/email-authentication/og.png?v=3c92dd4aa6787073","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: The complete email processing flow from send request to final delivery status in Email Service.
title: Email lifecycle
image: https://developers.cloudflare.com/email-service/concepts/email-lifecycle/og.png?v=b525893164e18a0f
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email lifecycle

Understand the complete email processing lifecycle from request received through final delivery status with Cloudflare Email Service

Last updated Sep 16, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/concepts/email-lifecycle/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

The email lifecycle describes the complete journey of an email through Cloudflare Email Service. Understanding this process helps you optimize your email implementation and troubleshoot delivery issues.

Email Sending and Email Routing follow distinct processing pipelines. The outbound flow covers emails you send through the service; the inbound flow covers emails received on domains configured with Email Routing.

## Outbound flow (Email Sending)

Every email sent through Cloudflare Email Service follows this processing pipeline:

```
flowchart LR
    A[Request Received] --> B["Rate Limit, Authentication & Suppression Check"] --> E[Delivery Attempt]
    E --> G{Success?}
    G -->|Yes, successfully delivered| F[Final Status & Metrics]
    G -->|No - Soft Bounce| H[Retry with Exponential Backoff]
    G -->|No - Hard Bounce| F
    H -->|Retries remaining| E
    H -->|Max retries exceeded| F

```

### Stage details

1. **Request received:** The system validates the email format, sender authorization, and message structure. Invalid requests are rejected immediately and do not proceed to the next stage.
2. **Rate limit check:** The system checks sending [limits](https://developers.cloudflare.com/email-service/platform/limits/) per account, domain, and recipient to prevent abuse. Requests that exceed these limits are temporarily rejected and must be retried later.
3. **Authentication and reputation**: The system performs email authentication checks and evaluates sender reputation:
   - **SPF (Sender Policy Framework)**: Verifies that the sending IP address is authorized to send emails for the domain by checking DNS TXT records. This prevents domain spoofing and improves deliverability.
   - **DKIM (DomainKeys Identified Mail)**: Validates the email's cryptographic signature to ensure message integrity and authenticate the sender domain. This builds trust with recipient servers.
   - **DMARC (Domain-based Message Authentication)**: Applies domain owner policies for handling emails that fail SPF or DKIM checks, helping prevent phishing and brand impersonation while providing feedback reports.

   These authentication mechanisms work together to establish sender legitimacy and protect against email fraud. Senders with low reputation scores may experience throttling or delayed processing.
4. **Suppression list check:** The system checks each recipient against the Email Sending [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/) for your account. Suppressed recipients do not reach the delivery stage or count toward your quota.

   The per-sending-domain [**Drop suppressed recipients** setting](https://developers.cloudflare.com/email-service/configuration/domains/#drop-suppressed-recipients) is off by default. When off, the REST API returns `400`, the Workers binding throws `E_RECIPIENT_SUPPRESSED`, and SMTP rejects the message if any recipient is suppressed.

   When on, Email Service removes suppressed recipients and processes the remaining recipients. Email Service does not process unsubscribe links, so add unsubscribed recipients manually.
5. **Delivery attempt:** The system connects to the recipient's mail server and attempts message delivery via SMTP. When delivery fails, the system applies different retry logic based on the failure type:
   - **Soft bounces (4xx responses)**: The system retries delivery using exponential backoff timing
   - **Hard bounces (5xx responses)**: The system marks the email as permanently failed with no retry attempts
6. **Server response handling:** The system processes SMTP response codes from the recipient server to determine the final email status:
   - **2xx codes**: The email was delivered successfully
   - **4xx codes**: Temporary failure occurred and the email will be retried
   - **5xx codes**: Permanent failure occurred and the email cannot be delivered
7. **Final status and metrics:** Based on the server response, the system assigns emails one of these final statuses:
   - **Delivered**: The email was successfully accepted by the recipient server
   - **Delivery failed**: The email permanently failed delivery (hard bounce) or exceeded the maximum retry attempts (soft bounce). This status appears as `deliveryFailed` when querying the [GraphQL Analytics API](https://developers.cloudflare.com/email-service/observability/metrics-analytics/).

## Inbound flow (Email Routing)

Every email received on a domain configured with Email Routing follows this processing pipeline:

```
flowchart LR
    A[SMTP Receipt] --> B[Authentication Check]
    B --> C{Authenticated?}
    C -->|Yes| D[Rule Match]
    C -->|No| R[Reject]
    D --> E{Action?}
    E -->|Send to email| F[ARC Sign & SRS Rewrite]
    E -->|Send to Worker| W[Worker]
    E -->|Drop| X[Drop]
    W --> Y{Worker action?}
    Y -->|forward| F
    Y -->|reply| F
    Y -->|setReject| R
    F --> G[Outbound Delivery]
    G --> H[Final Status & Metrics]

```

### Stage details

1. **SMTP receipt:** A sending server connects to a Cloudflare MX server and submits the message over SMTP. Messages larger than the [inbound message size limit](https://developers.cloudflare.com/email-service/platform/limits/) are rejected at this stage.
2. **Authentication check:** The system performs [SPF, DKIM, DMARC, and ARC](https://developers.cloudflare.com/email-service/concepts/email-authentication/) checks on the incoming message. Mail that fails authentication according to the sender's DMARC policy is rejected. Mail from IP addresses on a Realtime Block List is also rejected at this stage. Refer to [Postmaster information](https://developers.cloudflare.com/email-service/reference/postmaster/) for details.
3. **Rule match:** The system matches the recipient address against your configured [routing rules](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/). If [subaddressing](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#subaddressing) is enabled, sub-addressed recipients fall back to the base routing rule. If no rule matches and the [catch-all rule](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#catch-all-rule) is enabled, the catch-all rule applies.
4. **Action:** The system applies the matched rule's action:
   - **Send to an email**: The message is forwarded to the verified destination address (stage 5).
   - **Send to a Worker**: The message is passed to your [Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/). The Worker can call `forward()`, `reply()`, or `setReject()`.
   - **Drop**: The message is silently discarded. No further processing occurs.
5. **ARC sign and SRS rewrite:** For forwarded messages, the system adds an ARC seal preserving the original authentication results and rewrites the envelope sender using the [Sender Rewriting Scheme](https://developers.cloudflare.com/email-service/reference/postmaster/#sender-rewriting). This allows SPF to pass at the destination server.
6. **Outbound delivery:** The system connects to the destination mail server and delivers the message. Soft bounces are retried with exponential backoff. Hard bounces are returned to the original sender in-session as upstream SMTP errors. Refer to [Postmaster: SMTP errors](https://developers.cloudflare.com/email-service/reference/postmaster/#smtp-errors).
7. **Final status and metrics:** The final outcome is recorded and available through the [Activity log](https://developers.cloudflare.com/email-service/observability/logs/) and the [GraphQL Analytics API](https://developers.cloudflare.com/email-service/observability/metrics-analytics/).

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/email-lifecycle/#page","headline":"Email lifecycle","description":"The complete email processing flow from send request to final delivery status in Email Service.","url":"https://developers.cloudflare.com/email-service/concepts/email-lifecycle/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/concepts/email-lifecycle/og.png?v=b525893164e18a0f","dateModified":"2026-09-16","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Understand suppression scope, triggers, expiration, and enforcement.
title: Suppression lists
image: https://developers.cloudflare.com/email-service/concepts/suppressions/og.png?v=b7a3832c3a6526eb
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Suppression lists

Suppression lists prevent Email Service from sending to recipients who should not receive mail. Cloudflare adds entries after eligible bounces and spam complaints. You can also add entries manually.

Last updated Sep 25, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/concepts/suppressions/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

An Email Sending suppression list contains recipients that Email Service does not contact. Suppressions protect your sender reputation and help prevent sending unwanted mail.

Cloudflare creates suppressions after eligible delivery failures and spam complaints. You can also add entries for recipients who should not receive mail.

To add or remove entries, refer to [Manage suppressions](https://developers.cloudflare.com/email-service/configuration/suppressions/). Suppressions only apply to Email Sending.

## Suppression scope

Each suppression has a scope. The scope controls which sending domains the suppression applies to:

| Scope | Applies to |
| --- | --- |
| `account` | Every sending domain and subdomain in your account |
| `sending_domain` | One sending domain, such as `mail.myappexample.com` |

The sending domain of a message is the domain of its sender address. This is the `from` address for the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) and [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), and the envelope `MAIL FROM` address for [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/).

A `sending_domain` suppression matches only its exact domain. A suppression for `myappexample.com` does not block mail from `mail.myappexample.com`, and a suppression for `mail.myappexample.com` does not block mail from `myappexample.com`.

Email Service suppresses a recipient if the address has an `account` suppression, or a `sending_domain` suppression for the sending domain of the message.

Bounce and complaint suppressions use the `sending_domain` scope, with the sending domain of the message that bounced or received the complaint. A complaint creates an `account` suppression when Cloudflare cannot identify the sending domain of the original message.

## Suppression rules

The following table describes automatic and manual creation rules and expiration:

| Reason | Created when | Expiration |
| --- | --- | --- |
| Manual (`manual`) | You add the recipient through the dashboard or API | The time you choose, or no expiration |
| Spam complaint (`complaint`) | Cloudflare receives and validates a complaint from the recipient's email provider | No expiration |
| Hard bounce (`hard_bounce`) | Any other eligible recipient-side permanent rejection occurs | 7 days |
| Hard bounce (`hard_bounce`) | The recipient mailbox or domain does not exist, or a recipient-side issue persists across repeated delivery attempts | No expiration |
| Soft bounce (`soft_bounce`) | An eligible recipient-side temporary failure occurs, such as a full mailbox or rate limit | 24 hours by default |

The [account Email Sending suppression management REST API](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/) can return Cloudflare-managed entries with a `policy` reason. Clients must use `read_only` to determine mutability, not infer it from `reason`.

You cannot update or delete an entry when `read_only` is `true`. To investigate one, [contact Cloudflare Support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).

### Manual suppressions

Manual suppressions allow you to add application-level decisions that Email Service cannot observe. For example, if a user manually unsubscribes from emails in your app, you can add their email to your Email Service suppression list.

You select the expiration when creating the entry. An entry without an expiration remains active until you delete it.

You also select the scope. Manual suppressions use the `account` scope by default. Email Service does not check that your account sends from a `sending_domain` value. A suppression for a domain that you do not send from never blocks mail.

### Spam complaints

Email providers send feedback reports when recipients mark messages as spam. Cloudflare validates these reports before creating complaint suppressions.

Complaint suppressions are created without an expiration. You can change or delete one when `read_only` is `false`.

We recommend changing or deleting one only after the recipient opts in again through your application.

### Hard bounces

A hard bounce is a permanent rejection of one delivery attempt. Some addresses become valid again after their owner fixes the mailbox or domain.

Email Service creates a suppression without an expiration when the mailbox or domain does not exist. It also does this when a recipient-side issue persists across repeated delivery attempts without a successful delivery.

Other eligible hard-bounce suppressions last seven days.

### Soft bounces

A soft bounce is a temporary recipient-side failure. Examples include a full mailbox, a temporarily unavailable server, or recipient-side rate limiting.

Eligible soft-bounce suppressions last 24 hours by default. Email Service resumes sending after the suppression expires.

Not every temporary delivery failure creates a suppression. For example, Email Service does not suppress a recipient for a sender-side authentication or reputation problem.

## Enforcement by sending method

Each sending domain has a [**Drop suppressed recipients** setting](https://developers.cloudflare.com/email-service/configuration/domains/#drop-suppressed-recipients). The setting is off by default.

| Sending method | Setting off | Setting on |
| --- | --- | --- |
| [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) | Returns `400` and rejects the request when any recipient is suppressed. | Removes suppressed recipients and processes the remaining recipients. |
| [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) | Throws `E_RECIPIENT_SUPPRESSED` and rejects the `send()` call when any recipient is suppressed. | Removes suppressed recipients and processes the remaining recipients. |
| [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) | Rejects the message when any recipient is suppressed. | Removes suppressed recipients and processes the remaining recipients. If none remain, SMTP may return `250 2.0.0 Ok` without a Message-ID and delivers nothing. |

Suppressed recipients do not count toward your [monthly quota](https://developers.cloudflare.com/email-service/platform/pricing/) or daily sending limits. They appear as **Rejected** in [Email sending logs](https://developers.cloudflare.com/email-service/observability/logs/).

## Best practices

- Add manual suppressions for unsubscribes.
- Use the `account` scope when a recipient must not receive mail from any of your domains. Use the `sending_domain` scope when the decision applies to one subdomain only.
- Require recipients to opt in again through your application.
- Verify addresses before deleting suppressions.
- Investigate patterns across repeated bounces.
- Let temporary suppressions expire automatically.

## Related resources

- Use [Manage suppressions](https://developers.cloudflare.com/email-service/configuration/suppressions/) to manage entries through the dashboard or API.
- Review [Email deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/) to understand bounce and complaint effects.
- Follow [Email lifecycle](https://developers.cloudflare.com/email-service/concepts/email-lifecycle/) to locate suppression checks in the sending flow.
- Review [Suppression list limits](https://developers.cloudflare.com/email-service/platform/limits/#suppression-list-limits) for API limits.
- Use [Email sending logs](https://developers.cloudflare.com/email-service/observability/logs/) to investigate rejected sends.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/suppressions/#page","headline":"Suppression lists","description":"Understand suppression scope, triggers, expiration, and enforcement.","url":"https://developers.cloudflare.com/email-service/concepts/suppressions/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/concepts/suppressions/og.png?v=b7a3832c3a6526eb","dateModified":"2026-09-25","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Configure and verify DNS records for Email Service sending and routing on your domain.
title: Domain configuration
image: https://developers.cloudflare.com/email-service/configuration/domains/og.png?v=4871d876f74d072f
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Domain configuration

Configure domains for Cloudflare Email Service, manage DNS records, and verify domain setup for both email sending and routing.

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/domains/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Configure your domains to work with Cloudflare Email Service. This includes DNS record management, domain verification, and advanced domain settings.

## Automatic DNS configuration

Cloudflare can configure all required DNS records for you when you onboard a domain onto Email Sending or Email Routing.

Before using Email Sending, configure your domain.

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select **Onboard Domain**.
3. Choose a domain from your Cloudflare account. Optionally review the DNS records that Cloudflare will add to the `cf-bounce` subdomain of your domain:
   - MX records to route bounce emails to Cloudflare.
   - TXT record for SPF to authorize sending emails.
   - TXT record for DKIM to provide authentication for emails sent from your domain.
   - TXT record for DMARC on `_dmarc.yourdomain.com`.
4. Select **Done**.

Note

DNS changes can take up to 24 hours to propagate globally, but usually complete within 5-15 minutes for domains using Cloudflare DNS.

Once your domain is onboarded, you can start sending emails.

Before using Email Routing, configure your domain.

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select **Onboard Domain**.
3. Choose a domain from your Cloudflare account. Optionally review the DNS records that Cloudflare will add to your root domain:
   - MX records to route incoming emails to Cloudflare.
   - TXT record for SPF to authorize email routing.
   - TXT record for DKIM to provide authentication for routed emails.
4. Select **Done**.

Note

DNS changes can take up to 24 hours to propagate globally, but usually complete within 5-15 minutes for domains using Cloudflare DNS.

Once your domain is onboarded, you can start routing emails.

## DNS record configuration details

Cloudflare automatically configures required DNS records for both email sending and routing when you onboard a domain onto Email Service. Here are the specific details of the DNS records configured:

### Sending records

These records authenticate your outbound emails. Email Sending creates DNS records on a `cf-bounce.` subdomain of your domain to handle bounce processing. These are separate from the records used by Email Routing.

**Purpose**: Route bounce emails back to Cloudflare for processing.

```txt
MX cf-bounce.yourdomain.com route1.mx.cloudflare.net
MX cf-bounce.yourdomain.com route2.mx.cloudflare.net
MX cf-bounce.yourdomain.com route3.mx.cloudflare.net
```

**Configuration:**

- **Type**: MX
- **Name**: `cf-bounce` (subdomain)
- **Mail server**: Cloudflare MX servers
- **Priority**: Assigned automatically by Cloudflare

**Purpose**: Authorizes Cloudflare to send emails on behalf of your domain.

```txt
TXT cf-bounce.yourdomain.com "v=spf1 include:_spf.mx.cloudflare.net ~all"
```

**Configuration:**

- **Type**: TXT
- **Name**: `cf-bounce` (subdomain)
- **Value**: `v=spf1 include:_spf.mx.cloudflare.net ~all`
- **TTL**: Auto

**Purpose**: Provides cryptographic authentication for your emails.

```txt
TXT cf-bounce._domainkey.yourdomain.com "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
```

**Configuration:**

- **Type**: TXT
- **Name**: `cf-bounce._domainkey` (selector managed by Cloudflare)
- **Value**: DKIM public key (provided by Cloudflare)
- **TTL**: Auto

**Purpose**: Sets policy for email authentication failures.

```txt
TXT _dmarc.yourdomain.com "v=DMARC1; p=reject;"
```

**Configuration:**

- **Type**: TXT
- **Name**: `_dmarc`
- **Value**: DMARC policy
- **TTL**: Auto

**Policy options:**

- `p=none` - Monitor only (recommended for new setups)
- `p=quarantine` - Quarantine suspicious emails
- `p=reject` - Reject unauthenticated emails

### Routing records

These records route incoming emails to Cloudflare and authenticate forwarded emails. Email Routing DNS records are configured on the root domain.

**Purpose**: Route incoming emails to Cloudflare's mail servers.

```txt
MX yourdomain.com route1.mx.cloudflare.net
MX yourdomain.com route2.mx.cloudflare.net
MX yourdomain.com route3.mx.cloudflare.net
```

**Configuration:**

- **Type**: MX
- **Name**: `@` (root domain)
- **Mail server**: Cloudflare routing MX servers
- **Priority**: Assigned automatically by Cloudflare

**Purpose**: Authorizes Cloudflare to forward emails on behalf of your domain.

```txt
TXT yourdomain.com "v=spf1 include:_spf.mx.cloudflare.net ~all"
```

**Configuration:**

- **Type**: TXT
- **Name**: `@` (root domain)
- **Value**: `v=spf1 include:_spf.mx.cloudflare.net ~all`
- **TTL**: Auto

Existing SPF records

If you have existing SPF records, merge them: `v=spf1 include:_spf.mx.cloudflare.net include:_spf.google.com ~all`.

SPF records are limited to 10 DNS lookups total. If merging pushes you over the limit, consider flattening the record or removing non-essential `include:` entries.

**Purpose**: Provides cryptographic authentication for forwarded emails.

```txt
TXT cf2024-1._domainkey.yourdomain.com "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
```

**Configuration:**

- **Type**: TXT
- **Name**: `cf2024-1._domainkey` (selector provided by Cloudflare)
- **Value**: DKIM public key (provided by Cloudflare)
- **TTL**: Auto

**Separate from sending DKIM** - Email Routing uses its own DKIM selector (`cf2024-1._domainkey`) and keys, distinct from the sending DKIM selector (`cf-bounce._domainkey`).

## Domain verification

Email Sending and Email Routing have separate DNS records and separate settings pages where you can verify their status.

### Verify Email Sending records

1. Go to **Compute** > **Email Service** > **Email Sending** > **Settings**.
2. The **DNS records** section shows all sending-related records:
   - **MX records** on `cf-bounce.yourdomain.com`
   - **SPF record** on `cf-bounce.yourdomain.com`
   - **DKIM record** on `cf-bounce._domainkey.yourdomain.com`
   - **DMARC record** on `_dmarc.yourdomain.com`
3. Each record shows either a **Locked** or **Unlocked** status. Both states indicate the record is configured correctly; the status reflects whether Email Service is managing the record. Refer to [Locked DNS records](#locked-dns-records) for more information.

### Verify Email Routing records

1. Go to **Compute** > **Email Service** > **Email Routing** > **Settings**.
2. The **DNS records** section shows all routing-related records:
   - **MX records** on `yourdomain.com`
   - **SPF record** on `yourdomain.com`
   - **DKIM record** on `cf2024-1._domainkey.yourdomain.com`
3. Each record shows either a **Locked** or **Unlocked** status. Both states indicate the record is configured correctly; the status reflects whether Email Service is managing the record. Refer to [Locked DNS records](#locked-dns-records) for more information.

### If records are not configured

- Wait 5-15 minutes for DNS propagation.
- Check DNS configuration in your domain's **DNS** > **Records** settings.

### Locked DNS records

When Email Service onboarding succeeds, the DNS records it manages are locked to prevent accidental changes that would break mail flow. Locked records show a **Locked** status in the dashboard and cannot be edited or deleted from **DNS** > **Records** until they are unlocked.

Only Email Routing records on the root domain (MX, SPF, and DKIM) support unlocking. Email Sending records on the `cf-bounce` subdomain stay managed by Email Service for the lifetime of the domain configuration.

To unlock an Email Routing record:

1. Go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select the domain, then open **Settings**.
3. Locate the record in the **DNS records** section and select **Unlock**.

If you want to migrate to a different email provider without immediately interrupting service, unlock the routing records first, add the new provider's records alongside them, then remove the Email Service records once the new setup is verified.

### `_dc-mx` DNS responses

This section applies only if your MX records point to hostnames that are proxied through Cloudflare.

When an MX record on your domain points to a hostname that is proxied through Cloudflare, mail delivery to that hostname would normally fail because the Cloudflare proxy does not handle SMTP. To avoid this, Cloudflare automatically inserts a `_dc-mx.<hash>.example.com` record that resolves directly to the origin IP. Sending mail servers follow this record to bypass the proxy and reach the origin.

For more information, refer to [DNS troubleshooting: `_dc-` and `_dc-mx` subdomains](https://developers.cloudflare.com/dns/manage-dns-records/troubleshooting/unexpected-dns-records/#dc--and-_dc-mx-subdomains).

### Verification troubleshooting

**Issue**: Records show as "Not Found" immediately after adding.

**Solution**:

- Wait 5-15 minutes for DNS propagation
- Check propagation status: `dig TXT yourdomain.com`
- Cloudflare domains propagate faster than external domains

**Check propagation globally:**

```bash
# Check sending SPF record
dig TXT cf-bounce.yourdomain.com | grep spf

# Check routing SPF record
dig TXT yourdomain.com | grep spf

# Check sending DKIM record
dig TXT cf-bounce._domainkey.yourdomain.com

# Check routing DKIM record
dig TXT cf2024-1._domainkey.yourdomain.com

# Check routing MX records
dig MX yourdomain.com

# Check sending MX records (bounce handling)
dig MX cf-bounce.yourdomain.com
```

**Issue**: Existing DNS records conflict with Email Service.

**SPF conflicts:**

- Merge existing SPF records
- Remove duplicate `v=spf1` entries
- Ensure only one SPF record exists

**MX conflicts:**

- Email Routing requires Cloudflare MX records
- Remove or update existing MX records
- Cannot use Email Routing with external mail servers

**DKIM conflicts:**

- Use different selectors for different services
- `cf-bounce._domainkey` for Email Sending
- `cf2024-1._domainkey` for Email Routing
- `google._domainkey` for Google Workspace

## Domain management

Email Sending and Email Routing are managed separately. Removing one does not affect the other.

### Remove a domain from Email Sending

1. Go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select the domain to remove, then open **Settings**.
3. Select **Remove Domain** and confirm the action.

Removing a domain from Email Sending deletes the `cf-bounce` MX, SPF, DKIM, and DMARC records that Email Service created on the domain, and stops all outbound email sending from the domain.

### Remove a domain from Email Routing

1. Go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select the domain to remove, then open **Settings**.
3. Select **Disable Email Routing** and confirm the action.

Disabling Email Routing on a domain stops processing incoming emails and removes every routing-related DNS record (MX, SPF, DKIM) that Email Service added to the root domain. If you plan to switch to a different email provider, [unlock the records](#locked-dns-records) and add the new provider's records before disabling Email Routing so that mail flow is not interrupted.

Note

Unlocking an Email Service-created record and editing it to point to another provider does not remove its association with Email Service. The record will still be treated as managed by Email Service and will be deleted when you disable Email Routing.

### Transfer domain ownership

1. The domain must remain in the same Cloudflare account.
2. DNS records are tied to the account, not to specific users.
3. Use Cloudflare account-level permissions to manage access.

## Drop suppressed recipients

**Drop suppressed recipients** controls suppression handling for one sending domain. The setting is off by default.

When the setting is off, a message containing any [suppressed recipient](https://developers.cloudflare.com/email-service/concepts/suppressions/) fails. The REST API returns `400`, the Workers binding throws `E_RECIPIENT_SUPPRESSED`, and SMTP rejects the message.

When the setting is on, Email Service removes suppressed recipients and processes the remaining recipients.

To turn on the setting:

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select the sending domain, then open **Settings**.
3. Turn on **Drop suppressed recipients**.

## Email preview

Turn on **Email preview** to store sent messages so you can inspect their content in the [Activity log](https://developers.cloudflare.com/email-service/observability/logs/#message-preview). Previews cover messages sent while the setting is turned on and are retained for about seven days.

New sending domains have **Email preview** turned on automatically. To change the setting for a domain:

1. Go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select the domain, then open **Settings**.
3. Toggle **Enable email preview**.

## Next steps

- **[Send emails API](https://developers.cloudflare.com/email-service/api/send-emails/)**: Workers binding and REST API reference
- **[Domain authentication (DKIM and SPF)](https://developers.cloudflare.com/email-service/concepts/email-authentication/)**: Learn about SPF, DKIM, and DMARC
- **[Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/)**: Optimize email delivery

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/domains/#page","headline":"Domain configuration","description":"Configure and verify DNS records for Email Service sending and routing on your domain.","url":"https://developers.cloudflare.com/email-service/configuration/domains/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/domains/og.png?v=4871d876f74d072f","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Create and manage routing rules, destination addresses, and the catch-all rule in Email Service.
title: Email routing rules and addresses
image: https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/og.png?v=63b8f54a38ce8f37
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email routing rules and addresses

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

In Email Routing, a routing rule pairs an email pattern with a destination — either a verified email address or a Worker. You can route emails to either:

- Verified email addresses
- Workers with the `email` handler

This allows you to route emails to your preferred inbox, or apply logic with Workers before deciding what should happen to your emails. You can have multiple routing rules to route email from specific senders to specific mailboxes.

## Destination addresses

A destination address is the verified email address that Email Routing forwards messages to. Before you can create a routing rule, you must add and verify at least one destination address.

Destination addresses are shared at the account level and can be reused with any other domain in your account.

You can also send to verified destination addresses directly through the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) or the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), free of charge on any plan — including when only Email Routing is configured. Sends to verified destination addresses do not count toward your monthly quota or daily sending limits. Refer to [Pricing](https://developers.cloudflare.com/email-service/platform/pricing/) for details.

### Add a destination address

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account.
2. Go to **Compute** > **Email Service** > **Email Routing** > **Destination Addresses**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
3. Under **Destination addresses**, enter the email address you want to use as a destination in the inline form and submit it.
4. Cloudflare sends a verification email to that address. Open the email and select **Verify email address** to activate it.

Until a destination address is verified, any routing rule that points to it stays disabled.

### Manage destination addresses

The **Destination Addresses** page lists every address on your account, including those pending verification. From this page you can resend a verification email or delete a destination address.

Note

Deleting a destination address automatically disables all routing rules that use that address as a destination.

## Routing rules

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account and domain.
2. Go to **Compute** > **Email Service** > **Email Routing** > **Routing Rules**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
3. Select **Create routing rule**.
4. In **Email pattern**, enter the local part of the email address you want to use (for example, `my-new-email`), and select your domain.
5. In the **Action** drop-down menu, choose what this routing rule should do. Refer to [Routing rule actions](#routing-rule-actions) for more information.
6. In **Destination**, choose the verified address or Worker you want your emails to be forwarded to — for example, `your-name@gmail.com`. To add a new destination address, refer to [Add a destination address](#add-a-destination-address).

Note

If you create more than one rule with the same email pattern, only the rule shown first in the dashboard list processes incoming emails. To avoid this, do not create multiple rules with the same email pattern.

### Routing rule actions

When creating a routing rule, you must specify an **Action**:

- *Send to an email*: Emails will be routed to your destination address.
- *Send to a Worker*: Emails will be processed by the logic in your [Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/).
- *Drop*: Deletes emails matching the rule without routing them. This can be useful if you want to make an email address appear valid for privacy reasons.

Note

To prevent spamming unintended recipients, all routing rules are automatically disabled until the destination address is validated by the user.

### Disable a routing rule

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select **Routing Rules**.
3. Identify the routing rule you want to pause, and toggle the status button to **Disabled**.

Your routing rule is now disabled. It will not forward emails to a destination address or Worker. To forward emails again, toggle the routing rule status button to **Active**.

Renaming a Worker

Renaming a Worker removes the binding between that Worker and any routes that point to it. After renaming, edit the affected rules to point at the renamed Worker.

### Edit a routing rule

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select **Routing Rules**.
3. Identify the routing rule you want to edit, and select **Edit**.
4. Make the appropriate changes to the rule.

### Delete a routing rule

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account and domain.
2. Go to **Compute** > **Email Service** > **Email Routing** > **Routing Rules**.
3. Identify the routing rule you want to delete.
4. Select **Delete** and confirm the action.

Caution

Deleting a routing rule permanently removes it. Emails matching its pattern will no longer be routed. If you want to temporarily stop routing without deleting the rule, refer to [Disable a routing rule](#disable-a-routing-rule).

### Catch-all rule

When you enable this feature, Email Routing forwards every email sent to your domain, including misspelled local parts, to a single destination. For example, if you created a rule for `info@example.com` and a sender accidentally types `ifno@example.com`, the email will still be handled if you have the **Catch-all rule** enabled.

To enable the catch-all rule:

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select **Routing Rules**.
3. Enable **Catch-all rule**, so it shows as **Active**.
4. In the **Action** drop-down menu, select what to do with these emails. Refer to [Routing rule actions](#routing-rule-actions) for more information.
5. Select **Save**.

### Subaddressing

Email Routing supports subaddressing, also known as plus addressing, as defined in [RFC 5233 ↗︎](https://www.rfc-editor.org/rfc/rfc5233). This enables using the "+" separator to augment your routing rules with arbitrary detail information.

You can enable subaddressing at **Compute** > **Email Service** > **Email Routing** > **Settings**.

Once enabled, you can use subaddressing with any of your routing rules. For example, if you send an email to `user+detail@example.com` it will be matched by the `user@example.com` routing rule. The `+detail` part does not affect rule matching, but it is preserved in `message.to` and can be inspected by a [Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/), an [Agent application ↗︎](https://github.com/cloudflare/agents/tree/main/examples/email-agent), or via the activity log.

If a routing rule for `user+detail@example.com` already exists, it takes precedence over the rule for `user@example.com`. This prevents breaking existing routing rules and allows certain sub-addresses to be captured by a specific rule.

## Configure rules with Wrangler

If your routing rules send mail to a [Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/), you can define them in your Worker's [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) instead of creating them in the dashboard. Add the inbound addresses to the top-level `addresses` field:

This feature requires Wrangler 4.113.0 or later.

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-worker",
  "main": "src/index.ts",
  "upload_source_maps": true,
  // Set this to today's date
  "compatibility_date": "2026-10-18",
  "addresses": [
    "support@example.com",
    "*@example.com"
  ]
}
```

```toml
name = "my-worker"
main = "src/index.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-18"
addresses = ["support@example.com", "*@example.com"]
```

Each entry is either a literal recipient address (for example, `support@example.com`), which creates a routing rule for that address, or a `*@domain` catch-all (for example, `*@example.com`), which configures the [catch-all rule](#catch-all-rule) for that domain. Catch-all entries support apex domains only. To route mail sent to an Email Routing subdomain, list each literal recipient address, such as `support@mail.example.com`.

Every entry uses the *Send to a Worker* action to route mail to the Worker you are deploying.

The `addresses` field is top-level only and applies to every [environment](https://developers.cloudflare.com/workers/wrangler/environments/) of the Worker. Wrangler ignores `addresses` values under `env`.

If you omit `addresses`, Wrangler does not change Email Routing rules. If you set `addresses` to an empty array, Wrangler removes every Email Routing rule managed by this Worker.

When you run [`wrangler deploy`](https://developers.cloudflare.com/workers/wrangler/commands/workers/#deploy) or [`wrangler triggers deploy`](https://developers.cloudflare.com/workers/wrangler/commands/workers/#triggers), Wrangler reconciles the Email Routing rules for the addresses you listed. It adds rules for new entries, updates the rules it manages, and removes managed rules that are no longer listed. Wrangler prints the planned changes and asks you to confirm before applying a destructive change, such as deleting or taking over a rule. In non-interactive environments, Wrangler does not apply destructive changes and exits with an error after deploying the Worker.

To validate your local configuration without deploying or checking remote rules, run `wrangler deploy --dry-run`.

Note

Rules created from `addresses` are managed by your Wrangler configuration file. The dashboard warns you before an edit detaches a managed rule. On the next deploy, Wrangler reports a takeover conflict and asks for confirmation before restoring the configured rule.

## Next steps

- [Email handler](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/) — process emails programmatically with the `email()` handler.
- [Email Routing REST API](https://developers.cloudflare.com/email-service/api/email-routing-rest-api/) — manage routing rules and destination addresses programmatically.
- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — manage DNS records for Email Routing.
- [Email routing examples](https://developers.cloudflare.com/email-service/examples/email-routing/) — advanced patterns including spam filtering and email storage.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#page","headline":"Email routing rules and addresses","description":"Create and manage routing rules, destination addresses, and the catch-all rule in Email Service.","url":"https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/og.png?v=63b8f54a38ce8f37","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Enable MTA Strict Transport Security for your Email Service domain to protect against downgrade attacks.
title: Configure MTA-STS
image: https://developers.cloudflare.com/email-service/configuration/mta-sts/og.png?v=f1302ed23fb280a9
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Configure MTA-STS

Last updated Jun 24, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/mta-sts/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

MTA Strict Transport Security ([MTA-STS ↗︎](https://datatracker.ietf.org/doc/html/rfc8461)) was introduced by email service providers including Microsoft, Google and Yahoo as a solution to protect against downgrade and man-in-the-middle attacks in SMTP sessions, as well as solving the lack of security-first communication standards in email.

Suppose that `example.com` is your domain and uses Email Service. Here is how you can enable MTA-STS for it.

## Add the `_mta-sts` DNS record

1. In the Cloudflare dashboard, go to the **Records** page. [Go to **Records** ↗](https://dash.cloudflare.com/?to=/:account/:zone/dns/records)
2. Create a new CNAME record with the name `_mta-sts` that points to Cloudflare's record `_mta-sts.mx.cloudflare.net`. Make sure to disable the proxy mode.![MTA-STS CNAME record](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=1862,height=304,format=webp/_astro/mta-sts-record.DbwO-t_X.png)
3. Confirm that the record was created:

   ```sh
   dig txt _mta-sts.example.com
   ```

   ```sh
   _mta-sts.example.com. 300 IN  CNAME _mta-sts.mx.cloudflare.net.
   _mta-sts.mx.cloudflare.net. 300 IN  TXT "v=STSv1; id=20230615T153000;"
   ```

   This tells the other end client that is trying to connect to us that we support MTA-STS.

## Serve the policy file

Next you need an HTTPS endpoint at `mta-sts.example.com` to serve your policy file. This file defines the mail servers in the domain that use MTA-STS. The reason why HTTPS is used here instead of DNS is because not everyone uses DNSSEC yet, so we want to avoid another MITM attack vector.

To do this you need to deploy a Worker that allows email clients to pull Cloudflare's Email Service policy file using the "well-known" URI convention.

1. Deploy the MTA-STS proxy Worker to your account:

   [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/docs-examples/tree/main/workers/mta-sts-proxy)

   This Worker proxies `https://mta-sts.mx.cloudflare.net/.well-known/mta-sts.txt` to your own domain.
2. After deploying it, go to the Worker configuration, then **Settings** > **Domains & Routes** > **+Add**. Type the subdomain `mta-sts.example.com`.![MTA-STS Worker Custom Domain](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=1999,height=1148,format=webp/_astro/mta-sts-domain.UfZmAoBe.png) You can then confirm that your policy file is working with the following:

   ```sh
   curl https://mta-sts.example.com/.well-known/mta-sts.txt
   ```

   ```sh
   version: STSv1
   mode: enforce
   mx: *.mx.cloudflare.net
   max_age: 86400
   ```

   This says that you domain `example.com` enforces MTA-STS. Capable email clients will only deliver email to this domain over a secure connection to the specified MX servers. If no secure connection can be established the email will not be delivered. Test before enforcing

   A misconfigured policy in `enforce` mode causes legitimate inbound mail to be rejected. When rolling out MTA-STS on an existing domain, start with `mode: testing` and monitor [TLS-RPT ↗︎](https://datatracker.ietf.org/doc/html/rfc8460) reports for a few weeks before switching to `enforce`.

Email Service also supports MTA-STS upstream, which greatly improves security when forwarding your emails to service providers like Gmail, Microsoft, and others.

## Next steps

- [Email authentication](https://developers.cloudflare.com/email-service/concepts/email-authentication/) — SPF, DKIM, and DMARC reference.
- [Postmaster](https://developers.cloudflare.com/email-service/reference/postmaster/) — TLS, ARC, and SMTP details for postmasters.
- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — review your domain's DNS records.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/mta-sts/#page","headline":"Configure MTA-STS","description":"Enable MTA Strict Transport Security for your Email Service domain to protect against downgrade attacks.","url":"https://developers.cloudflare.com/email-service/configuration/mta-sts/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/mta-sts/og.png?v=f1302ed23fb280a9","dateModified":"2026-06-24","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Service send binding restrictions and Wrangler configuration variants.
title: Send bindings
image: https://developers.cloudflare.com/email-service/configuration/send-bindings/og.png?v=541cb44fc1f33153
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Send bindings

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/send-bindings/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

When you add a `send_email` binding to a Worker, you can restrict its sender and recipient addresses. The following Wrangler configuration variants apply these restrictions. For the binding API, refer to the [Email Sending Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/).

## Binding types

Each `send_email` entry supports a different restriction choice. The sender address must belong to an onboarded Email Service domain.

- **No restriction attribute**: The binding can send to any verified destination address in your account.
- **`destination_address`**: The binding can only send to the single destination address configured here. If you call `send()` with `to` set to `null` or `undefined`, the configured address is used.
- **`allowed_destination_addresses`**: The binding can only send to addresses listed in this allowlist.
- **`allowed_sender_addresses`**: The binding can only send from the addresses listed in this allowlist.

```jsonc
{
	"send_email": [
		// Send to any verified destination
		{ "name": "EMAIL" },
		// Send only to a single fixed destination
		{
			"name": "NOTIFY_OPS",
			"destination_address": "ops@yourdomain.com",
		},
		// Send only to addresses on an allowlist
		{
			"name": "EMAIL_TEAM",
			"allowed_destination_addresses": [
				"alice@yourdomain.com",
				"bob@yourdomain.com",
			],
		},
		// Send only from addresses on an allowlist
		{
			"name": "RESTRICTED_EMAIL",
			"allowed_sender_addresses": [
				"noreply@yourdomain.com",
				"support@yourdomain.com",
			],
		},
	],
}
```

```toml
[[send_email]]
name = "EMAIL"

[[send_email]]
name = "NOTIFY_OPS"
destination_address = "ops@yourdomain.com"

[[send_email]]
name = "EMAIL_TEAM"
allowed_destination_addresses = [ "alice@yourdomain.com", "bob@yourdomain.com" ]

[[send_email]]
name = "RESTRICTED_EMAIL"
allowed_sender_addresses = [ "noreply@yourdomain.com", "support@yourdomain.com" ]
```

## Next steps

- [Email Sending Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — send emails from a Worker using the binding.
- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — onboard the domains you send from.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/send-bindings/#page","headline":"Send bindings","description":"Email Service send binding restrictions and Wrangler configuration variants.","url":"https://developers.cloudflare.com/email-service/configuration/send-bindings/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/send-bindings/og.png?v=541cb44fc1f33153","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Configure Email Sending and Email Routing on subdomains within your zone.
title: Configure subdomains
image: https://developers.cloudflare.com/email-service/configuration/subdomains/og.png?v=84421650f7637406
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Configure subdomains

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/subdomains/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email Routing is a zone-level feature that applies to the apex domain (for example, `example.com`) by default. Email Sending treats each domain separately and is onboarded per domain. You can extend either service to subdomains of the same zone, such as `mail.example.com` or `corp.example.com`, but the onboarding flow differs between the two.

A zone can have up to 30 domains configured for Email Routing or Email Sending combined, including the apex domain. Refer to [Limits](https://developers.cloudflare.com/email-service/platform/limits/) for the full list of platform limits.

## Add a subdomain to Email Routing

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account and domain.
2. Go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
3. Select the apex domain, then open **Settings**.
4. Under **Subdomains**, enter the subdomain you want to enable in the inline form and submit it.

Cloudflare adds the required DNS records to the subdomain. Once the records propagate, you can create [literal routing rules](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/) for addresses on the subdomain. Catch-all rules are only available for the apex domain.

## Add a subdomain to Email Sending

Email Sending treats a subdomain as a separate sending domain. Onboard the subdomain through the standard onboarding flow:

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account.
2. Go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
3. Select **Onboard Domain** and choose the subdomain you want to send from. The onboarding flow adds the `cf-bounce` MX, SPF, DKIM, and DMARC records to the subdomain.
4. Select **Done**.

Once verified, you can send emails from addresses on the subdomain (for example, `notifications@mail.example.com`) using either the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) or the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/).

## Next steps

- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — manage DNS records for sending and routing.
- [Routing rules and addresses](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/) — create routing rules on subdomains.
- [Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/) — separate subdomains for different email types.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/subdomains/#page","headline":"Configure subdomains","description":"Configure Email Sending and Email Routing on subdomains within your zone.","url":"https://developers.cloudflare.com/email-service/configuration/subdomains/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/subdomains/og.png?v=84421650f7637406","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Manage Email Sending suppression entries for your whole account or for one sending domain.
title: Manage suppressions
image: https://developers.cloudflare.com/email-service/configuration/suppressions/og.png?v=538db99d7f01b642
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Manage suppressions

Last updated Sep 25, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/configuration/suppressions/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Manage recipients that Email Service must not contact. For suppression scope, triggers, and expiration rules, refer to [Suppression lists](https://developers.cloudflare.com/email-service/concepts/suppressions/).

Each suppression has a scope. An `account` suppression applies to every sending domain and subdomain in the account. A `sending_domain` suppression applies to one sending domain only. For details, refer to [Suppression scope](https://developers.cloudflare.com/email-service/concepts/suppressions/#suppression-scope).

## View suppressions

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select **Suppressions**.

The table displays each recipient, reason, scope, creation time, and expiration. **Never** means the entry has no scheduled expiration.

## Add a suppression

1. In **Suppressions**, enter the recipient email address.
2. Select an expiration preset, a custom future time, or **Never**.
3. In **Scope**, select **Account** or **Sending domain**. For **Sending domain**, select one of your sending domains or enter a domain.
4. Select **Add**.

Entries created in the dashboard have the `manual` reason. You cannot change the scope of an existing entry. To change it, delete the entry and add a new one.

## Import suppressions

You can paste addresses or upload `.csv`, `.json`, or `.txt` files. Supported file contents are:

- `.csv` or `.txt`: Put one entry on each line as `<EMAIL_ADDRESS>`, `<EMAIL_ADDRESS>,<EXPIRATION_TIMESTAMP>`, or `<EMAIL_ADDRESS>,<EXPIRATION_TIMESTAMP>,<SCOPE>`. Use an [RFC 3339 ↗︎](https://datatracker.ietf.org/doc/html/rfc3339) timestamp and omit the header row. `<SCOPE>` is `account` or a sending domain.
- `.json`: Use an array of address strings or objects. Each object requires `email` and can include an RFC 3339 `expires_at` timestamp and a `scope`. `scope` is `account`, a sending domain, or a scope object such as `{ "type": "sending_domain", "value": "mail.myappexample.com" }`.

1. In **Suppressions**, select **Import**.
2. Upload a supported file or paste the addresses.
3. In **Defaults**, choose an expiration and a scope for entries that do not set their own.
4. Select **Import**. Larger imports run in batches, so keep the dialog open until every batch finishes.

## Remove a suppression

To remove a mutable entry, select **Delete** for that recipient. Read-only entries do not provide a delete action.

Deleting an entry permits future delivery attempts. Verify that the recipient should receive mail before deleting an automatic suppression.

## Use the API

The [account Email Sending suppression management REST API](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/) supports listing, adding, importing, updating, and deleting entries. Requests require an [API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the **Email Sending: Edit** permission.

API clients must use `read_only` to determine whether an entry is mutable. Do not infer mutability from the `reason` value.

Every entry in an API response includes a `scope` object:

```json
{ "type": "account" }
```

```json
{ "type": "sending_domain", "value": "mail.myappexample.com" }
```

### Add a suppression for one sending domain

To add an entry for one sending domain, include `scope` in the request body. If you omit `scope`, the API creates an `account` entry.

```bash
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/suppressions \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "user@example.net",
    "scope": { "type": "sending_domain", "value": "mail.myappexample.com" }
  }'
```

The API lowercases `value` and removes trailing dots. The value can contain letters, digits, `.`, `-`, and `_`, up to 253 characters. For an internationalized domain, use its ASCII (`xn--`) form. Other values return `400` with the `invalid_scope_value` error code.

The API does not check that your account sends from the domain. An entry for a domain that you do not send from never blocks mail.

Bulk imports accept the same optional `scope` on each item. The API removes duplicate items that have the same address and scope.

### List suppressions by scope

The list returns `sending_domain` entries first, then `account` entries. Entries are sorted newest first within each scope. To list one scope only, use these query parameters:

| Parameter | Description |
| --- | --- |
| `scope_type` | `account` or `sending_domain`. |
| `scope_value` | One sending domain. Requires `scope_type=sending_domain`. |

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/suppressions?scope_type=sending_domain&scope_value=mail.myappexample.com" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

A `scope_value` without `scope_type=sending_domain` returns `400` with the `scope_value_requires_type` error code.

To find every suppression for one address, use the `email` parameter. The results can include one `account` suppression and one `sending_domain` suppression for each sending domain.

### Change the scope of an entry

You cannot change the scope of an existing entry. A `PATCH` request that includes `scope` returns `400` with the `scope_immutable` error code. To change the scope, delete the entry and create a new one.

Caution

Deleting a hard-bounce or complaint suppression permits delivery attempts to an address that already failed or reported your mail as spam. Verify the recipient before deleting the entry.

## Limits

Each address can have one active `account` suppression and one active `sending_domain` suppression for each sending domain. For page size, import, and rate limits, refer to [Suppression list limits](https://developers.cloudflare.com/email-service/platform/limits/#suppression-list-limits).

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/suppressions/#page","headline":"Manage suppressions","description":"Manage Email Sending suppression entries for your whole account or for one sending domain.","url":"https://developers.cloudflare.com/email-service/configuration/suppressions/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/configuration/suppressions/og.png?v=538db99d7f01b642","dateModified":"2026-09-25","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Store incoming emails in KV and process them with Queues for support tickets and workflow automation in Email Service.
title: Email storage and processing
image: https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/og.png?v=0445d82bbad6b2f0
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email storage and processing

Store and process incoming emails using KV storage and queue systems for support tickets and workflow automation

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Store and process incoming emails with comprehensive storage, queue processing, and support ticket automation for streamlined email workflow management.

## Store emails in KV

Store emails in a KV namespace for later processing. This example uses [`mimetext` ↗︎](https://www.npmjs.com/package/mimetext) to build replies, which requires the [`nodejs_compat`](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) compatibility flag.

```typescript
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

interface Env {
	EMAIL: SendEmail;
	EMAILS: KVNamespace;
	SUPPORT_TICKETS: KVNamespace;
}

export default {
	async email(message, env, ctx): Promise<void> {
		const emailId = `email-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

		// Read email content as an ArrayBuffer, then decode to a string
		const rawBuffer = await new Response(message.raw).arrayBuffer();
		const rawContent = new TextDecoder().decode(rawBuffer);

		// Store email metadata and content
		const emailData = {
			id: emailId,
			from: message.from,
			to: message.to,
			subject: message.headers.get("subject"),
			timestamp: new Date().toISOString(),
			size: message.rawSize,
			rawContent: rawContent,
			processed: false,
		};

		await env.EMAILS.put(emailId, JSON.stringify(emailData));

		// Process based on recipient
		if (message.to.includes("support@")) {
			await handleSupportEmail(message, env, emailId);
		} else {
			await message.forward("general@example.com");
		}
	},
};

async function handleSupportEmail(message, env, emailId) {
	const ticketId = `TICKET-${Date.now()}`;

	// Create support ticket
	const ticketData = {
		id: ticketId,
		emailId: emailId,
		from: message.from,
		subject: message.headers.get("subject"),
		status: "open",
		priority: "normal",
		createdAt: new Date().toISOString(),
		updatedAt: new Date().toISOString(),
	};

	await env.SUPPORT_TICKETS.put(ticketId, JSON.stringify(ticketData));

	// Send threaded auto-reply with ticket number
	const messageId = message.headers.get("Message-ID");
	const reply = createMimeMessage();
	if (messageId) {
		reply.setHeader("In-Reply-To", messageId);
		reply.setHeader("References", messageId);
	}
	reply.setSender(message.to);
	reply.setRecipient(message.from);
	reply.setSubject(`Support Ticket Created: ${ticketId}`);
	reply.addMessage({
		contentType: "text/html",
		data: `
            <h1>Support Ticket Created</h1>
            <p>Your support request has been received and assigned ticket number: <strong>${ticketId}</strong></p>
            <p>We will respond within 2-4 hours during business hours.</p>
            <hr>
            <p><em>Original subject: ${message.headers.get("subject")}</em></p>
        `,
	});

	await message.reply(
		new EmailMessage(message.to, message.from, reply.asRaw()),
	);

	// Forward to support team
	await message.forward("support-team@example.com");
}
```

## Queue-based processing

Process emails asynchronously using Cloudflare Queues. The `email()` handler sends a threaded acknowledgement with `message.reply()` and enqueues the message for later processing. The queue consumer runs in a separate invocation where the original `EmailMessage` is no longer available, so any follow-up emails it sends use `env.EMAIL.send()` instead.

```typescript
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

interface Env {
	EMAIL: SendEmail;
	EMAIL_QUEUE: Queue;
	EMAIL_STORAGE: KVNamespace;
	EMAIL_ANALYTICS: AnalyticsEngine;
	SUPPORT_TICKETS?: KVNamespace;
	SALES_LEADS?: KVNamespace;
}

interface EmailQueueMessage {
	emailId: string;
	from: string;
	to: string;
	subject: string;
	timestamp: string;
	priority: "low" | "normal" | "high" | "urgent";
	category: string;
}

export default {
	// Handle incoming emails
	async email(message, env, ctx): Promise<void> {
		const emailId = `email-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

		// Read raw email content as an ArrayBuffer, then decode to a string
		const rawBuffer = await new Response(message.raw).arrayBuffer();
		const rawContent = new TextDecoder().decode(rawBuffer);

		// Store email with metadata
		const emailData = {
			id: emailId,
			from: message.from,
			to: message.to,
			subject: message.headers.get("subject"),
			timestamp: new Date().toISOString(),
			size: message.rawSize,
			rawContent: rawContent,
			processed: false,
			status: "queued",
		};

		await env.EMAIL_STORAGE.put(emailId, JSON.stringify(emailData));

		// Determine priority and category
		const priority = determinePriority(message);
		const category = determineCategory(message);

		// Queue email for processing
		const queueMessage: EmailQueueMessage = {
			emailId,
			from: message.from,
			to: message.to,
			subject: message.headers.get("subject") || "",
			timestamp: new Date().toISOString(),
			priority,
			category,
		};

		await env.EMAIL_QUEUE.send(queueMessage, {
			delaySeconds: priority === "urgent" ? 0 : priority === "high" ? 5 : 30,
		});

		// Send threaded immediate auto-reply
		const messageId = message.headers.get("Message-ID");
		const reply = createMimeMessage();
		if (messageId) {
			reply.setHeader("In-Reply-To", messageId);
			reply.setHeader("References", messageId);
		}
		reply.setSender(message.to);
		reply.setRecipient(message.from);
		reply.setSubject(`Re: ${message.headers.get("subject")}`);
		reply.addMessage({
			contentType: "text/plain",
			data: "Thank you for your message. It has been queued for processing.",
		});

		await message.reply(
			new EmailMessage(message.to, message.from, reply.asRaw()),
		);
	},

	// Process queued emails
	async queue(batch, env, ctx): Promise<void> {
		console.log(`Processing ${batch.messages.length} queued emails`);

		for (const message of batch.messages) {
			try {
				const emailData = message.body as EmailQueueMessage;

				console.log(
					`Processing ${emailData.category} email from ${emailData.from}`,
				);

				// Get stored email content
				const storedEmailData = await env.EMAIL_STORAGE.get(emailData.emailId);
				if (!storedEmailData) {
					console.error(`Email data not found: ${emailData.emailId}`);
					message.ack();
					continue;
				}

				const emailContent = JSON.parse(storedEmailData);

				// Process based on category
				let processResult;
				switch (emailData.category) {
					case "support":
						processResult = await processSupport(emailData, emailContent, env);
						break;
					case "sales":
						processResult = await processSales(emailData, emailContent, env);
						break;
					case "billing":
						processResult = await processBilling(emailData, emailContent, env);
						break;
					default:
						processResult = await processGeneral(emailData, emailContent, env);
				}

				// Update email status
				emailContent.processed = true;
				emailContent.status = "completed";
				emailContent.processedAt = new Date().toISOString();
				emailContent.processingResult = processResult;

				await env.EMAIL_STORAGE.put(
					emailData.emailId,
					JSON.stringify(emailContent),
				);

				// Track processing metrics
				env.EMAIL_ANALYTICS?.writeDataPoint({
					blobs: [
						"email_processed",
						emailData.from,
						emailData.to,
						emailData.category,
						emailData.priority,
					],
					doubles: [1, emailContent.size],
					indexes: [
						`category:${emailData.category}`,
						`priority:${emailData.priority}`,
					],
				});

				message.ack();
			} catch (error) {
				console.error("Failed to process email:", error);
				message.retry();
			}
		}
	},
};

function determinePriority(message): "low" | "normal" | "high" | "urgent" {
	const subject = (message.headers.get("subject") || "").toLowerCase();
	const to = message.to.toLowerCase();

	if (subject.includes("urgent") || subject.includes("emergency")) {
		return "urgent";
	}

	if (
		to.includes("support") &&
		(subject.includes("down") || subject.includes("error"))
	) {
		return "high";
	}

	if (to.includes("sales") || to.includes("billing")) {
		return "high";
	}

	return "normal";
}

function determineCategory(message): string {
	const to = message.to.toLowerCase();
	const subject = (message.headers.get("subject") || "").toLowerCase();

	if (
		to.includes("support") ||
		subject.includes("help") ||
		subject.includes("issue")
	) {
		return "support";
	}

	if (
		to.includes("sales") ||
		subject.includes("quote") ||
		subject.includes("pricing")
	) {
		return "sales";
	}

	if (
		to.includes("billing") ||
		subject.includes("invoice") ||
		subject.includes("payment")
	) {
		return "billing";
	}

	return "general";
}

async function processSupport(
	emailData: EmailQueueMessage,
	emailContent: any,
	env: Env,
) {
	const ticketId = `TICKET-${Date.now()}`;

	// Create support ticket
	const ticketData = {
		id: ticketId,
		emailId: emailData.emailId,
		from: emailData.from,
		subject: emailData.subject,
		priority: emailData.priority,
		status: "open",
		category: "support",
		createdAt: new Date().toISOString(),
		updatedAt: new Date().toISOString(),
		content: emailContent.rawContent.substring(0, 5000), // Limit stored content
	};

	await env.SUPPORT_TICKETS?.put(ticketId, JSON.stringify(ticketData));

	// Send confirmation email
	await env.EMAIL.send({
		to: emailData.from,
		from: "support@yourdomain.com",
		subject: `Support Ticket Created: ${ticketId}`,
		html: `
			<h2>Support Ticket Created</h2>
			<p>Your support request has been received and assigned ticket number: <strong>${ticketId}</strong></p>
			<p><strong>Priority:</strong> ${emailData.priority}</p>
			<p>We will respond based on the priority level:</p>
			<ul>
				<li><strong>Urgent:</strong> Within 1 hour</li>
				<li><strong>High:</strong> Within 4 hours</li>
				<li><strong>Normal:</strong> Within 24 hours</li>
			</ul>
			<hr>
			<p><em>Original subject: ${emailData.subject}</em></p>
		`,
	});

	return { ticketId, action: "ticket_created" };
}

async function processSales(
	emailData: EmailQueueMessage,
	emailContent: any,
	env: Env,
) {
	// Create sales lead
	const leadId = `LEAD-${Date.now()}`;

	const leadData = {
		id: leadId,
		emailId: emailData.emailId,
		contact: emailData.from,
		subject: emailData.subject,
		priority: emailData.priority,
		status: "new",
		source: "email",
		createdAt: new Date().toISOString(),
	};

	await env.SALES_LEADS?.put(leadId, JSON.stringify(leadData));

	// Send sales response
	await env.EMAIL.send({
		to: emailData.from,
		from: "sales@yourdomain.com",
		subject: `Re: ${emailData.subject}`,
		html: `
			<h2>Thank you for your interest!</h2>
			<p>We've received your sales inquiry and assigned it reference: <strong>${leadId}</strong></p>
			<p>A member of our sales team will contact you within 24 hours.</p>
			<p>Best regards,<br>Sales Team</p>
		`,
	});

	return { leadId, action: "lead_created" };
}

async function processBilling(
	emailData: EmailQueueMessage,
	emailContent: any,
	env: Env,
) {
	// Handle billing inquiries
	await env.EMAIL.send({
		to: emailData.from,
		from: "billing@yourdomain.com",
		subject: `Re: ${emailData.subject}`,
		html: `
			<h2>Billing Inquiry Received</h2>
			<p>Thank you for contacting our billing department.</p>
			<p>Your inquiry has been forwarded to our billing specialists who will respond within 2 business hours.</p>
			<p>For immediate assistance, please call: +1-800-555-0123</p>
		`,
	});

	return { action: "billing_forwarded" };
}

async function processGeneral(
	emailData: EmailQueueMessage,
	emailContent: any,
	env: Env,
) {
	// Handle general inquiries
	await env.EMAIL.send({
		to: emailData.from,
		from: "info@yourdomain.com",
		subject: `Re: ${emailData.subject}`,
		text: `
			Thank you for contacting us.
			
			We have received your message and will respond within 48 hours.
			
			For urgent matters, please contact our support team at support@yourdomain.com.
			
			Best regards,
			Customer Service Team
		`,
	});

	return { action: "general_acknowledged" };
}
```

## Next steps

- [Email handler](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/) — reference for the `email()` handler and its actions.
- [Spam filtering](https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/) — block spam before storing or processing.
- [Hard bounce handling](https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/) — detect and process bounce notifications.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/#page","headline":"Email storage and processing","description":"Store incoming emails in KV and process them with Queues for support tickets and workflow automation in Email Service.","url":"https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/og.png?v=0445d82bbad6b2f0","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Detect hard bounce notifications and store recipients in Workers KV.
title: Hard bounce handling
image: https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/og.png?v=f99a6c6d7fdb1fa0
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Hard bounce handling

Detect and handle hard bounce emails to maintain sender reputation and manage undeliverable addresses

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This example detects hard bounce notifications and stores recipient addresses in Workers KV.

## Identify hard bounces

Hard bounces occur when an email cannot be delivered due to permanent reasons:

- Invalid email address: The email address does not exist
- Domain does not exist: The domain name is invalid or expired
- Mailbox full: The recipient's mailbox has exceeded storage limits
- Email blocked: The recipient's server permanently rejects emails

## Configure bindings

Configure your worker to handle bounce notifications:

```jsonc
{
	"name": "bounce-handler",
	// Set this to today's date
	"compatibility_date": "2026-10-18",
	"send_email": [{ "name": "EMAIL" }],
	"kv_namespaces": [
		{
			"binding": "SUPPRESSION_LIST",
			"id": "your-kv-namespace-id",
		},
	],
}
```

```toml
name = "bounce-handler"
# Set this to today's date
compatibility_date = "2026-10-18"

[[send_email]]
name = "EMAIL"

[[kv_namespaces]]
binding = "SUPPRESSION_LIST"
id = "your-kv-namespace-id"
```

## Detect hard bounces

```javascript
import * as PostalMime from "postal-mime";

export default {
	async email(message, env, ctx) {
		// Parse the raw email message
		const parser = new PostalMime.default();
		const rawEmail = new Response(message.raw);
		const email = await parser.parse(await rawEmail.arrayBuffer());

		// Check if this is a bounce notification
		if (isBounceNotification(email)) {
			const bounceInfo = await parseBounceInfo(email);

			if (bounceInfo.type === "hard") {
				await handleHardBounce(bounceInfo, env);
				console.log(
					`Hard bounce processed for: ${bounceInfo.originalRecipient}`,
				);
				return;
			}
		}

		// Forward non-bounce emails normally
		await message.forward("admin@yourdomain.com");
	},
};

function isBounceNotification(email) {
	// Check common bounce indicators
	const subject = email.subject?.toLowerCase() || "";
	const fromAddress = email.from?.address?.toLowerCase() || "";

	// Common bounce indicators
	const bounceSubjects = [
		"mail delivery failed",
		"undelivered mail returned to sender",
		"delivery status notification",
		"returned mail",
		"mail system error",
	];

	const bounceFromPatterns = [
		"mailer-daemon",
		"mail-daemon",
		"postmaster",
		"noreply",
		"bounce",
	];

	return (
		bounceSubjects.some((phrase) => subject.includes(phrase)) ||
		bounceFromPatterns.some((pattern) => fromAddress.includes(pattern))
	);
}

async function parseBounceInfo(email) {
	const text = email.text || "";
	const html = email.html || "";
	const content = text + " " + html;

	// Extract original recipient email
	const recipientMatch =
		content.match(/(?:to|for|recipient):\s*([^\s<]+@[^\s>]+)/i) ||
		content.match(/([^\s<]+@[^\s>]+)/);

	const originalRecipient = recipientMatch ? recipientMatch[1] : null;

	// Determine bounce type based on content
	const hardBounceIndicators = [
		"user unknown",
		"no such user",
		"invalid recipient",
		"recipient address rejected",
		"mailbox unavailable",
		"domain not found",
		"5.1.1", // SMTP error code for bad destination mailbox
		"5.1.2", // SMTP error code for bad destination system
		"5.4.1", // SMTP error code for no answer from host
	];

	const isHardBounce = hardBounceIndicators.some((indicator) =>
		content.toLowerCase().includes(indicator.toLowerCase()),
	);

	return {
		type: isHardBounce ? "hard" : "soft",
		originalRecipient,
		reason: extractBounceReason(content),
		timestamp: new Date().toISOString(),
	};
}

function extractBounceReason(content) {
	// Extract the specific error message
	const reasonPatterns = [
		/diagnostic[- ]code:\s*(.+)/i,
		/reason:\s*(.+)/i,
		/error:\s*(.+)/i,
		/(5\.\d+\.\d+[^.\n]*)/i,
	];

	for (const pattern of reasonPatterns) {
		const match = content.match(pattern);
		if (match) {
			return match[1].trim().split("\n")[0]; // Take first line only
		}
	}

	return "Unknown bounce reason";
}

async function handleHardBounce(bounceInfo, env) {
	if (!bounceInfo.originalRecipient) {
		console.log("Could not extract original recipient from bounce");
		return;
	}

	// Add to suppression list in KV
	await env.SUPPRESSION_LIST.put(
		bounceInfo.originalRecipient,
		JSON.stringify({
			type: "hard_bounce",
			reason: bounceInfo.reason,
			timestamp: bounceInfo.timestamp,
			status: "suppressed",
		}),
		{
			metadata: {
				bounceType: "hard",
				addedDate: bounceInfo.timestamp,
			},
		},
	);

	console.log(
		`Added ${bounceInfo.originalRecipient} to suppression list: ${bounceInfo.reason}`,
	);
}
```

## Test the handler

Create a test bounce notification:

```bash
curl --request POST 'http://localhost:8787/cdn-cgi/local/email' \
  --url-query 'from=mailer-daemon@example.com' \
  --url-query 'to=bounce-handler@yourdomain.com' \
  --header 'Content-Type: application/json' \
  --data-raw 'From: Mail Delivery Subsystem <mailer-daemon@example.com>
To: bounce-handler@yourdomain.com
Subject: Mail delivery failed: returning message to sender
Date: Wed, 28 Aug 2024 10:30:00 +0000
Message-ID: <bounce123@example.com>

This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  nonexistent@example.com
    SMTP error from remote mail server after RCPT TO:<nonexistent@example.com>:
    host mx.example.com [192.168.1.1]: 550 5.1.1 User unknown

------ This is a copy of the message, including all the headers. ------

Return-path: <sender@yourdomain.com>
From: sender@yourdomain.com
To: nonexistent@example.com
Subject: Welcome to our service
Message-ID: <original123@yourdomain.com>

Welcome! Thanks for signing up.'
```

## Check the suppression list

Add a utility function to check if an email is suppressed before sending:

```javascript
async function isEmailSuppressed(email, env) {
	const suppressionEntry = await env.SUPPRESSION_LIST.get(email);

	if (suppressionEntry) {
		const data = JSON.parse(suppressionEntry);
		console.log(`Email ${email} is suppressed: ${data.reason}`);
		return true;
	}

	return false;
}

// Use before sending emails
export async function sendEmail(recipient, subject, content, env) {
	if (await isEmailSuppressed(recipient, env)) {
		console.log(`Skipping email to suppressed address: ${recipient}`);
		return { success: false, reason: "suppressed" };
	}

	// Proceed with email sending
	// ... your email sending logic
}
```

## Best practices

1. **Monitor bounce rates**: Track bounce rates to maintain good sender reputation
2. **Automatic cleanup**: Regularly review and clean suppression lists
3. **Double opt-in**: Use double opt-in to reduce invalid addresses
4. **Retry logic**: Implement appropriate retry logic for soft bounces
5. **Logging**: Log all bounce handling for debugging and analytics

## Next steps

- Review [email authentication](https://developers.cloudflare.com/email-service/concepts/email-authentication/) to improve deliverability
- Set up [metrics and analytics](https://developers.cloudflare.com/email-service/observability/metrics-analytics/) to monitor bounce rates
- Implement [spam filtering](https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/) for incoming emails

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/#page","headline":"Hard bounce handling","description":"Detect hard bounce notifications and store recipients in Workers KV.","url":"https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/og.png?v=f99a6c6d7fdb1fa0","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Filter email with subject keywords and trusted-domain heuristics.
title: Spam filtering
image: https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/og.png?v=bcadf0d23ef36e90
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Spam filtering

Filter email with subject keywords and trusted-domain heuristics

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This example scores incoming email using subject keywords and sender-domain heuristics.

## Basic spam filter

Simple spam detection with keyword matching and domain validation:

```typescript
interface Env {
	EMAIL: SendEmail;
	EMAIL_ANALYTICS: AnalyticsEngine;
}

interface SpamFilter {
	checkSpam(
		message: any,
	): Promise<{ isSpam: boolean; score: number; reasons: string[] }>;
}

class SimpleSpamFilter implements SpamFilter {
	private spamKeywords = [
		"buy now",
		"limited time",
		"act fast",
		"click here",
		"free money",
		"guaranteed",
		"risk free",
		"urgent",
		"winner",
		"congratulations",
		"inheritance",
		"lottery",
	];

	private trustedDomains = ["example.com", "trusted-partner.com", "vendor.net"];

	async checkSpam(
		message,
	): Promise<{ isSpam: boolean; score: number; reasons: string[] }> {
		let score = 0;
		const reasons = [];

		const sender = message.from;
		const subject = message.headers.get("subject") || "";
		const senderDomain = sender.split("@")[1];

		// Check sender domain
		if (this.trustedDomains.includes(senderDomain)) {
			score -= 2; // Trusted sender
		}

		// Check subject for spam keywords
		const subjectLower = subject.toLowerCase();
		for (const keyword of this.spamKeywords) {
			if (subjectLower.includes(keyword)) {
				score += 1;
				reasons.push(`Spam keyword: ${keyword}`);
			}
		}

		// Check for excessive capitalization
		const capsRatio = (subject.match(/[A-Z]/g) || []).length / subject.length;
		if (capsRatio > 0.7 && subject.length > 10) {
			score += 1;
			reasons.push("Excessive capitalization");
		}

		// Check for suspicious patterns
		if (subject.includes("!!!") || subject.includes("$$$")) {
			score += 1;
			reasons.push("Suspicious punctuation");
		}

		// Check for suspicious sender patterns
		if (
			sender.includes("noreply") &&
			subject.toLowerCase().includes("urgent")
		) {
			score += 2;
			reasons.push("Suspicious noreply + urgent combination");
		}

		return {
			isSpam: score >= 2,
			score,
			reasons,
		};
	}
}

const spamFilter = new SimpleSpamFilter();

export default {
	async email(message, env, ctx): Promise<void> {
		const startTime = Date.now();

		// Check for spam
		const spamCheck = await spamFilter.checkSpam(message);

		// Track spam check metrics
		env.EMAIL_ANALYTICS?.writeDataPoint({
			blobs: [
				"spam_check_completed",
				message.from,
				message.to,
				spamCheck.isSpam ? "spam" : "legitimate",
			],
			doubles: [
				1, // Count
				spamCheck.score,
				Date.now() - startTime,
			],
			indexes: [
				`spam_detected:${spamCheck.isSpam}`,
				`score_range:${getScoreRange(spamCheck.score)}`,
			],
		});

		if (spamCheck.isSpam) {
			console.log(
				`Rejected spam email from ${message.from}: ${spamCheck.reasons.join(", ")}`,
			);
			message.setReject(`Message rejected: ${spamCheck.reasons[0]}`);
			return;
		}

		// Add spam score headers and forward
		const headers = new Headers();
		headers.set("X-Spam-Score", spamCheck.score.toString());
		headers.set("X-Spam-Reasons", spamCheck.reasons.join(", "));
		headers.set("X-Spam-Check-Time", (Date.now() - startTime).toString());

		await message.forward("inbox@example.com", headers);
	},
};

function getScoreRange(score: number): string {
	if (score < 0) return "trusted";
	if (score === 0) return "neutral";
	if (score === 1) return "suspicious";
	return "spam";
}
```

## Optional Workers AI integration

The basic spam filter uses simple heuristic checks. You can optionally use [Workers AI](https://developers.cloudflare.com/workers-ai/) to analyze email content.

## Next steps

- [Email handler](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/) — reference for `setReject()` and `forward()` actions used here.
- [Hard bounce handling](https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/) — detect bounce notifications that often originate from spam infrastructure.
- [Email storage and processing](https://developers.cloudflare.com/email-service/examples/email-routing/email-storage/) — log filtered emails to KV for later review.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/#page","headline":"Spam filtering","description":"Filter email with subject keywords and trusted-domain heuristics.","url":"https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/og.png?v=bcadf0d23ef36e90","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Advanced patterns and examples for sending transactional emails with Email Service.
title: Email sending
image: https://developers.cloudflare.com/email-service/examples/email-sending/og.png?v=c52f52e1d819eca1
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email sending

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Advanced patterns and examples for sending emails with Cloudflare Email Service. Most examples use the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/); the same [`EmailMessageBuilder`](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#send-method) fields (`to`, `from`, `subject`, `html`, `text`, `cc`, `bcc`, `replyTo`, `attachments`, `headers`) apply to the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) as JSON in your HTTP request body. The [Send email over SMTP](https://developers.cloudflare.com/email-service/examples/email-sending/smtp/) example covers sending over SMTP from several languages and clients.

- [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/)
- [User signup flow](https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/)
- [Email a login link](https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/)
- [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/)
- [Send email over SMTP](https://developers.cloudflare.com/email-service/examples/email-sending/smtp/)
- [Sync recipient records](https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/)

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"WebPage","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/#page","headline":"Email sending","description":"Advanced patterns and examples for sending transactional emails with Email Service.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/og.png?v=c52f52e1d819eca1","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Handle different types of email attachments including PDFs, inline images, and file uploads with validation and encoding.
title: Email attachments
image: https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/og.png?v=55b81084399408df
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email attachments

Send emails with various types of attachments including PDFs, images, and file uploads with proper validation.

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This example demonstrates how to send emails with various types of attachments including PDFs, inline images, and file uploads.

Configure the email binding in your Wrangler configuration file:

```jsonc
{
	"send_email": [{ "name": "EMAIL" }],
	"vars": {
		"DOMAIN": "yourdomain.com",
	},
}
```

```toml
[[send_email]]
name = "EMAIL"

[vars]
DOMAIN = "yourdomain.com"
```

```typescript
interface Env {
	EMAIL: SendEmail;
	DOMAIN: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const url = new URL(request.url);

		if (url.pathname === "/send-invoice" && request.method === "POST") {
			return sendInvoiceWithPDF(request, env);
		}

		if (url.pathname === "/send-report" && request.method === "POST") {
			return sendReportWithImages(request, env);
		}

		if (url.pathname === "/upload-attachment" && request.method === "POST") {
			return sendEmailWithUpload(request, env);
		}

		return new Response("Not Found", { status: 404 });
	},
};
```

## PDF attachments

Generate and send PDF documents as email attachments:

```typescript
async function sendInvoiceWithPDF(
	request: Request,
	env: Env,
): Promise<Response> {
	const { customerEmail, invoiceData } = await request.json();

	// Generate PDF content
	const pdfContent = generateInvoicePDF(invoiceData);

	await env.EMAIL.send({
		to: customerEmail,
		from: `billing@${env.DOMAIN}`,
		subject: `Invoice #${invoiceData.number}`,
		html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <h1>Invoice #${invoiceData.number}</h1>
        <p>Dear ${invoiceData.customerName},</p>
        
        <div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;">
          <h3>Invoice Details</h3>
          <p><strong>Invoice Number:</strong> ${invoiceData.number}</p>
          <p><strong>Date:</strong> ${new Date(invoiceData.date).toLocaleDateString()}</p>
          <p><strong>Amount Due:</strong> $${invoiceData.total}</p>
          <p><strong>Due Date:</strong> ${new Date(invoiceData.dueDate).toLocaleDateString()}</p>
        </div>
        
        <p>Please find your invoice attached. Payment is due within 30 days.</p>
        <p>Thank you for your business!</p>
      </div>
    `,
		attachments: [
			{
				filename: `invoice-${invoiceData.number}.pdf`,
				content: new TextEncoder().encode(pdfContent).buffer,
				type: "application/pdf",
				disposition: "attachment",
			},
		],
	});

	return new Response(
		JSON.stringify({ success: true, message: "Invoice sent" }),
	);
}

function generateInvoicePDF(invoiceData: any): string {
	// Simple PDF generation (in practice, use a proper PDF library)
	const pdfContent = `%PDF-1.4
1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj
2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj
3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>endobj
4 0 obj<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>endobj
5 0 obj<< /Length 200 >>stream
BT
/F1 24 Tf
50 700 Td
(INVOICE #${invoiceData.number}) Tj
0 -50 Td
/F1 12 Tf
(Customer: ${invoiceData.customerName}) Tj
0 -20 Td
(Date: ${new Date(invoiceData.date).toLocaleDateString()}) Tj
0 -20 Td
(Amount: $${invoiceData.total}) Tj
ET
endstream
endobj
xref
0 6
0000000000 65535 f 
0000000010 00000 n 
0000000053 00000 n 
0000000100 00000 n 
0000000200 00000 n 
0000000300 00000 n 
trailer<< /Size 6 /Root 1 0 R >>
startxref
400
%%EOF`;

	return pdfContent;
}
```

## Inline images

Embed images directly in email content using Content-ID references:

```typescript
async function sendReportWithImages(
	request: Request,
	env: Env,
): Promise<Response> {
	const { recipientEmail, reportData } = await request.json();

	// Generate chart and logo images as SVG
	const chartImage = generateChartImage(reportData);
	const logoImage = getCompanyLogo();

	await env.EMAIL.send({
		to: recipientEmail,
		from: `reports@${env.DOMAIN}`,
		subject: `${reportData.title} - ${reportData.period}`,
		html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <!-- Company Logo (inline) -->
        <div style="text-align: center; margin-bottom: 30px;">
          <img src="/service/cid:company-logo" alt="Company Logo" style="width: 200px; height: auto;">
        </div>
        
        <h1 style="color: #2563eb;">${reportData.title}</h1>
        <p style="color: #666;">Period: ${reportData.period}</p>
        
        <h2>Key Metrics</h2>
        <div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;">
          <p><strong>Revenue:</strong> $${reportData.metrics.revenue.toLocaleString()}</p>
          <p><strong>New Users:</strong> ${reportData.metrics.users.toLocaleString()}</p>
          <p><strong>Growth Rate:</strong> ${reportData.metrics.growth}%</p>
        </div>
        
        <h2>Performance Chart</h2>
        <!-- Inline chart image -->
        <div style="text-align: center; margin: 20px 0;">
          <img src="/service/cid:performance-chart" alt="Performance Chart" 
               style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;">
        </div>
        
        <p>For detailed analysis, please contact our team.</p>
      </div>
    `,
		attachments: [
			{
				filename: "company-logo.svg",
				content: logoImage,
				type: "image/svg+xml",
				disposition: "inline",
				contentId: "company-logo",
			},
			{
				filename: "performance-chart.svg",
				content: chartImage,
				type: "image/svg+xml",
				disposition: "inline",
				contentId: "performance-chart",
			},
		],
	});

	return new Response(
		JSON.stringify({ success: true, message: "Report sent" }),
	);
}

function generateChartImage(reportData: any): ArrayBuffer {
	// Generate simple SVG chart (in practice, use a chart library)
	const chartSVG = `
    <svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
      <rect width="400" height="200" fill="#f8f9fa" stroke="#dee2e6"/>
      <rect x="50" y="150" width="30" height="${Math.max(10, reportData.metrics.growth * 2)}" fill="#2563eb"/>
      <rect x="100" y="120" width="30" height="50" fill="#28a745"/>
      <rect x="150" y="100" width="30" height="70" fill="#ffc107"/>
      <rect x="200" y="80" width="30" height="90" fill="#dc3545"/>
      <text x="200" y="30" text-anchor="middle" font-family="Arial" font-size="16">Performance Chart</text>
    </svg>
  `;

	return new TextEncoder().encode(chartSVG).buffer;
}

function getCompanyLogo(): ArrayBuffer {
	// Simple SVG logo
	const logoSVG = `
    <svg width="200" height="60" xmlns="http://www.w3.org/2000/svg">
      <rect width="200" height="60" fill="#2563eb" rx="8"/>
      <text x="100" y="35" text-anchor="middle" font-family="Arial"
            font-size="24" font-weight="bold" fill="white">Company</text>
    </svg>
  `;

	return new TextEncoder().encode(logoSVG).buffer;
}
```

## File uploads

Handle file uploads and send them as email attachments with validation:

```typescript
async function sendEmailWithUpload(
	request: Request,
	env: Env,
): Promise<Response> {
	const formData = await request.formData();
	const file = formData.get("file") as File;
	const recipientEmail = formData.get("email") as string;
	const message = formData.get("message") as string;
	const senderName = (formData.get("senderName") as string) || "Someone";

	if (!file || !recipientEmail) {
		return new Response(
			JSON.stringify({
				error: "Missing required fields: file and email",
			}),
			{ status: 400 },
		);
	}

	// Validate file
	const validation = validateFile(file);
	if (!validation.valid) {
		return new Response(
			JSON.stringify({
				error: validation.error,
			}),
			{ status: 400 },
		);
	}

	await env.EMAIL.send({
		to: recipientEmail,
		from: `uploads@${env.DOMAIN}`,
		subject: `File shared: ${file.name}`,
		html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <h1 style="color: #2563eb;">File Shared</h1>

        <div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;">
          <p><strong>${senderName}</strong> has shared a file with you.</p>
          ${message ? `<p><em>"${message}"</em></p>` : ""}
        </div>

        <div style="border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; margin: 20px 0;">
          <h3 style="margin: 0 0 15px 0;">File Details</h3>
          <p><strong>Filename:</strong> ${file.name}</p>
          <p><strong>Size:</strong> ${formatFileSize(file.size)}</p>
          <p><strong>Type:</strong> ${getFileTypeDescription(file.type)}</p>
        </div>

        <p>The file is attached to this email for download.</p>

        <div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 4px;">
          <p style="margin: 0; color: #856404;">
            <strong>Security notice:</strong> Always verify the source before opening attachments.
          </p>
        </div>
      </div>
    `,
		attachments: [
			{
				filename: file.name,
				content: await file.arrayBuffer(),
				type: file.type,
				disposition: "attachment",
			},
		],
	});

	return new Response(
		JSON.stringify({
			success: true,
			message: `File ${file.name} sent to ${recipientEmail}`,
		}),
	);
}

function validateFile(file: File): { valid: boolean; error?: string } {
	// 5 MiB matches the general outbound message size limit. Messages up to
	// 25 MiB are accepted only when sent to verified destination addresses.
	// Refer to /email-service/platform/limits/ for details.
	const maxSize = 5 * 1024 * 1024;
	const allowedTypes = [
		"image/jpeg",
		"image/png",
		"image/gif",
		"application/pdf",
		"text/plain",
		"text/csv",
		"application/msword",
		"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		"application/vnd.ms-excel",
		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
		"application/zip",
	];

	if (file.size === 0) {
		return { valid: false, error: "File is empty" };
	}

	if (file.size > maxSize) {
		return {
			valid: false,
			error: `File too large (max ${formatFileSize(maxSize)})`,
		};
	}

	if (!allowedTypes.includes(file.type)) {
		return { valid: false, error: "File type not allowed" };
	}

	// Check for dangerous extensions
	const dangerousExtensions = [".exe", ".bat", ".cmd", ".scr", ".vbs", ".js"];
	const fileName = file.name.toLowerCase();
	if (dangerousExtensions.some((ext) => fileName.endsWith(ext))) {
		return {
			valid: false,
			error: "File extension not allowed for security reasons",
		};
	}

	return { valid: true };
}

function formatFileSize(bytes: number): string {
	if (bytes === 0) return "0 Bytes";
	const k = 1024;
	const sizes = ["Bytes", "KB", "MB", "GB"];
	const i = Math.floor(Math.log(bytes) / Math.log(k));
	return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}

function getFileTypeDescription(mimeType: string): string {
	const typeMap: Record<string, string> = {
		"image/jpeg": "JPEG Image",
		"image/png": "PNG Image",
		"image/gif": "GIF Image",
		"application/pdf": "PDF Document",
		"text/plain": "Text Document",
		"application/msword": "Word Document",
		"application/vnd.ms-excel": "Excel Spreadsheet",
		"application/zip": "ZIP Archive",
	};

	return typeMap[mimeType] || "Unknown";
}
```

## Next steps

- [Send method](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — full reference for the `Attachment` interface and the `send()` method.
- [Limits](https://developers.cloudflare.com/email-service/platform/limits/) — message size and attachment limits.
- [Email headers](https://developers.cloudflare.com/email-service/reference/headers/) — set custom headers alongside attachments.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/#page","headline":"Email attachments","description":"Handle different types of email attachments including PDFs, inline images, and file uploads with validation and encoding.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/og.png?v=55b81084399408df","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Generate and email a login link from a Worker.
title: Email a login link
image: https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/og.png?v=a014a614113a81d9
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email a login link

Generate and email a login link from a Worker.

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This example generates a login link and emails it with Cloudflare Email Service.

Configure the email binding in your Wrangler configuration file:

```jsonc
{
	"send_email": [{ "name": "EMAIL" }],
	"vars": {
		"DOMAIN": "yourdomain.com",
	},
}
```

```toml
[[send_email]]
name = "EMAIL"

[vars]
DOMAIN = "yourdomain.com"
```

The Worker exposes a `POST /send-magic-link` route. It checks the email address, generates a random token, and emails a login link.

```typescript
interface Env {
	EMAIL: SendEmail;
	DOMAIN: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const url = new URL(request.url);

		if (url.pathname === "/send-magic-link" && request.method === "POST") {
			return handleSendMagicLink(request, env);
		}

		return new Response("Not Found", { status: 404 });
	},
};

async function handleSendMagicLink(
	request: Request,
	env: Env,
): Promise<Response> {
	const { email } = await request.json();

	if (!email || !isValidEmail(email)) {
		return new Response(JSON.stringify({ error: "Invalid email" }), {
			status: 400,
		});
	}

	// Generate a random token for the login link
	const token = crypto.randomUUID();
	const magicUrl = `https://${env.DOMAIN}/login?token=${token}`;

	// Send magic link email
	await env.EMAIL.send({
		to: email,
		from: `noreply@${env.DOMAIN}`,
		subject: "Your login link",
		html: `
			<h1>Login to your account</h1>
			<p>Click the link below to log in:</p>
			<p><a href="https://developers.cloudflare.com/$%7BmagicUrl%7D">Login Now</a></p>
		`,
		text: `
			Login to your account
			
			Click this link to log in: ${magicUrl}
			
		`,
	});

	return new Response(
		JSON.stringify({
			success: true,
			message: "Magic link sent to your email",
		}),
	);
}

function isValidEmail(email: string): boolean {
	return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
```

## Next steps

- [User signup flow](https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/) — combine magic links with account verification.
- [Send method](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — full reference for the `send()` method.
- [Email headers](https://developers.cloudflare.com/email-service/reference/headers/) — add tracking or list-management headers.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/#page","headline":"Email a login link","description":"Generate and email a login link from a Worker.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/og.png?v=a014a614113a81d9","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Send to multiple recipients, CC and BCC, and named addresses using the Workers binding, REST API, or SMTP.
title: Specify recipients
image: https://developers.cloudflare.com/email-service/examples/email-sending/recipients/og.png?v=350ae4cc036927b2
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Specify recipients

Specify multiple recipients, CC and BCC, and named addresses when sending with Email Service.

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email Service lets you specify recipients in several ways — multiple recipients, CC and BCC, and named addresses — using the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/), or [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/). The combined number of addresses across `to`, `cc`, and `bcc` must not exceed 50. See [Limits](https://developers.cloudflare.com/email-service/platform/limits/).

## Multiple recipients

```ts
const response = await env.EMAIL.send({
	to: ["user1@example.com", "user2@example.com", "user3@example.com"],
	from: { email: "newsletter@yourdomain.com", name: "Newsletter Team" },
	subject: "Monthly Newsletter",
	html: "<h1>This month's updates</h1>",
	text: "This month's updates",
});
```

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": ["user1@example.com", "user2@example.com"],
    "from": { "address": "newsletter@yourdomain.com", "name": "Newsletter Team" },
    "subject": "Monthly Newsletter",
    "html": "<h1>This month'\''s updates</h1>",
    "text": "This month'\''s updates"
  }'
```

List every recipient in the `To` header and pass one `--mail-rcpt` flag per address.

```sh
cat > mail.txt <<EOF
From: Newsletter Team <newsletter@yourdomain.com>
To: user1@example.com, user2@example.com
Subject: Monthly Newsletter

This month's updates
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "newsletter@yourdomain.com" \
  --mail-rcpt "user1@example.com" \
  --mail-rcpt "user2@example.com" \
  --upload-file mail.txt
```

## CC and BCC

```ts
const response = await env.EMAIL.send({
	to: "customer@example.com",
	cc: ["manager@example.com"],
	bcc: ["archive@example.com"],
	from: "orders@yourdomain.com",
	replyTo: "support@yourdomain.com",
	subject: "Order Confirmation #12345",
	html: "<h1>Your order is confirmed</h1>",
	text: "Your order is confirmed",
});
```

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "customer@example.com",
    "cc": ["manager@example.com"],
    "bcc": ["archive@example.com"],
    "from": "orders@yourdomain.com",
    "reply_to": "support@yourdomain.com",
    "subject": "Order Confirmation #12345",
    "html": "<h1>Your order is confirmed</h1>",
    "text": "Your order is confirmed"
  }'
```

Add a `Cc` header for CC recipients. BCC recipients are added as `--mail-rcpt` flags but omitted from the headers.

```sh
cat > mail.txt <<EOF
From: orders@yourdomain.com
To: customer@example.com
Cc: manager@example.com
Reply-To: support@yourdomain.com
Subject: Order Confirmation #12345

Your order is confirmed
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "orders@yourdomain.com" \
  --mail-rcpt "customer@example.com" \
  --mail-rcpt "manager@example.com" \
  --mail-rcpt "archive@example.com" \
  --upload-file mail.txt
```

## Named recipients

Provide a display name alongside the address for the sender and recipients.

```ts
const response = await env.EMAIL.send({
	to: { email: "jane@example.com", name: "Jane Doe" },
	from: { email: "support@yourdomain.com", name: "Support Team" },
	subject: "Welcome!",
	html: "<h1>Thanks for joining!</h1>",
	text: "Thanks for joining!",
});
```

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": { "address": "jane@example.com", "name": "Jane Doe" },
    "from": { "address": "support@yourdomain.com", "name": "Support Team" },
    "subject": "Welcome!",
    "html": "<h1>Thanks for joining!</h1>",
    "text": "Thanks for joining!"
  }'
```

Use the `Display Name <address>` form in the `From` and `To` headers.

```sh
cat > mail.txt <<EOF
From: Support Team <support@yourdomain.com>
To: Jane Doe <jane@example.com>
Subject: Welcome!

Thanks for joining!
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "support@yourdomain.com" \
  --mail-rcpt "jane@example.com" \
  --upload-file mail.txt
```

## Mixed plain and named recipients

Combine plain addresses and named addresses in the same `to` field.

```ts
const response = await env.EMAIL.send({
	to: ["plain@example.com", { email: "jane@example.com", name: "Jane Doe" }],
	from: "support@yourdomain.com",
	subject: "Team update",
	html: "<h1>Monthly update</h1>",
	text: "Monthly update",
});
```

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": [
      "plain@example.com",
      { "address": "jane@example.com", "name": "Jane Doe" }
    ],
    "from": "support@yourdomain.com",
    "subject": "Team update",
    "html": "<h1>Monthly update</h1>",
    "text": "Monthly update"
  }'
```

```sh
cat > mail.txt <<EOF
From: support@yourdomain.com
To: plain@example.com, Jane Doe <jane@example.com>
Subject: Team update

Monthly update
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "support@yourdomain.com" \
  --mail-rcpt "plain@example.com" \
  --mail-rcpt "jane@example.com" \
  --upload-file mail.txt
```

## Next steps

- [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — full `send()` reference.
- [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) — send over HTTPS.
- [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) — send from any SMTP-capable client.
- [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/) — send PDFs, inline images, and uploads.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/recipients/#page","headline":"Specify recipients","description":"Send to multiple recipients, CC and BCC, and named addresses using the Workers binding, REST API, or SMTP.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/recipients/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/recipients/og.png?v=350ae4cc036927b2","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Handle user registration with automated welcome emails and email verification using secure tokens.
title: User signup flow
image: https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/og.png?v=f5044c8c07ca08ef
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# User signup flow

Send welcome and verification emails during user signup with secure token-based email verification.

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This example demonstrates a complete user registration system with welcome emails and email verification.

Configure the email binding and KV namespaces in your Wrangler configuration file:

```jsonc
{
	"send_email": [{ "name": "EMAIL" }],
	"kv_namespaces": [
		{ "binding": "USERS", "id": "<USERS_KV_NAMESPACE_ID>" },
		{
			"binding": "VERIFICATION_TOKENS",
			"id": "<VERIFICATION_TOKENS_KV_NAMESPACE_ID>",
		},
	],
	"vars": {
		"DOMAIN": "yourdomain.com",
		"COMPANY_NAME": "Acme Inc.",
	},
}
```

```toml
[[send_email]]
name = "EMAIL"

[[kv_namespaces]]
binding = "USERS"
id = "<USERS_KV_NAMESPACE_ID>"

[[kv_namespaces]]
binding = "VERIFICATION_TOKENS"
id = "<VERIFICATION_TOKENS_KV_NAMESPACE_ID>"

[vars]
DOMAIN = "yourdomain.com"
COMPANY_NAME = "Acme Inc."
```

The Worker exposes two routes: `POST /signup` creates a user, stores it in KV, and sends both a welcome email and a verification email containing a tokenized link; `GET /verify` validates that token and marks the user verified. The following code implements both handlers.

```typescript
interface Env {
	EMAIL: SendEmail;
	USERS: KVNamespace;
	VERIFICATION_TOKENS: KVNamespace;
	DOMAIN: string;
	COMPANY_NAME: string;
}

interface User {
	id: string;
	email: string;
	firstName: string;
	verified: boolean;
	createdAt: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const url = new URL(request.url);

		if (url.pathname === "/signup" && request.method === "POST") {
			return handleSignup(request, env);
		}

		if (url.pathname === "/verify" && request.method === "GET") {
			return handleVerification(request, env);
		}

		return new Response("Not Found", { status: 404 });
	},
};

async function handleSignup(request: Request, env: Env): Promise<Response> {
	const { email, firstName } = await request.json();

	// Validate and check for existing user
	if (!email || !firstName || !isValidEmail(email)) {
		return new Response(JSON.stringify({ error: "Invalid input" }), {
			status: 400,
		});
	}

	if (await env.USERS.get(email)) {
		return new Response(JSON.stringify({ error: "User already exists" }), {
			status: 409,
		});
	}

	// Create user
	const userId = crypto.randomUUID();
	const user: User = {
		id: userId,
		email,
		firstName,
		verified: false,
		createdAt: new Date().toISOString(),
	};

	await env.USERS.put(email, JSON.stringify(user));

	// Generate verification token (expires in 1 hour)
	const verificationToken = crypto.randomUUID();
	await env.VERIFICATION_TOKENS.put(verificationToken, email, {
		expirationTtl: 3600,
	});

	// Send welcome email
	await env.EMAIL.send({
		to: email,
		from: `noreply@${env.DOMAIN}`,
		subject: `Welcome to ${env.COMPANY_NAME}!`,
		html: `
      <h1>Welcome ${firstName}!</h1>
      <p>Thanks for joining ${env.COMPANY_NAME}. Please verify your email to get started.</p>
    `,
	});

	// Send verification email
	const verificationUrl = `https://${env.DOMAIN}/verify?token=${verificationToken}`;
	await env.EMAIL.send({
		to: email,
		from: `noreply@${env.DOMAIN}`,
		subject: "Please verify your email address",
		html: `
      <h1>Verify Your Email</h1>
      <p>Hi ${firstName}! Click the link below to verify your email:</p>
      <a href="https://developers.cloudflare.com/$%7BverificationUrl%7D" style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;">Verify Email</a>
      <p>This link expires in 1 hour.</p>
    `,
	});

	return new Response(
		JSON.stringify({
			success: true,
			message: "Account created. Please check your email.",
			userId,
		}),
		{ status: 201 },
	);
}

async function handleVerification(
	request: Request,
	env: Env,
): Promise<Response> {
	const url = new URL(request.url);
	const token = url.searchParams.get("token");

	if (!token) {
		return new Response("Missing token", { status: 400 });
	}

	// Verify token
	const userEmail = await env.VERIFICATION_TOKENS.get(token);
	if (!userEmail) {
		return new Response("Invalid or expired token", { status: 400 });
	}

	// Get and update user
	const userData = await env.USERS.get(userEmail);
	if (!userData) {
		return new Response("User not found", { status: 404 });
	}

	const user: User = JSON.parse(userData);
	user.verified = true;

	await env.USERS.put(user.email, JSON.stringify(user));
	await env.VERIFICATION_TOKENS.delete(token);

	return new Response(
		`
    <html>
      <body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
        <h1>Email Verified!</h1>
        <p>Welcome ${user.firstName}! Your account is now active.</p>
        <a href="https://developers.cloudflare.com/" style="background: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;">Continue</a>
      </body>
    </html>
  `,
		{
			headers: { "Content-Type": "text/html" },
		},
	);
}

function isValidEmail(email: string): boolean {
	return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
```

## Next steps

- [Email a login link](https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/) — generate and email a link using a similar token pattern.
- [Send method](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) — full reference for the `send()` method.
- [Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/) — keep verification emails out of spam folders.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/#page","headline":"User signup flow","description":"Handle user registration with automated welcome emails and email verification using secure tokens.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/og.png?v=f5044c8c07ca08ef","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Send transactional emails through Cloudflare Email Service authenticated SMTP from curl, Node.js, Python, or PHP.
title: Send email over SMTP
image: https://developers.cloudflare.com/email-service/examples/email-sending/smtp/og.png?v=be928fd6861ebc8d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Send email over SMTP

Send transactional emails over Cloudflare Email Service SMTP using curl, Nodemailer, Python smtplib, or PHPMailer.

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/smtp/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Send transactional emails over Cloudflare Email Service [authenticated SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) (`smtp.mx.cloudflare.net:465`) from any SMTP-capable language or client.

## Prerequisites

- A domain onboarded for [Email Sending](https://developers.cloudflare.com/email-service/configuration/domains/).
- A [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the **Email Sending: Edit** permission. Set it as `CF_API_TOKEN` in your environment. The token is used as the SMTP password; the username is the literal string `api_token`.

### Send an email

```sh
cat > mail.txt <<EOF
From: welcome@yourdomain.com
To: recipient@example.com
Subject: Welcome to our service!

Thanks for signing up.
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:$CF_API_TOKEN" \
  --mail-from "welcome@yourdomain.com" \
  --mail-rcpt "recipient@example.com" \
  --upload-file mail.txt
```

The sender domain must be onboarded for Email Sending on the account that owns the API token.

Install [Nodemailer ↗︎](https://nodemailer.com/) with `npm install nodemailer`.

### Send an email

```js
import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
	host: "smtp.mx.cloudflare.net",
	port: 465,
	secure: true, // implicit TLS
	auth: {
		user: "api_token",
		pass: process.env.CF_API_TOKEN,
	},
});

const info = await transporter.sendMail({
	from: '"Acme" <welcome@yourdomain.com>',
	to: "user@example.com",
	subject: "Welcome to Acme",
	text: "Thanks for signing up.",
	html: "<h1>Welcome to Acme</h1><p>Thanks for signing up.</p>",
});

console.log("Message sent:", info.messageId);
```

### Send with an attachment

```js
const info = await transporter.sendMail({
	from: '"Acme Billing" <billing@yourdomain.com>',
	to: "customer@example.com",
	subject: "Your invoice",
	text: "Please find your invoice attached.",
	attachments: [
		{
			filename: "invoice-2026-04.pdf",
			path: "./invoices/invoice-2026-04.pdf",
			contentType: "application/pdf",
		},
	],
});
```

Total message size (including base64-encoded attachments) must not exceed 5 MiB. See [Limits](https://developers.cloudflare.com/email-service/platform/limits/).

### Error handling

Nodemailer rejects the promise with an `Error` whose `.responseCode` reflects the SMTP reply code. See [SMTP response codes](https://developers.cloudflare.com/email-service/api/send-emails/smtp/#response-codes) and [Troubleshooting](https://developers.cloudflare.com/email-service/api/send-emails/smtp/#troubleshooting).

```js
try {
	await transporter.sendMail({
		/* ... */
	});
} catch (err) {
	console.error(err.responseCode, err.message);
}
```

Uses the standard-library [`smtplib` ↗︎](https://docs.python.org/3/library/smtplib.html) (Python 3.8 or later).

### Send an email

```python
import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "Acme <welcome@yourdomain.com>"
msg["To"] = "user@example.com"
msg["Subject"] = "Welcome to Acme"
msg.set_content("Thanks for signing up.")
msg.add_alternative(
    "<h1>Welcome to Acme</h1><p>Thanks for signing up.</p>",
    subtype="html",
)

with smtplib.SMTP_SSL("smtp.mx.cloudflare.net", 465) as s:
    s.login("api_token", os.environ["CF_API_TOKEN"])
    s.send_message(msg)
```

`smtplib.SMTP_SSL` opens an implicit-TLS connection on port `465`, which is what Cloudflare's SMTP endpoint requires. Do not use `smtplib.SMTP` with `starttls()`; `STARTTLS` is not supported.

### Send to multiple recipients

```python
msg["To"] = ", ".join([
    "user1@example.com",
    "user2@example.com",
    "user3@example.com",
])
```

A single SMTP session can deliver to up to 50 `RCPT TO` addresses. See [Limits](https://developers.cloudflare.com/email-service/platform/limits/).

### Send with an attachment

```python
from pathlib import Path

pdf = Path("invoice-2026-04.pdf").read_bytes()
msg.add_attachment(
    pdf,
    maintype="application",
    subtype="pdf",
    filename="invoice-2026-04.pdf",
)
```

### Error handling

`smtplib` raises subclasses of `smtplib.SMTPException` with the SMTP reply code attached. See [SMTP response codes](https://developers.cloudflare.com/email-service/api/send-emails/smtp/#response-codes) and [Troubleshooting](https://developers.cloudflare.com/email-service/api/send-emails/smtp/#troubleshooting).

```python
try:
    with smtplib.SMTP_SSL("smtp.mx.cloudflare.net", 465) as s:
        s.login("api_token", os.environ["CF_API_TOKEN"])
        s.send_message(msg)
except smtplib.SMTPAuthenticationError as e:
    print(f"Auth failed: {e.smtp_code} {e.smtp_error!r}")
except smtplib.SMTPResponseException as e:
    print(f"SMTP error: {e.smtp_code} {e.smtp_error!r}")
```

### Send an email

```php
<?php
use PHPMailer\PHPMailer\PHPMailer;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host       = 'smtp.mx.cloudflare.net';
$mail->Port       = 465;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->SMTPAuth   = true;
$mail->Username   = 'api_token';
$mail->Password   = getenv('CF_API_TOKEN');

$mail->setFrom('welcome@yourdomain.com', 'Acme');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'Welcome to our service!';
$mail->Body    = 'Thanks for signing up.';
$mail->send();
```

## Next steps

- [SMTP reference](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) — connection details, authentication, response codes, and troubleshooting.
- [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/) — multiple recipients, CC and BCC, and named addresses.
- [Limits](https://developers.cloudflare.com/email-service/platform/limits/) — account, message, and session limits.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/smtp/#page","headline":"Send email over SMTP","description":"Send transactional emails through Cloudflare Email Service authenticated SMTP from curl, Node.js, Python, or PHP.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/smtp/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/smtp/og.png?v=be928fd6861ebc8d","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Remove recipients after hard bounces and spam complaints.
title: Sync recipient records
image: https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/og.png?v=d551728e3df5df76
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Sync recipient records

Synchronize application recipient records with Email Sending lifecycle events.

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Use [Email Sending event subscriptions](https://developers.cloudflare.com/email-service/reference/event-subscriptions/) to update application records after delivery problems. This example uses [Cloudflare Queues](https://developers.cloudflare.com/queues/) and [Workers KV](https://developers.cloudflare.com/kv/) to remove recipients from transactional notifications.

Suppression lists

Email Sending automatically [suppresses](https://developers.cloudflare.com/email-service/concepts/suppressions/) hard bounces and spam complaints. This example also prevents your application from selecting those recipients.

Workers KV is eventually consistent. Deletions can take 60 seconds or longer to appear in other locations.

## Prepare the resources

Before you begin:

- Enable an [Email Sending domain](https://developers.cloudflare.com/email-service/configuration/domains/).
- Create a [Worker project](https://developers.cloudflare.com/workers/get-started/guide/).
- Create a [Workers KV namespace](https://developers.cloudflare.com/kv/get-started/#2-create-a-kv-namespace).

Store each eligible recipient address as a key in KV. The value can contain notification preferences or related metadata.

## Review the event flow

1. Email Sending publishes bounce and complaint events.
2. A queue delivers those events to a Worker.
3. The Worker removes ineligible recipient records from KV.

## Choose removal events

Remove records for every `message.complained` event. These events indicate that a recipient reported the message as spam.

Remove bounced records only when `payload.bounce.type` is `"hard"`. Temporary failures produce `message.deferred` events while retries remain. Exhausted temporary retries can produce `message.bounced` events with a `"soft"` bounce type.

For payload details, refer to [Available Email Sending events](https://developers.cloudflare.com/email-service/reference/event-subscriptions/#available-email-sending-events).

## Create the queue and subscription

Create a queue and subscribe it to your sending domain:

1. In the Cloudflare dashboard, go to the **Queues** page. Create a queue named `email-events`. [Go to **Queues** ↗](https://dash.cloudflare.com/?to=/:account/workers/queues)
2. Select `email-events`, then select **Subscriptions** > **Subscribe to events**.
3. Enter a subscription name and select **Email Sending** as the source.
4. Select your sending domain and the `message.bounced` and `message.complained` events.
5. Select **Subscribe**.

## Configure the Worker

Bind the KV namespace and register the Worker as the queue consumer:

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "recipient-record-sync",
  "main": "src/index.ts",
  "upload_source_maps": true,
  // Set this to today's date
  "compatibility_date": "2026-10-18",
  "kv_namespaces": [
    {
      "binding": "RECIPIENTS",
      "id": "<RECIPIENTS_KV_NAMESPACE_ID>"
    }
  ],
  "queues": {
    "consumers": [
      {
        "queue": "email-events",
        "max_batch_size": 10,
        "max_retries": 3,
        "dead_letter_queue": "email-events-dlq"
      }
    ]
  }
}
```

```toml
name = "recipient-record-sync"
main = "src/index.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-18"

[[kv_namespaces]]
binding = "RECIPIENTS"
id = "<RECIPIENTS_KV_NAMESPACE_ID>"

[[queues.consumers]]
queue = "email-events"
max_batch_size = 10
max_retries = 3
dead_letter_queue = "email-events-dlq"
```

The configuration creates `email-events-dlq` during deployment. Queues moves events there after three retries.

## Add the queue consumer

The [`queue()` handler](https://developers.cloudflare.com/queues/configuration/javascript-apis/#consumer) processes each event independently. It deletes applicable recipient records and retries failed KV operations.

*src/index.jsjs*

```js
export default {
	async queue(batch, env) {
		for (const message of batch.messages) {
			try {
				const event = message.body;

				if (shouldRemove(event)) {
					await removeRecipient(env, event);
				}

				message.ack();
			} catch (error) {
				console.error("Failed to process Email Sending event", {
					eventId: message.body.payload.eventId,
					error,
				});
				message.retry();
			}
		}
	},
};

function shouldRemove(event) {
	if (event.type === "cf.email.sending.message.complained") {
		return true;
	}

	return (
		event.type === "cf.email.sending.message.bounced" &&
		event.payload.bounce?.type === "hard"
	);
}

async function removeRecipient(env, event) {
	await env.RECIPIENTS.delete(event.payload.recipient);
	console.log("Removed recipient record", {
		eventId: event.payload.eventId,
		reason: event.type,
	});
}
```

*src/index.tsts*

```ts
interface Env {
	RECIPIENTS: KVNamespace;
}

interface EmailSendingEvent {
	type:
		| "cf.email.sending.message.bounced"
		| "cf.email.sending.message.complained";
	payload: {
		eventId: string;
		recipient: string;
		bounce?: {
			type: "hard" | "soft";
		};
	};
}

export default {
	async queue(batch, env): Promise<void> {
		for (const message of batch.messages) {
			try {
				const event = message.body;

				if (shouldRemove(event)) {
					await removeRecipient(env, event);
				}

				message.ack();
			} catch (error) {
				console.error("Failed to process Email Sending event", {
					eventId: message.body.payload.eventId,
					error,
				});
				message.retry();
			}
		}
	},
} satisfies ExportedHandler<Env, EmailSendingEvent>;

function shouldRemove(event: EmailSendingEvent): boolean {
	if (event.type === "cf.email.sending.message.complained") {
		return true;
	}

	return (
		event.type === "cf.email.sending.message.bounced" &&
		event.payload.bounce?.type === "hard"
	);
}

async function removeRecipient(
	env: Env,
	event: EmailSendingEvent,
): Promise<void> {
	await env.RECIPIENTS.delete(event.payload.recipient);
	console.log("Removed recipient record", {
		eventId: event.payload.eventId,
		reason: event.type,
	});
}
```

Deleting a missing KV key succeeds. This makes repeated event delivery safe.

The handler acknowledges each successful message. Failed operations move to the [dead letter queue](https://developers.cloudflare.com/queues/configuration/dead-letter-queues/) after three retries.

## Deploy the Worker

Deploy the Worker and its queue consumer configuration:

npmyarnpnpm

```
npx wrangler deploy
```

```
yarn wrangler deploy
```

```
pnpm wrangler deploy
```

Monitor the dead letter queue for failed events. Reprocess them after fixing the underlying error.

## Next steps

- [Event subscriptions](https://developers.cloudflare.com/email-service/reference/event-subscriptions/) — review event schemas.
- [Suppression lists](https://developers.cloudflare.com/email-service/concepts/suppressions/) — understand automatic suppressions.
- [Queues retries](https://developers.cloudflare.com/queues/configuration/batching-retries/) — control message retries.
- [Workers KV consistency](https://developers.cloudflare.com/kv/concepts/how-kv-works/#consistency) — account for propagation delays.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/#page","headline":"Sync recipient records","description":"Remove recipients after hard bounces and spam complaints.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/examples/email-sending/sync-recipient-records/og.png?v=d551728e3df5df76","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Forward incoming emails to existing mailboxes or process them with Workers using Email Service.
title: Route emails
image: https://developers.cloudflare.com/email-service/get-started/route-emails/og.png?v=8ea964ad5bdc8e19
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Route emails

Set up email routing to forward incoming emails to existing mailboxes or process them with Workers.

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/get-started/route-emails/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Route incoming emails sent to your domain to existing mailboxes, Workers for processing, or other destinations.

Note

You must be using Cloudflare DNS to use Email Service.

## Set up your domain

Before using Email Routing, configure your domain.

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select **Onboard Domain**.
3. Choose a domain from your Cloudflare account. Optionally review the DNS records that Cloudflare will add to your root domain:
   - MX records to route incoming emails to Cloudflare.
   - TXT record for SPF to authorize email routing.
   - TXT record for DKIM to provide authentication for routed emails.
4. Select **Done**.

Note

DNS changes can take up to 24 hours to propagate globally, but usually complete within 5-15 minutes for domains using Cloudflare DNS.

Once your domain is onboarded, you can start routing emails.

## Route your first email

You can route your first email by setting up routing rules in the dashboard, or by processing emails with Workers.

The simplest way to route emails is forwarding them to existing email addresses.

### Add a destination address

Before you can create a routing rule, add and verify the destination address that will receive the forwarded emails. Destination addresses are managed at the account level and can be reused across domains.

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing** > **Destination Addresses**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Under **Destination addresses**, enter the email address you want to use as a destination in the inline form and submit it.
3. Open the verification email Cloudflare sends to that address and select **Verify email address**.

For full details, refer to [Add a destination address](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#add-a-destination-address).

### Create a routing rule

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select the domain you want to create an email address for.
3. Select the **Routing Rules** tab.
4. Select **Create routing rule**.
5. Configure your first rule (for instance, forwarding emails to `support@yourdomain.com` to your personal email address):
   - **Email pattern**: Enter the local part of the email (for example, `support` for `support@yourdomain.com`), and select your domain
   - **Action**: Send to an email
   - **Destination**: Your personal email address (for example, `your-email@gmail.com`)
6. Select **Save**.

### Test your routing rule

Verify that your routing rule is working:

1. Send an email from another email account to your newly created address (for example, `support@yourdomain.com`). Send from an account that is different from the destination address. Some providers discard messages that appear to come from the same account they are being delivered to.
2. Check the destination inbox for the forwarded email.
3. If you do not see the email right away, check your spam folder.

Use Workers to process emails with custom logic before forwarding or responding.

### Create an email processing Worker

1. Create a new Worker project:

   ```sh
   npm create cloudflare@latest email-processor
   ```

   When prompted, select **"Hello World" Worker** as the template. Then navigate to the project directory:

   ```sh
   cd email-processor
   ```

2. Install the required package for creating email replies:

   ```sh
   npm install mimetext
   ```

3. Add the `nodejs_compat` compatibility flag to your Wrangler configuration file. This is required for the `mimetext` package:

   ```jsonc
   {
   	"compatibility_flags": ["nodejs_compat"],
   }
   ```

   ```toml
   compatibility_flags = [ "nodejs_compat" ]
   ```

4. Create your email handler in `src/index.ts`:

   ```ts
   import { EmailMessage } from "cloudflare:email";
   import { createMimeMessage } from "mimetext";

   // ============================================
   // Configuration - Update these values
   // ============================================
   const YOUR_DOMAIN = "yourdomain.com"; // Replace with your verified domain
   const FORWARD_TO_EMAIL = "your-team@example.com"; // Replace with where you want emails forwarded

   export default {
   	async email(message, env, ctx): Promise<void> {
   		const sender = message.from;
   		const recipient = message.to;
   		const subject = message.headers.get("subject") || "";

   		console.log(
   			`Processing email from ${sender} to ${recipient} with subject ${subject}`,
   		);

   		// Route based on recipient
   		if (recipient.includes("support@")) {
   			// Send auto-reply
   			const msg = createMimeMessage();
   			const messageId = message.headers.get("Message-ID");
   			if (messageId) {
   				msg.setHeader("In-Reply-To", messageId);
   				msg.setHeader("References", messageId);
   			}
   			msg.setSender({
   				name: "Support Team",
   				addr: `support@${YOUR_DOMAIN}`,
   			});
   			msg.setRecipient(message.from);
   			msg.setSubject(`Re: ${subject}`);

   			// Add plain text version
   			msg.addMessage({
   				contentType: "text/plain",
   				data: "Thank you for contacting support. Your ticket number is 123.\n\nA member of our support team will get back to you shortly.",
   			});

   			// Add HTML version
   			msg.addMessage({
   				contentType: "text/html",
   				data: "<p>Thank you for contacting support. Your ticket number is <strong>123</strong>.</p><p>A member of our support team will get back to you shortly.</p>",
   			});

   			const replyMessage = new EmailMessage(
   				`support@${YOUR_DOMAIN}`,
   				message.from,
   				msg.asRaw(),
   			);

   			await message.reply(replyMessage);

   			// Forward to support team
   			await message.forward(FORWARD_TO_EMAIL);
   		} else {
   			// Default: forward to admin
   			await message.forward(FORWARD_TO_EMAIL);
   		}
   	},
   } satisfies ExportedHandler<Env>;
   ```

   Update configuration

   Before deploying, update the constants at the top of the file:
   - `YOUR_DOMAIN`: Your verified domain from the Cloudflare dashboard
   - `FORWARD_TO_EMAIL`: The email address where you want to receive forwarded emails
5. Deploy your Worker:

   ```sh
   npm run deploy
   ```

### Configure routing to Worker

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Routing**. [Go to **Email Routing** ↗](https://dash.cloudflare.com/?to=/:account/email-service/routing)
2. Select the domain you want to configure routing for.
3. Select the **Routing Rules** tab.
4. Select **Create routing rule**.
5. Configure Worker routing:
   - **Email pattern**: Enter the local part of the email (for example, `support` for `support@yourdomain.com`), and select your domain
   - **Action**: Send to a Worker
   - **Worker**: Select your `email-processor` Worker
6. Select **Save**.

### Test your email routing

After configuring the routing rule, test that it works:

1. Send an email from your personal email account to the address you configured (for example, `support@yourdomain.com`).
2. Check your `FORWARD_TO_EMAIL` inbox for the forwarded email.
3. If the recipient email was `support@`, you should also receive an auto-reply at your personal email address.

## Next steps

Now that you can route emails, explore advanced features:

- **[Send outbound emails](https://developers.cloudflare.com/email-service/get-started/send-emails/)** - Send emails from your applications
- **[API reference](https://developers.cloudflare.com/email-service/api/route-emails/)** - Complete routing API documentation
- **[Examples](https://developers.cloudflare.com/email-service/examples/)** - Real-world routing patterns

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/get-started/route-emails/#page","headline":"Route emails","description":"Forward incoming emails to existing mailboxes or process them with Workers using Email Service.","url":"https://developers.cloudflare.com/email-service/get-started/route-emails/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/get-started/route-emails/og.png?v=8ea964ad5bdc8e19","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Send your first email using the Cloudflare Email Service Workers binding, REST API, or SMTP.
title: Send emails
image: https://developers.cloudflare.com/email-service/get-started/send-emails/og.png?v=18bd192866585844
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Send emails

Send your first email using the Workers binding, the REST API, or SMTP.

Last updated Jun 25, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/get-started/send-emails/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Send emails from your applications using Cloudflare Email Service. You can use the **Workers binding** for applications built on Cloudflare Workers, the **REST API** from any platform, or **SMTP** from any SMTP-capable application or mail client.

Note

You must be using Cloudflare DNS to use Email Service.

## Set up your domain

Before using Email Sending, configure your domain.

1. In the Cloudflare dashboard, go to **Compute** > **Email Service** > **Email Sending**. [Go to **Email Sending** ↗](https://dash.cloudflare.com/?to=/:account/email-service/sending)
2. Select **Onboard Domain**.
3. Choose a domain from your Cloudflare account. Optionally review the DNS records that Cloudflare will add to the `cf-bounce` subdomain of your domain:
   - MX records to route bounce emails to Cloudflare.
   - TXT record for SPF to authorize sending emails.
   - TXT record for DKIM to provide authentication for emails sent from your domain.
   - TXT record for DMARC on `_dmarc.yourdomain.com`.
4. Select **Done**.

Note

DNS changes can take up to 24 hours to propagate globally, but usually complete within 5-15 minutes for domains using Cloudflare DNS.

Once your domain is onboarded, you can start sending emails.

## Send your first email

You can send your first email using the Workers binding, the REST API, or SMTP.

If you are building on Cloudflare Workers, you can use the Workers binding for native email sending. Start by creating a new Worker project.

1. Create a new Worker project:npmyarnpnpm

   ```
   npm create cloudflare@latest -- email-service-tutorial
   ```

   ```
   yarn create cloudflare email-service-tutorial
   ```

   ```
   pnpm create cloudflare@latest email-service-tutorial
   ```

   When prompted, select **"Hello World" Worker** as the template.
2. Add the email binding to your Wrangler configuration file:

   ```jsonc
   {
   	"send_email": [
   		{
   			"name": "EMAIL",
   			"remote": true,
   		},
   	],
   }
   ```

   ```toml
   [[send_email]]
   name = "EMAIL"
   remote = true
   ```

3. Create your Worker code in `src/index.ts`:

   ```ts
   // Configuration - Update these values
   const YOUR_DOMAIN = "yourdomain.com"; // Replace with your verified domain
   const RECIPIENT_EMAIL = "recipient@example.com"; // Replace with your email to receive test emails

   export default {
   	async fetch(request: Request, env: Env): Promise<Response> {
   		// Send a welcome email
   		const response = await env.EMAIL.send({
   			to: RECIPIENT_EMAIL,
   			from: `welcome@${YOUR_DOMAIN}`,
   			subject: "Welcome to our service!",
   			html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
   			text: "Welcome! Thanks for signing up.",
   		});

   		return new Response(`Email sent: ${response.messageId}`);
   	},
   } satisfies ExportedHandler<Env>;
   ```

4. Use `npx wrangler dev` to develop your Worker project and send emails. This runs your code locally while connecting to Cloudflare Email Service (using [remote bindings](https://developers.cloudflare.com/workers/local-development/#remote-bindings)).

   ```sh
   npx wrangler dev
   # ⎔ Starting remote preview...
   # Total Upload: 24.96 KiB / gzip: 6.17 KiB
   # [wrangler:info] Ready on http://localhost:8787
   ```

5. Deploy your Worker:

   ```sh
   npm run deploy
   ```

After deploying, test that your Worker can send emails:

1. Visit your Worker URL in a browser (shown in the deploy output, for example: `https://email-service-tutorial.<your-subdomain>.workers.dev`).
2. You should see a response like `Email sent: <message-id>`.
3. Check the inbox for the email address you specified in `RECIPIENT_EMAIL`. If you do not see the email, check your spam folder.

Send an email with a single `curl` command. Replace `{account_id}` with your [Cloudflare account ID](https://developers.cloudflare.com/fundamentals/account/find-account-and-zone-ids/) and `<API_TOKEN>` with an [API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/).

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "recipient@example.com",
    "from": "welcome@yourdomain.com",
    "subject": "Welcome to our service!",
    "html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
    "text": "Welcome! Thanks for signing up."
  }'
```

A successful response includes the delivery status for each recipient:

```json
{
	"success": true,
	"errors": [],
	"messages": [],
	"result": {
		"delivered": ["recipient@example.com"],
		"permanent_bounces": [],
		"queued": []
	}
}
```

For more details, see the [REST API reference](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/).

Send an email with a single `curl` command. Replace `<API_TOKEN>` with a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) that has the **Email Sending: Edit** permission, and replace the `--mail-from` and `--mail-rcpt` addresses with your own.

```sh
cat > mail.txt <<EOF
From: welcome@yourdomain.com
To: recipient@example.com
Subject: Welcome to our service!

Thanks for signing up.
EOF

curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "welcome@yourdomain.com" \
  --mail-rcpt "recipient@example.com" \
  --upload-file mail.txt
```

The sender domain (`welcome@yourdomain.com`) must be onboarded for [Email Sending](https://developers.cloudflare.com/email-service/configuration/domains/) on the account that owns the API token.

For connection details, authentication, response codes, and language-specific examples, see the [SMTP reference](https://developers.cloudflare.com/email-service/api/send-emails/smtp/).

## Next steps

Now that you can send emails, explore advanced features:

- **[Route incoming emails](https://developers.cloudflare.com/email-service/get-started/route-emails/)** - Process emails sent to your domain
- **[API reference](https://developers.cloudflare.com/email-service/api/send-emails/)** - Complete API documentation
- **[Examples](https://developers.cloudflare.com/email-service/examples/)** - Real-world implementation patterns

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/get-started/send-emails/#page","headline":"Send emails","description":"Send your first email using the Cloudflare Email Service Workers binding, REST API, or SMTP.","url":"https://developers.cloudflare.com/email-service/get-started/send-emails/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/get-started/send-emails/og.png?v=18bd192866585844","dateModified":"2026-06-25","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Track Email Service configuration changes such as rule edits and address additions in Cloudflare audit logs.
title: Audit logs
image: https://developers.cloudflare.com/email-service/observability/audit-logs/og.png?v=ade317ef1db125b8
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Audit logs

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/observability/audit-logs/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email Service writes configuration changes to [Cloudflare audit logs](https://developers.cloudflare.com/fundamentals/account/account-security/review-audit-logs/). Use audit logs to track who changed what and when.

## Email Routing actions

The following Email Routing actions are recorded:

- Add, edit, or delete a routing rule.
- Add or delete a destination address.
- Change the status of a destination address (for example, from pending to verified).
- Update the catch-all rule.
- Enable, disable, or unlock the zone for Email Routing.

## Email Sending actions

The following Email Sending actions are recorded:

- Onboard or remove a sending domain or subdomain.
- Add, edit, or delete entries on the suppression list.
- Enable or disable Email Sending on a domain.

To review audit logs, refer to [Review audit logs](https://developers.cloudflare.com/fundamentals/account/account-security/review-audit-logs/).

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/audit-logs/#page","headline":"Audit logs","description":"Track Email Service configuration changes such as rule edits and address additions in Cloudflare audit logs.","url":"https://developers.cloudflare.com/email-service/observability/audit-logs/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/observability/audit-logs/og.png?v=ade317ef1db125b8","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Test Email Service routing Workers locally with simulated incoming emails.
title: Test email routing locally
image: https://developers.cloudflare.com/email-service/observability/local-development/routing/og.png?v=17d6ff7e7dfc8640
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Test email routing locally

Test email routing Workers locally using wrangler dev with simulated incoming emails

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/observability/local-development/routing/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Test email routing behavior locally using `wrangler dev` to simulate incoming emails and verify your routing logic before deploying.

## Prerequisites

1. Sign up for a [Cloudflare account ↗︎](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [`Node.js` ↗︎](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

<details>

<summary>

Node.js version manager

</summary>

Use <a href="https://viteplus.dev/guide/env">Vite+ (<code>vp env</code>) ↗︎</a> to install and manage Node.js versions.

For supported Node.js versions, refer to the <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/#install-wrangler">Wrangler system requirements</a>.

</details>

## Configuration

Configure your Wrangler file:

```jsonc
{
	"name": "email-routing-worker",
	// Set this to today's date
	"compatibility_date": "2026-10-18",
}
```

```toml
name = "email-routing-worker"
# Set this to today's date
compatibility_date = "2026-10-18"
```

## Basic routing worker

```javascript
import * as PostalMime from "postal-mime";

export default {
	async email(message, env, ctx) {
		// Parse the raw email message
		const parser = new PostalMime.default();
		const rawEmail = new Response(message.raw);
		const email = await parser.parse(await rawEmail.arrayBuffer());

		console.log("Received email:", {
			from: message.from,
			to: message.to,
			subject: email.subject,
			text: email.text,
			html: email.html,
		});

		// Route based on recipient
		if (message.to.includes("support@")) {
			await message.forward("support-team@example.com");
		} else {
			await message.forward("general@example.com");
		}
	},
};
```

## Send a test email

Start your development server:

```bash
npx wrangler dev
```

Send a test email using the local endpoint. The request body must be a raw email message in [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) format, and the message must include a `Message-ID` header:

```bash
curl --request POST 'http://localhost:8787/cdn-cgi/local/email' \
  --url-query 'from=sender@example.com' \
  --url-query 'to=recipient@example.com' \
  --data-raw 'Received: from smtp.example.com (127.0.0.1)
        by cloudflare-email.com (unknown) id 4fwwffRXOpyR
        for <recipient@example.com>; Tue, 27 Aug 2024 15:50:20 +0000
From: "John" <sender@example.com>
Reply-To: sender@example.com
To: recipient@example.com
Subject: Testing Email Workers Local Dev
Content-Type: text/html; charset="windows-1252"
X-Mailer: Curl
Date: Tue, 27 Aug 2024 08:49:44 -0700
Message-ID: <6114391943504294873000@ZSH-GHOSTTY>

Hi there'
```

The console shows the parsed email structure:

```json
{
	"headers": [
		{
			"key": "received",
			"value": "from smtp.example.com (127.0.0.1) by cloudflare-email.com (unknown) id 4fwwffRXOpyR for <recipient@example.com>; Tue, 27 Aug 2024 15:50:20 +0000"
		},
		{ "key": "from", "value": "\"John\" <sender@example.com>" },
		{ "key": "reply-to", "value": "sender@example.com" },
		{ "key": "to", "value": "recipient@example.com" },
		{ "key": "subject", "value": "Testing Email Workers Local Dev" },
		{ "key": "content-type", "value": "text/html; charset=\"windows-1252\"" },
		{ "key": "x-mailer", "value": "Curl" },
		{ "key": "date", "value": "Tue, 27 Aug 2024 08:49:44 -0700" },
		{
			"key": "message-id",
			"value": "<6114391943504294873000@ZSH-GHOSTTY>"
		}
	],
	"from": { "address": "sender@example.com", "name": "John" },
	"to": [{ "address": "recipient@example.com", "name": "" }],
	"replyTo": [{ "address": "sender@example.com", "name": "" }],
	"subject": "Testing Email Workers Local Dev",
	"messageId": "<6114391943504294873000@ZSH-GHOSTTY>",
	"date": "2024-08-27T15:49:44.000Z",
	"html": "Hi there\n",
	"attachments": []
}
```

## Next steps

- Deploy your routing worker: [Route emails get started](https://developers.cloudflare.com/email-service/get-started/route-emails/)
- Explore advanced patterns: [Email routing examples](https://developers.cloudflare.com/email-service/examples/email-routing/)

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/local-development/routing/#page","headline":"Test email routing locally","description":"Test Email Service routing Workers locally with simulated incoming emails.","url":"https://developers.cloudflare.com/email-service/observability/local-development/routing/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/observability/local-development/routing/og.png?v=17d6ff7e7dfc8640","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Test Email Service sending Workers locally with simulated delivery.
title: Test email sending locally
image: https://developers.cloudflare.com/email-service/observability/local-development/sending/og.png?v=76c071adeb4a172d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Test email sending locally

Test email sending Workers locally using wrangler dev with simulated email delivery

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/observability/local-development/sending/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Test email sending functionality locally using `wrangler dev` to simulate email delivery and verify your sending logic before deploying.

Note

If you are using the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) instead of Workers, you can test by sending requests directly with `curl` or any HTTP client without a local development server. The rest of this page covers the Workers local development flow.

## Prerequisites

1. Sign up for a [Cloudflare account ↗︎](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [`Node.js` ↗︎](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

<details>

<summary>

Node.js version manager

</summary>

Use <a href="https://viteplus.dev/guide/env">Vite+ (<code>vp env</code>) ↗︎</a> to install and manage Node.js versions.

For supported Node.js versions, refer to the <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/#install-wrangler">Wrangler system requirements</a>.

</details>

## Configuration

Configure your Wrangler file with the email binding:

```jsonc
{
	"name": "email-sending-worker",
	// Set this to today's date
	"compatibility_date": "2026-10-18",
	"send_email": [{ "name": "EMAIL" }],
}
```

```toml
name = "email-sending-worker"
# Set this to today's date
compatibility_date = "2026-10-18"

[[send_email]]
name = "EMAIL"
```

## Remote bindings (recommended)

Use [remote bindings](https://developers.cloudflare.com/workers/local-development/#remote-bindings) to develop with Email Service locally. With remote bindings, your Worker runs locally but sends real emails through Email Service.

By default, `wrangler dev` simulates the email binding locally. It logs emails to the console but does not send them.

Set `remote: true` on the email binding in your Wrangler configuration:

```jsonc
{
	"name": "email-sending-worker",
	// Set this to today's date
	"compatibility_date": "2026-10-18",
	"send_email": [
		{
			"name": "EMAIL",
			"remote": true,
		},
	],
}
```

```toml
name = "email-sending-worker"
# Set this to today's date
compatibility_date = "2026-10-18"

[[send_email]]
name = "EMAIL"
remote = true
```

Then run `wrangler dev` as usual. Calls to `env.EMAIL.send()` send actual emails through Email Service while your Worker code runs locally.

Caution

Remote bindings send real emails to real recipients. Use test email addresses to avoid sending unintended emails during development.

## Local simulation

When running `wrangler dev` without remote bindings, the email binding is simulated locally. Emails are not sent. Instead, the email content is logged to the console and saved to local files for inspection.

## Basic sending worker

```javascript
export default {
	async fetch(request, env, ctx) {
		if (request.method !== "POST") {
			return new Response("Method not allowed", { status: 405 });
		}

		try {
			const emailData = await request.json();

			console.log("Sending email:", {
				to: emailData.to,
				from: emailData.from,
				subject: emailData.subject,
			});

			const response = await env.EMAIL.send(emailData);

			return new Response(
				JSON.stringify({
					success: true,
					id: response.messageId,
				}),
				{
					headers: { "Content-Type": "application/json" },
				},
			);
		} catch (error) {
			return new Response(
				JSON.stringify({
					success: false,
					error: error.message,
				}),
				{
					status: 500,
					headers: { "Content-Type": "application/json" },
				},
			);
		}
	},
};
```

## Test locally

Start your development server:

```bash
npx wrangler dev
```

Send a test email:

```bash
curl -X POST http://localhost:8787/ \
  -H "Content-Type: application/json" \
  -d '{
    "to": "recipient@example.com",
    "from": "sender@yourdomain.com",
    "subject": "Test Email",
    "html": "<h1>Hello from Wrangler!</h1>",
    "text": "Hello from Wrangler!"
  }'
```

Wrangler shows output like:

```txt
[wrangler:info] send_email binding called with MessageBuilder:
From: sender@yourdomain.com
To: recipient@example.com
Subject: Test Email

Text: /tmp/miniflare-.../files/email-text/<message-id>.txt
```

The email content (text and HTML) is saved to local files that you can inspect to verify your email structure before deploying.

## Known limitations

### Binary attachments

Local development simulates the `send_email` binding locally, but `ArrayBuffer` values in attachment `content` cannot be serialized by the local simulator. If you pass an `ArrayBuffer` (for example, for image or PDF attachments), you receive an error like:

```txt
Cannot serialize value: [object ArrayBuffer]
```

**Workaround:** Use string content for text-based attachments during local development. To test binary attachments (images, PDFs), deploy your Worker with `npx wrangler deploy` and test against the deployed version.

This limitation only affects local development — `ArrayBuffer` content works correctly on deployed Workers.

## Next steps

- Deploy your sending worker: [Send emails get started](https://developers.cloudflare.com/email-service/get-started/send-emails/)
- Explore advanced patterns: [Email sending examples](https://developers.cloudflare.com/email-service/examples/email-sending/)

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/local-development/sending/#page","headline":"Test email sending locally","description":"Test Email Service sending Workers locally with simulated delivery.","url":"https://developers.cloudflare.com/email-service/observability/local-development/sending/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/observability/local-development/sending/og.png?v=76c071adeb4a172d","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Service activity statuses, authentication details, and message fields.
title: Activity log
image: https://developers.cloudflare.com/email-service/observability/logs/og.png?v=7322ea94b0a5a48d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Activity log

Email sending and routing statuses, authentication details, and message fields

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/observability/logs/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

The Email Service Activity log lists sending and routing events. Each event includes delivery and authentication details.

The Activity log allows you to sort through all email activities and check actions taken by Email Service. In the dashboard you can filter the Activity log to a time range between 30 minutes and 30 days, or specify a custom range.

The Activity log surfaces the same per-event data that is also available programmatically through the [`emailSendingAdaptive` and `emailRoutingAdaptive` datasets](https://developers.cloudflare.com/email-service/observability/metrics-analytics/) in the GraphQL Analytics API.

For Email Routing, you can expand an individual email in the Activity log to inspect authentication results ([SPF ↗︎](https://datatracker.ietf.org/doc/html/rfc7208), [DKIM ↗︎](https://datatracker.ietf.org/doc/html/rfc6376), and [DMARC ↗︎](https://datatracker.ietf.org/doc/html/rfc7489)).

## Email sending statuses

For outbound emails sent through Email Service:

- **Sent**: Email successfully accepted and queued for delivery.
- **Delivered**: Email successfully delivered to recipient's mail server.
- **Delivery failed**: Email bounced (hard or soft bounce). This corresponds to the `deliveryFailed` status in the [GraphQL Analytics API](https://developers.cloudflare.com/email-service/observability/metrics-analytics/).
- **Rejected**: Email was not sent because the recipient is on your account's [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/).
- **Failed**: Email failed to send due to configuration or authentication issues.

## Email routing statuses

For inbound emails processed through Email Routing:

- **Forwarded**: Email successfully forwarded to destination address.
- **Handled**: Email processed by a Worker handler.
- **Dropped**: Email dropped due to filtering rules or configuration.
- **Rejected**: Email rejected due to SPF, DKIM, or DMARC failures.
- **Delivery failed**: Email could not be delivered to the destination address.
- **Error**: Email could not be processed due to an internal error.

## Email details

Select any email in the Activity log to expand its details and view authentication and delivery information.

### Authentication status

Check the status of email authentication protocols:

- **SPF status**: Shows pass/fail for Sender Policy Framework validation.
- **DKIM status**: Shows pass/fail for DomainKeys Identified Mail signature verification.
- **DMARC status**: Shows pass/fail for Domain-based Message Authentication compliance.

### Delivery information

For sent emails, see delivery details:

- Recipient mail server response.
- Delivery attempts and timestamps.
- Bounce reason codes and categories.
- Final delivery status.

### Message preview

For sent emails, expand the email in the Activity log to open the **Preview** section and inspect the message as it was sent. The preview provides the following tabs:

- **HTML**: The rendered HTML body.
- **Text**: The plain text body.
- **Headers**: The message headers.
- **Attachments**: Files included with the message.
- **Raw**: The full raw [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) message source.

To make sent messages previewable, turn on [**Email preview**](https://developers.cloudflare.com/email-service/configuration/domains/#email-preview) for the sending domain. Previews cover messages sent while the setting is turned on and are retained for about seven days. New sending domains have **Email preview** turned on automatically.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/logs/#page","headline":"Activity log","description":"Email Service activity statuses, authentication details, and message fields.","url":"https://developers.cloudflare.com/email-service/observability/logs/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/observability/logs/og.png?v=7322ea94b0a5a48d","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Query Email Service sending metrics and delivery rates via the dashboard or GraphQL Analytics API.
title: Metrics and analytics
image: https://developers.cloudflare.com/email-service/observability/metrics-analytics/og.png?v=4e1595332f3d6f1f
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Metrics and analytics

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/observability/metrics-analytics/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email Service exposes analytics that allow you to inspect email sending performance and delivery rates across all your domains.

The metrics displayed in the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) charts are queried from Cloudflare's [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/). You can access the metrics [programmatically](#query-via-the-graphql-api) via GraphQL or HTTP client.

## Metrics

Email Service currently exposes the below metrics:

| Dataset | GraphQL Dataset Name | Description |
| --- | --- | --- |
| Sending (aggregated) | `emailSendingAdaptiveGroups` | Aggregated email sending counts grouped by dimensions such as status, date, sending domain, and authentication results. |
| Sending (events) | `emailSendingAdaptive` | Individual email sending events with full detail including sender, recipient, subject, message ID, and error information. |
| Routing (aggregated) | `emailRoutingAdaptiveGroups` | Aggregated email routing counts grouped by dimensions such as status, date, recipient domain, and authentication results. |
| Routing (events) | `emailRoutingAdaptive` | Individual email routing events with full detail including sender, recipient, subject, message ID, and processing decisions. |

Metrics can be queried (and are retained) for the past 31 days.

## View metrics in the dashboard

Per-domain analytics for Email Service are available in the Cloudflare dashboard. To view current and historical metrics:

1. Log in to the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/) and select your account.
2. Go to **Compute** > **Email Service** and select **Email Sending** or **Email Routing**.
3. Select an existing domain or view account-wide metrics.
4. Select the **Analytics** tab.

You can optionally select a time window to query. This defaults to the last 24 hours.

## Query via the GraphQL API

You can programmatically query analytics for your Email Service domains via the [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/). This API queries the same datasets as the Cloudflare dashboard, and supports GraphQL [introspection](https://developers.cloudflare.com/analytics/graphql-api/features/discovery/introspection/).

To get started using the [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/), follow the documentation to setup [Authentication for the GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/getting-started/authentication/). Your API token must include the **Analytics Read** permission.

These are **zone-level** datasets. To query them, provide your zone ID (not account ID) as the `zoneTag` filter. The GraphQL datasets for Email Service include:

- `emailSendingAdaptiveGroups` — aggregated email sending counts with groupable dimensions
- `emailSendingAdaptive` — individual email sending events
- `emailRoutingAdaptiveGroups` — aggregated email routing counts with groupable dimensions
- `emailRoutingAdaptive` — individual email routing events

### Email Sending dimensions

The `emailSendingAdaptiveGroups` dataset supports the following dimensions for grouping and filtering:

| Dimension | Type | Description |
| --- | --- | --- |
| `date` | Date | Day-level grouping |
| `datetime` | Time | Exact event timestamp |
| `datetimeMinute` | Time | Minute-level grouping |
| `datetimeFiveMinutes` | Time | 5-minute interval grouping |
| `datetimeFifteenMinutes` | Time | 15-minute interval grouping |
| `datetimeHour` | Time | Hour-level grouping |
| `status` | string | Delivery status (for example, `delivered`, `deliveryFailed`) |
| `eventType` | string | Origin of email (`incoming`, `forward`, `reply`, `newEmail`) |
| `sendingDomain` | string | The domain used to send the email |
| `envelopeTo` | string | Recipient envelope address |
| `errorCause` | string | Error cause for failed sends |
| `arc` | string | ARC authentication result |
| `dkim` | string | DKIM authentication result |
| `dmarc` | string | DMARC authentication result |
| `spf` | string | SPF authentication result |
| `isSpam` | uint8 | Whether the email was flagged as spam |
| `isNDR` | uint8 | Whether the email is a non-delivery report |
| `isLastEvent` | uint8 | Whether this is the last event for this email |

The `emailSendingAdaptive` dataset includes all of the above plus per-event fields: `from`, `to`, `subject`, `messageId`, `sessionId`, `errorDetail`.

### Email Routing dimensions

The `emailRoutingAdaptiveGroups` dataset supports the following dimensions for grouping and filtering:

| Dimension | Type | Description |
| --- | --- | --- |
| `date` | Date | Day-level grouping |
| `datetime` | Time | Exact event timestamp |
| `datetimeMinute` | Time | Minute-level grouping |
| `datetimeFiveMinutes` | Time | 5-minute interval grouping |
| `datetimeFifteenMinutes` | Time | 15-minute interval grouping |
| `datetimeHour` | Time | Hour-level grouping |
| `status` | string | Resulting outcome for the email |
| `eventType` | string | Origin of email (`incoming`, `forward`, `reply`, `newEmail`) |
| `action` | string | Action applied by the routing rule |
| `ruleMatched` | string | UUID of the routing rule matched by the email |
| `arc` | string | ARC authentication result |
| `dkim` | string | DKIM authentication result |
| `dmarc` | string | DMARC authentication result |
| `spf` | string | SPF authentication result |
| `isSpam` | uint8 | Whether the email was flagged as spam |
| `isNDR` | uint8 | Whether the email is a non-delivery report |
| `isLastEvent` | uint8 | Whether this is the last event for this email |

The `emailRoutingAdaptive` dataset includes all of the above plus per-event fields: `from`, `to`, `subject`, `messageId`, `sessionId`, `errorDetail`, `ruleMatched`.

### Examples

The following are common GraphQL queries that you can use to retrieve information about Email Service analytics. These queries use the variable `$zoneTag`, which should be set to your Cloudflare Zone ID. You can find this in the Cloudflare dashboard under your domain's **Overview** page.

```json
{
	"zoneTag": "<YOUR_ZONE_ID>",
	"start": "2024-07-15",
	"end": "2024-07-30"
}
```

#### Email sending operations

To query the count of emails for a given date range, grouped by `date` and `status` (for example, `delivered`, `deliveryFailed`):

```graphql
query EmailSendingByStatus($zoneTag: string!, $start: Date!, $end: Date!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailSendingAdaptiveGroups(
				filter: { date_geq: $start, date_leq: $end }
				limit: 10000
				orderBy: [date_DESC]
			) {
				count
				dimensions {
					date
					status
				}
			}
		}
	}
}
```

#### Delivery failure analysis

To investigate delivery failure causes for a specific date range, grouped by `errorCause` and `sendingDomain`:

```graphql
query EmailDeliveryFailures($zoneTag: string!, $start: Date!, $end: Date!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailSendingAdaptiveGroups(
				filter: { date_geq: $start, date_leq: $end, status: "deliveryFailed" }
				limit: 10000
				orderBy: [date_DESC]
			) {
				count
				dimensions {
					date
					errorCause
					sendingDomain
				}
			}
		}
	}
}
```

#### Hourly volume

To query email sending volume grouped by hour, useful for identifying traffic patterns:

```graphql
query EmailSendingHourlyVolume($zoneTag: string!, $start: Time!, $end: Time!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailSendingAdaptiveGroups(
				filter: { datetimeHour_geq: $start, datetimeHour_leq: $end }
				limit: 10000
				orderBy: [datetimeHour_ASC]
			) {
				count
				dimensions {
					datetimeHour
					status
				}
			}
		}
	}
}
```

#### Individual email events

To query individual email events for troubleshooting specific delivery issues. This uses the `emailSendingAdaptive` dataset and filters by `datetime` (Time type):

```graphql
query RecentEmailEvents($zoneTag: string!, $start: Time!, $end: Time!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailSendingAdaptive(
				filter: { datetime_geq: $start, datetime_leq: $end }
				limit: 50
				orderBy: [datetime_DESC]
			) {
				datetime
				from
				to
				subject
				status
				eventType
				sendingDomain
				messageId
				errorCause
				errorDetail
				dkim
				dmarc
				spf
				isSpam
			}
		}
	}
}
```

#### Email routing operations

To query the count of routed emails for a given date range, grouped by `date` and `status`:

```graphql
query EmailRoutingByStatus($zoneTag: string!, $start: Date!, $end: Date!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailRoutingAdaptiveGroups(
				filter: { date_geq: $start, date_leq: $end }
				limit: 10000
				orderBy: [date_DESC]
			) {
				count
				dimensions {
					date
					status
				}
			}
		}
	}
}
```

#### Routing rule activity

To see which routing rules are matching emails, grouped by `ruleMatched` and `action`:

```graphql
query EmailRoutingRuleActivity($zoneTag: string!, $start: Date!, $end: Date!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailRoutingAdaptiveGroups(
				filter: { date_geq: $start, date_leq: $end }
				limit: 10000
				orderBy: [date_DESC]
			) {
				count
				dimensions {
					date
					ruleMatched
					action
				}
			}
		}
	}
}
```

#### Individual routing events

To query individual routing events for troubleshooting:

```graphql
query RecentRoutingEvents($zoneTag: string!, $start: Time!, $end: Time!) {
	viewer {
		zones(filter: { zoneTag: $zoneTag }) {
			emailRoutingAdaptive(
				filter: { datetime_geq: $start, datetime_leq: $end }
				limit: 50
				orderBy: [datetime_DESC]
			) {
				datetime
				from
				to
				subject
				status
				action
				ruleMatched
				messageId
				errorDetail
				dkim
				dmarc
				spf
				isSpam
			}
		}
	}
}
```

Note

The `*AdaptiveGroups` datasets use `Date` type filters (`date_geq`, `date_leq`) for day-level filtering, or `Time` type filters (`datetimeHour_geq`, etc.) for finer granularity. The `*Adaptive` (events) datasets use `Time` type filters (`datetime_geq`, `datetime_leq`), for example `"2024-07-15T00:00:00Z"`.

## Next steps

- [Activity log](https://developers.cloudflare.com/email-service/observability/logs/) — view individual email activity in the dashboard.
- [Audit logs](https://developers.cloudflare.com/email-service/observability/audit-logs/) — track configuration changes.
- [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/) — full GraphQL API reference.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/metrics-analytics/#page","headline":"Metrics and analytics","description":"Query Email Service sending metrics and delivery rates via the dashboard or GraphQL Analytics API.","url":"https://developers.cloudflare.com/email-service/observability/metrics-analytics/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/observability/metrics-analytics/og.png?v=4e1595332f3d6f1f","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Track Cloudflare Email Service updates, features, and fixes.
title: Changelog
image: https://developers.cloudflare.com/email-service/platform/changelog/og.png?v=bd83792d4994d46d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Changelog

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/platform/changelog/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

[Subscribe to RSS](https://developers.cloudflare.com/changelog/rss/email-service.xml)

## 2026-09-25

  
**Suppress recipients for one sending domain**  

[Email Sending suppressions](https://developers.cloudflare.com/email-service/concepts/suppressions/) now have a **scope**:

- **`account`**: The suppression applies to every sending domain and subdomain in your account. This is the default.
- **`sending_domain`**: The suppression applies to one sending domain only. A suppression for `mail.myappexample.com` does not block mail from `myappexample.com`.

Most importantly, Email Sending now automatically creates bounce and complaint suppressions at the sending-domain level. This provides greater granularity by preventing an issue with one sending domain from suppressing the recipient across your entire account.

To add a suppression for one sending domain in the dashboard, go to **Email Sending** > **Suppressions** and select **Sending domain** in **Scope**. Imports can also set a scope for each row or a default scope.

In the API, pass `scope` when you create the suppression:

```bash
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/suppressions \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "user@example.net",
    "scope": { "type": "sending_domain", "value": "mail.myappexample.com" }
  }'
```

The [suppressions API](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/) returns `scope` on every suppression. To list the suppressions for one domain, use `scope_type=sending_domain&scope_value=mail.myappexample.com`. If you omit `scope`, the API creates an `account` suppression, so existing integrations continue to work.

Refer to [Suppression lists](https://developers.cloudflare.com/email-service/concepts/suppressions/#suppression-scope) and [Manage suppressions](https://developers.cloudflare.com/email-service/configuration/suppressions/) for details.

## 2026-09-04

  
**Manage Email Routing rules with Wrangler**  

You can now manage Email Routing rules that route emails to Workers from your Wrangler configuration. Add literal addresses or a catch-all address to the top-level `addresses` field:

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "invoice-handler",
  "main": "src/index.ts",
  // Set this to today's date
  "compatibility_date": "2026-10-18",
  "addresses": [
    "invoice@yourdomain.com"
  ]
}
```

```toml
name = "invoice-handler"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-10-18"
addresses = ["invoice@yourdomain.com"]
```

When you run `wrangler deploy`, Wrangler creates rules for new addresses, updates existing rules managed by the Worker, and removes managed rules that are no longer in the configuration. Wrangler shows the planned changes and asks for confirmation before applying potentially destructive changes.

Email Routing rules created by Wrangler also appear in the dashboard but with an icon that identifies them.

![Email Routing rule created by Wrangler in dashboard](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=2440,height=526,format=webp/_astro/wrangler-email-routing-rules-dash.BZQZlAJI.png)

Refer to [Configure rules with Wrangler](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#configure-rules-with-wrangler) for more information.

## 2026-07-17

  
**Preview sent emails in the Activity log**  

You can now preview the content of sent emails directly from the Email Service Activity log. Expand a sent email and open the new **Preview** section to inspect the message as it was sent, across tabs for the rendered **HTML** body, the **Text** body, the **Headers**, the **Attachments**, and the full **Raw** [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) source.

![The rendered HTML preview of a sent email in the Email Service Activity log](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=2864,height=1172,format=webp/_astro/email-message-preview.Bj6Lk8Y6.png)

Previously, the Activity log surfaced delivery and authentication metadata but not the message content, making rendering and content issues harder to debug. Message preview closes that gap.

To make messages previewable, turn on **Email preview** in your sending domain's settings. Previews cover messages sent while the setting is turned on and are retained for about seven days. Sending domains onboarded on or after 2026-07-02 have **Email preview** turned on automatically.

![The Email preview setting in a sending domain's settings](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=2256,height=264,format=webp/_astro/email-preview-setting.XEd1WIiO.png)

Refer to [Email logs](https://developers.cloudflare.com/email-service/observability/logs/#message-preview) for more information.

## 2026-07-15

  
**Subscribe to Email Sending events with Queues**  

You can now subscribe to **[Email Sending](https://developers.cloudflare.com/email-service/api/send-emails/) events** through [Queues event subscriptions](https://developers.cloudflare.com/queues/event-subscriptions/) and receive outbound transactional email lifecycle events on a queue. Each subscription is scoped to one sending domain — either the zone apex, such as `example.com`, or a verified sending subdomain, such as `send.example.com`.

Six event types are published: `message.delivered`, `message.deferred`, `message.bounced`, `message.failed`, `message.rejected`, and `message.complained`. Use them to track deliverability, react to bounces and complaints, and drive suppression or retry logic. Email Routing events are not published on this source.

Each event includes the message details, delivery status, and SMTP response:

```json
{
	"type": "cf.email.sending.message.delivered",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "example.com"
	},
	"payload": {
		"messageId": "0101018f7d0c4d9a-msg-deadbeef",
		"recipient": "user@example.net",
		"terminal": true,
		"delivery": {
			"status": "delivered",
			"smtpStatusCode": "250"
		}
	}
}
```

Refer to [Event subscriptions](https://developers.cloudflare.com/email-service/reference/event-subscriptions/) to see all event types and example payloads.

## 2026-06-08

  
**Authenticated SMTP submission now available in beta**  

You can now send emails through **Cloudflare Email Service** using authenticated [SMTP submission](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) on `smtp.mx.cloudflare.net:465`. SMTP joins the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) and the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) as a third way to send transactional email — useful for existing applications that already speak SMTP and language-native SMTP libraries (Nodemailer, `smtplib`, PHPMailer, JavaMail).

| Setting | Value |
| --- | --- |
| Host | `smtp.mx.cloudflare.net` |
| Port | `465` (implicit TLS) |
| AUTH | `PLAIN` or `LOGIN` |
| Username | `api_token` |
| Password | A Cloudflare API token (account-owned or user-owned) with **Email Sending: Edit** |

Submissions enter the same delivery pipeline as the REST API and Workers binding: identical [limits](https://developers.cloudflare.com/email-service/platform/limits/), automatic DKIM and ARC signing, and shared dashboard logs.

Send your first email with a single command:

```sh
curl --ssl-reqd \
  --url "smtps://smtp.mx.cloudflare.net:465" \
  --user "api_token:<API_TOKEN>" \
  --mail-from "welcome@yourdomain.com" \
  --mail-rcpt "user@example.com" \
  --upload-file mail.txt
```

Refer to the [SMTP reference](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) for authentication details, response codes, and language-specific examples.

## 2026-05-28

  
**Send emails with named recipient addresses**  

You can now send emails with display names on recipient addresses in addition to the existing `from` support. Pass an object with `email` and an optional `name` field for `to`, `cc`, `bcc`, `replyTo`, or `from`:

*src/index.jsjs*

```js
export default {
	async fetch(request, env) {
		const response = await env.EMAIL.send({
			from: { email: "support@example.com", name: "Support Team" },
			to: { email: "jane@example.com", name: "Jane Doe" },
			cc: [
				"manager@company.com",
				{ email: "team@company.com", name: "Engineering Team" },
			],
			subject: "Welcome!",
			html: "<h1>Thanks for joining!</h1>",
			text: "Thanks for joining!",
		});

		return Response.json({ messageId: response.messageId });
	},
};
```

*src/index.tsts*

```ts
export default {
	async fetch(request, env): Promise<Response> {
		const response = await env.EMAIL.send({
			from: { email: "support@example.com", name: "Support Team" },
			to: { email: "jane@example.com", name: "Jane Doe" },
			cc: [
				"manager@company.com",
				{ email: "team@company.com", name: "Engineering Team" },
			],
			subject: "Welcome!",
			html: "<h1>Thanks for joining!</h1>",
			text: "Thanks for joining!",
		});

		return Response.json({ messageId: response.messageId });
	},
} satisfies ExportedHandler<Env>;
```

Plain strings remain fully supported for backward compatibility, and you can mix strings and named objects in the same array.

Refer to the [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) and [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) documentation for full request examples.

## 2026-04-16

  
**Email Sending now in public beta**  

**[Email Sending](https://developers.cloudflare.com/email-service/api/send-emails/)** is now in public beta. Send transactional emails directly from Workers (`env.EMAIL.send()`) or the REST API, with support for HTML, plain text, attachments, inline images, and custom headers. Email Sending joins [Email Routing ↗︎](https://blog.cloudflare.com/introducing-email-routing/) under the new **Cloudflare Email Service** — a single service for sending and receiving email on the Cloudflare developer platform.

Send an email from a Worker in a few lines of code:

*src/index.jsjs*

```js
export default {
	async fetch(request, env) {
		const response = await env.EMAIL.send({
			from: "notifications@yourdomain.com",
			to: "user@example.com",
			subject: "Order confirmed",
			html: "<h1>Your order has been confirmed</h1>",
			text: "Your order has been confirmed.",
		});

		return Response.json({ messageId: response.messageId });
	},
};
```

*src/index.tsts*

```ts
export default {
	async fetch(request, env): Promise<Response> {
		const response = await env.EMAIL.send({
			from: "notifications@yourdomain.com",
			to: "user@example.com",
			subject: "Order confirmed",
			html: "<h1>Your order has been confirmed</h1>",
			text: "Your order has been confirmed.",
		});

		return Response.json({ messageId: response.messageId });
	},
} satisfies ExportedHandler<Env>;
```

Email Service also integrates with the [Agents SDK](https://developers.cloudflare.com/agents/), giving your agents a native `onEmail` hook to receive, process, and reply to emails. Combined with the new [Email MCP server ↗︎](https://github.com/cloudflare/mcp-server-cloudflare) and Wrangler CLI email commands, any agent can send email regardless of where it runs.

Start sending and receiving emails from Workers and agents today. Email Sending is available on the Workers paid plan. Refer to the [Email Service documentation](https://developers.cloudflare.com/email-service/) to get started.

## 2025-07-21

  
**Subaddressing support in Email Routing**  

Subaddressing, as defined in [RFC 5233 ↗︎](https://www.rfc-editor.org/rfc/rfc5233), also known as plus addressing, is now supported in Email Routing. This enables using the "+" separator to augment your custom addresses with arbitrary detail information.

Now you can send an email to `user+detail@example.com` and it will be captured by the `user@example.com` custom address. The `+detail` part is ignored by Email Routing, but it can be captured next in the processing chain in the logs, an [Email Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/) or an [Agent application ↗︎](https://github.com/cloudflare/agents/tree/main/examples/email-agent).

Customers can use this feature to dynamically add context to their emails, such as tracking the source of an email or categorizing emails without needing to create multiple custom addresses.

![Subaddressing](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=1316,height=678,format=webp/_astro/subaddressing.x65bljxx.png)

Check our [Developer Docs](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#subaddressing) to learn how to enable subaddressing in Email Routing.

## 2025-06-30

  
**Mail authentication requirements for Email Routing**  

The Email Routing platform supports [SPF ↗︎](https://datatracker.ietf.org/doc/html/rfc7208) records and [DKIM (DomainKeys Identified Mail) ↗︎](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) signatures and honors these protocols when the sending domain has them configured. However, if the sending domain doesn't implement them, we still forward the emails to upstream mailbox providers.

Starting on July 3, 2025, we will require all emails to be authenticated using at least one of the protocols, SPF or DKIM, to forward them. We also strongly recommend that all senders implement the DMARC protocol.

If you are using a Worker with an Email trigger to receive email messages and forward them upstream, you will need to handle the case where the forward action may fail due to missing authentication on the incoming email.

SPAM has been a long-standing issue with email. By enforcing mail authentication, we will increase the efficiency of identifying abusive senders and blocking bad emails. If you're an email server delivering emails to large mailbox providers, it's likely you already use these protocols; otherwise, please ensure you have them properly configured.

## 2025-04-08

  
**Local development support for Email Workers**  

Email Workers enables developers to programmatically take action on anything that hits their email inbox. If you're building with Email Workers, you can now test the behavior of an Email Worker script, receiving, replying and sending emails in your local environment using `wrangler dev`.

Below is an example that shows you how you can receive messages using the `email()` handler and parse them using [postal-mime ↗︎](https://www.npmjs.com/package/postal-mime):

```ts
import * as PostalMime from "postal-mime";

export default {
	async email(message, env, ctx) {
		const parser = new PostalMime.default();
		const rawEmail = new Response(message.raw);
		const email = await parser.parse(await rawEmail.arrayBuffer());
		console.log(email);
	},
};
```

Now when you run `npx wrangler dev`, wrangler will expose a local `/cdn-cgi/local/email` endpoint that you can `POST` email messages to and trigger your Worker's `email()` handler:

```bash
curl -X POST 'http://localhost:8787/cdn-cgi/local/email' \
  --url-query 'from=sender@example.com' \
  --url-query 'to=recipient@example.com' \
  --header 'Content-Type: application/json' \
  --data-raw 'Received: from smtp.example.com (127.0.0.1)
        by cloudflare-email.com (unknown) id 4fwwffRXOpyR
        for <recipient@example.com>; Tue, 27 Aug 2024 15:50:20 +0000
From: "John" <sender@example.com>
Reply-To: sender@example.com
To: recipient@example.com
Subject: Testing Email Workers Local Dev
Content-Type: text/html; charset="windows-1252"
X-Mailer: Curl
Date: Tue, 27 Aug 2024 08:49:44 -0700
Message-ID: <6114391943504294873000@ZSH-GHOSTTY>

Hi there'
```

This is what you get in the console:

```json
{
	"headers": [
		{
			"key": "received",
			"value": "from smtp.example.com (127.0.0.1) by cloudflare-email.com (unknown) id 4fwwffRXOpyR for <recipient@example.com>; Tue, 27 Aug 2024 15:50:20 +0000"
		},
		{ "key": "from", "value": "\"John\" <sender@example.com>" },
		{ "key": "reply-to", "value": "sender@example.com" },
		{ "key": "to", "value": "recipient@example.com" },
		{ "key": "subject", "value": "Testing Email Workers Local Dev" },
		{ "key": "content-type", "value": "text/html; charset=\"windows-1252\"" },
		{ "key": "x-mailer", "value": "Curl" },
		{ "key": "date", "value": "Tue, 27 Aug 2024 08:49:44 -0700" },
		{
			"key": "message-id",
			"value": "<6114391943504294873000@ZSH-GHOSTTY>"
		}
	],
	"from": { "address": "sender@example.com", "name": "John" },
	"to": [{ "address": "recipient@example.com", "name": "" }],
	"replyTo": [{ "address": "sender@example.com", "name": "" }],
	"subject": "Testing Email Workers Local Dev",
	"messageId": "<6114391943504294873000@ZSH-GHOSTTY>",
	"date": "2024-08-27T15:49:44.000Z",
	"html": "Hi there\n",
	"attachments": []
}
```

Local development is part of the development flow. It also works for sending, replying, and forwarding emails. For more information, refer to [local development for email routing](https://developers.cloudflare.com/email-service/observability/local-development/routing/).

## 2025-03-12

  
**Threaded replies now possible in Email Workers**  

We’re removing some of the restrictions in Email Routing so that AI Agents and task automation can better handle email workflows, including how Workers can [reply](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/#reply-to-emails) to incoming emails.

It's now possible to keep a threaded email conversation with an [Email Worker](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/) script as long as:

- The incoming email has to have valid [DMARC ↗︎](https://www.cloudflare.com/learning/dns/dns-records/dns-dmarc-record/).
- The email can only be replied to once in the same `EmailMessage` event.
- The recipient in the reply must match the incoming sender.
- The outgoing sender domain must match the same domain that received the email.
- Every time an email passes through Email Routing or another MTA, an entry is added to the `References` list. We stop accepting replies to emails with more than 100 `References` entries to prevent abuse or accidental loops.

Here's an example of a Worker responding to Emails using a Workers AI model:

*AI model responding to emailsts*

```ts
import PostalMime from "postal-mime";
import { createMimeMessage } from "mimetext";
import { EmailMessage } from "cloudflare:email";

export default {
	async email(message, env, ctx) {
		const email = await PostalMime.parse(message.raw);
		const res = await env.AI.run("@cf/meta/llama-2-7b-chat-fp16", {
			messages: [
				{
					role: "user",
					content: email.text ?? "",
				},
			],
		});

		// message-id is generated by mimetext
		const response = createMimeMessage();
		response.setHeader("In-Reply-To", message.headers.get("Message-ID")!);
		response.setSender("agent@example.com");
		response.setRecipient(message.from);
		response.setSubject("Llama response");
		response.addMessage({
			contentType: "text/plain",
			data:
				res instanceof ReadableStream
					? await new Response(res).text()
					: res.response!,
		});

		const replyMessage = new EmailMessage(
			"<email>",
			message.from,
			response.asRaw(),
		);
		await message.reply(replyMessage);
	},
} satisfies ExportedHandler<Env>;
```

See [Reply to emails from Workers](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/#reply-to-emails) for more information.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"BlogPosting","@id":"https://developers.cloudflare.com/email-service/platform/changelog/#page","headline":"Changelog","description":"Track Cloudflare Email Service updates, features, and fixes.","url":"https://developers.cloudflare.com/email-service/platform/changelog/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/platform/changelog/og.png?v=bd83792d4994d46d","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Service sending quotas, rate limits, message size limits, and compliance requirements.
title: Limits
image: https://developers.cloudflare.com/email-service/platform/limits/og.png?v=6630ae5a023cafad
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Limits

Email sending quotas, rate limits, and how to request higher limits for production use

Last updated Sep 25, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/platform/limits/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Cloudflare Email Service has the following limits to ensure optimal performance and prevent abuse. These limits apply to emails sent via the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/), the [Workers binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), and [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) unless noted otherwise.

## Daily sending limits

New accounts start with a conservative daily quota and scale up over time based on your sending behavior, deliverability rates, and account standing. Limits are applied per account and may be adjusted automatically as your reputation improves.

If you need higher sending limits sooner than automatic adjustment provides, refer to "Need a higher limit?" at the bottom of this page to request an increase.

## Verified destination addresses

Before you onboard a sending domain, you can send emails only to [verified destination addresses](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#destination-addresses) in your account. After you onboard a sending domain, you can send to any recipient immediately.

Sends to verified destination addresses are always free: they do not count toward your monthly [quota](https://developers.cloudflare.com/email-service/platform/pricing/) or your daily sending limits, on any plan, including when only Email Routing is configured. You can only send from your routing domains.

## Email content limits

| Component | Limit | Notes |
| --- | --- | --- |
| **Recipients (to, cc, bcc)** | 50 per email | Combined across all recipient fields |
| **Subject line** | 998 characters | RFC 5322 compliant |
| **Total message size** | 5 MiB | Including attachments |
| **Total message size** | 25 MiB | For [verified destination addresses](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#destination-addresses) only |
| **Header size** | 16 KB | All custom headers combined |

## Suppression list limits

The following limits apply to the Email Sending [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/) API:

| Limit | Value | Notes |
| --- | --- | --- |
| **Active entries** | 1 per address and scope | One `account` suppression and one per sending domain |
| **Results per page** | 1,000 | Maximum `per_page` value with a default of 100 |
| **Items per bulk import** | 1,000 | Split larger imports across several requests |
| **Bulk import requests** | 10 per minute | Applies per account and returns `429` when exceeded |
| **Note length** | 1,000 characters | The optional `note` field on an entry |

The suppression list has no total-count field. Refer to the [list suppressions API reference](https://developers.cloudflare.com/api/resources/email_sending/subresources/suppressions/methods/list/) for pagination details.

## Zone limits

| Limit | Value | Notes |
| --- | --- | --- |
| **Domains per zone** | 30 | Combined total of domains configured for Email Routing or Email Sending in a zone, including the apex domain |

## Email Routing limits

The following limits apply to inbound email handled by Email Routing.

| Limit | Value | Notes |
| --- | --- | --- |
| **Routing rules per domain** | 200 | Each rule maps an email pattern to a destination |
| **Destination addresses per account** | 200 | Verified destination addresses are shared across all domains in the account |
| **Inbound message size** | 25 MiB | Messages larger than this are rejected |
| **Reply `References` entries** | 100 | If the incoming email has more than 100 `References` entries, `message.reply()` throws. Reduces reply loops. |

Each routing rule maps one email pattern to one destination address or one Worker. To forward a single email pattern to multiple destinations, use a Worker that calls `forward()` once per destination. All destinations must be verified beforehand.

### Routing to Workers on the Workers Free plan

Workers that handle incoming emails count toward the standard Workers CPU and memory limits. On the Workers Free plan, complex handlers may exceed these limits and fail to process a message. Failed invocations appear in [Workers logs](https://developers.cloudflare.com/workers/observability/logs/) with the `EXCEEDED_CPU` error. Upgrade to the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/) for higher CPU and memory limits.

### Emails sent from Workers

Emails sent from a Worker using the `send_email` binding appear in the Email Routing summary as **dropped**, even when they were delivered successfully. To track outbound send success, use [Email sending metrics and logs](https://developers.cloudflare.com/email-service/observability/) instead.

## Compliance

All email sending must follow applicable anti-spam laws and regulations to maintain good standing and deliverability.

- **CAN-SPAM Act** (United States)
- **GDPR** (European Union)
- **CASL** (Canada)
- Include proper unsubscribe mechanisms
- Honor opt-out requests promptly

Need a higher limit?

To request an adjustment to a limit, complete the [Limit Increase Request Form ↗︎](https://forms.gle/eX6pXvit1wBv77Yw5). If the limit can be increased, Cloudflare will contact you with next steps.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/platform/limits/#page","headline":"Limits","description":"Email Service sending quotas, rate limits, message size limits, and compliance requirements.","url":"https://developers.cloudflare.com/email-service/platform/limits/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/platform/limits/og.png?v=6630ae5a023cafad","dateModified":"2026-09-25","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Service pricing for outbound sending and inbound routing across Workers Free and Paid plans.
title: Pricing
image: https://developers.cloudflare.com/email-service/platform/pricing/og.png?v=36248b01a1557d58
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Pricing

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/platform/pricing/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Cloudflare Email Service pricing is based on your Cloudflare plan and email usage.

## Plan pricing

Email Routing is available on both the Workers Free and Workers Paid plans. Sending to arbitrary recipients requires the Workers Paid plan. Sending to [verified destination addresses](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/#destination-addresses) in your account is free on all plans, including when only Email Routing is configured.

|  | Workers Free | Workers Paid |
| --- | --- | --- |
| **Outbound emails (Email Sending)** | Not available | 3,000 included per month, then $0.35 per 1,000 emails |
| **Inbound emails (Email Routing)** | Unlimited | Unlimited |

The 3,000 included emails apply per account, per month, aligned with your Cloudflare subscription billing cycle. Emails that hard-bounce or are otherwise accepted by Email Service count toward the quota. Emails rejected at the API boundary, including sends blocked by the [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/), do not count toward the quota.

Sends to verified destination addresses are free and do not count toward the included quota.

Email Routing Workers is billed according to [Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/).

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/platform/pricing/#page","headline":"Pricing","description":"Email Service pricing for outbound sending and inbound routing across Workers Free and Paid plans.","url":"https://developers.cloudflare.com/email-service/platform/pricing/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/platform/pricing/og.png?v=36248b01a1557d58","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Email Sending event names and Queues payload fields.
title: Event subscriptions
image: https://developers.cloudflare.com/email-service/reference/event-subscriptions/og.png?v=5c6d7bf41be9127a
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Event subscriptions

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/reference/event-subscriptions/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

[Event subscriptions](https://developers.cloudflare.com/queues/event-subscriptions/) allow you to receive messages when events occur across your Cloudflare account. Cloudflare products (e.g., [KV](https://developers.cloudflare.com/kv/), [Workers AI](https://developers.cloudflare.com/workers-ai/), [Workers](https://developers.cloudflare.com/workers/)) can publish structured events to a [queue](https://developers.cloudflare.com/queues/), which you can then consume with Workers or [HTTP pull consumers](https://developers.cloudflare.com/queues/configuration/pull-consumers/) to build custom workflows, integrations, or logic.

For more information on [Event Subscriptions](https://developers.cloudflare.com/queues/event-subscriptions/), refer to the [management guide](https://developers.cloudflare.com/queues/event-subscriptions/manage-event-subscriptions/).

## Available Email Sending events

Email Sending event subscriptions publish outbound transactional email lifecycle events for messages sent from an enabled sending domain. Each subscription is scoped to one sending domain: either the zone apex, such as `example.com`, or a verified sending subdomain, such as `send.example.com`.

Email Routing events, including inbound forwards, replies, Worker-emitted routing events, and deliveries to verified routing recipients, are not published as Email Sending events.

#### `message.delivered`

Triggered when the recipient mail server accepts the message.

**Example:**

```json
{
	"type": "cf.email.sending.message.delivered",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "example.com"
	},
	"payload": {
		"eventId": "0190d0c4-7e9a-7b3c-9f12-1a2b3c4d5e6f",
		"messageId": "0101018f7d0c4d9a-msg-deadbeef",
		"sender": "noreply@example.com",
		"recipient": "user@example.net",
		"subject": "Welcome",
		"terminal": true,
		"delivery": {
			"status": "delivered",
			"provider": "gmail",
			"deliveryTimeMs": 1234,
			"smtpStatusCode": "250",
			"smtpEnhancedStatusCode": "2.0.0",
			"smtpResponse": "250 2.0.0 OK 1714820445 a1b2c3 - gsmtp"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

#### `message.deferred`

Triggered when a temporary delivery failure occurs and delivery retries are still pending.

**Example:**

```json
{
	"type": "cf.email.sending.message.deferred",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "send.example.com"
	},
	"payload": {
		"eventId": "0190d0c4-7e9b-7714-9004-11f0b6d9a341",
		"messageId": "0101018f7d0c4d9a-msg-deferred",
		"sender": "receipts@send.example.com",
		"recipient": "user@example.net",
		"subject": "Your receipt",
		"terminal": false,
		"delivery": {
			"status": "deferred",
			"provider": "external_smtp",
			"deliveryTimeMs": 2143,
			"smtpStatusCode": "451",
			"smtpEnhancedStatusCode": "4.2.0",
			"smtpResponse": "451 4.2.0 Temporary mailbox error"
		},
		"bounce": {
			"type": "soft",
			"classification": "temporary_failure",
			"reason": "451 4.2.0 Temporary mailbox error"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

#### `message.bounced`

Triggered when a permanent bounce occurs, or when temporary delivery retries are exhausted.

**Example:**

```json
{
	"type": "cf.email.sending.message.bounced",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "send.example.com"
	},
	"payload": {
		"eventId": "0190d0c4-7ea1-7af2-8b88-c1d2e3f4a5b6",
		"messageId": "0101018f7d0c4d9a-msg-bounced",
		"sender": "receipts@send.example.com",
		"recipient": "user@example.net",
		"subject": "Your receipt",
		"terminal": true,
		"delivery": {
			"status": "bounced",
			"provider": "external_smtp",
			"deliveryTimeMs": 2143,
			"smtpStatusCode": "550",
			"smtpEnhancedStatusCode": "5.1.1",
			"smtpResponse": "550 5.1.1 User unknown"
		},
		"bounce": {
			"type": "hard",
			"classification": "permanent_failure",
			"reason": "550 5.1.1 User unknown"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

#### `message.failed`

Triggered when an internal or non-SMTP delivery error occurs.

**Example:**

```json
{
	"type": "cf.email.sending.message.failed",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "example.com"
	},
	"payload": {
		"eventId": "0190d0c4-81c1-7bc6-9344-829cd9001a12",
		"messageId": "0101018f7d0c4d9a-msg-failed",
		"sender": "noreply@example.com",
		"recipient": "user@example.net",
		"subject": "Welcome",
		"terminal": true,
		"delivery": {
			"status": "failed"
		},
		"failure": {
			"reason": "delivery_failed"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

#### `message.rejected`

Triggered when the message is rejected before delivery by policy, including suppression, validation rejection, or spam rejection.

**Example:**

```json
{
	"type": "cf.email.sending.message.rejected",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "example.com"
	},
	"payload": {
		"eventId": "0190d0c4-830a-77e5-8fa0-8d10f4e8dc8b",
		"messageId": "0101018f7d0c4d9a-msg-rejected",
		"sender": "noreply@example.com",
		"recipient": "user@example.net",
		"subject": "Welcome",
		"terminal": true,
		"delivery": {
			"status": "rejected"
		},
		"rejection": {
			"reason": "suppressed",
			"party": "recipient",
			"detail": "Recipient is suppressed"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

#### `message.complained`

Triggered when a delivered message is marked as spam by the recipient through a feedback loop.

**Example:**

```json
{
	"type": "cf.email.sending.message.complained",
	"source": {
		"type": "email.sending",
		"zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
		"domain": "send.example.com"
	},
	"payload": {
		"eventId": "0190d0c4-9f01-7c44-b2a1-aabbccddeeff",
		"messageId": "0101018f7d0c4d9a-msg-complained",
		"sender": "news@send.example.com",
		"recipient": "user@example.net",
		"terminal": true,
		"delivery": {
			"status": "complained"
		},
		"complaint": {
			"type": "abuse"
		}
	},
	"metadata": {
		"accountId": "f9f79265f388666de8122cfb508d7776",
		"eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f",
		"eventSchemaVersion": 1,
		"eventTimestamp": "2026-06-01T02:48:57.132Z"
	}
}
```

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/event-subscriptions/#page","headline":"Event subscriptions","description":"Email Sending event names and Queues payload fields.","url":"https://developers.cloudflare.com/email-service/reference/event-subscriptions/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/reference/event-subscriptions/og.png?v=5c6d7bf41be9127a","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Answers about Email Service limits, reputation, supported email, and abuse.
title: FAQ
image: https://developers.cloudflare.com/email-service/reference/faq/og.png?v=39a17a72000f6427
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# FAQ

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/reference/faq/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Find direct answers to common Cloudflare Email Service questions.

## What happens if I exceed my sending limits?

Email Service may queue or reject emails that exceed sending limits. Error responses include rate-limit information.

Refer to [Email Service limits](https://developers.cloudflare.com/email-service/platform/limits/) for quota details and increase requests.

## What affects my sender reputation?

Sender reputation measures how much inbox providers trust your email. Authentication, bounces, complaints, volume patterns, engagement, and sending history affect it.

Refer to [Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/) for related reputation factors.

## Can I use Email Service for marketing emails?

No. Email Service supports transactional email only.

## How do I report email abuse or spam?

Report email abuse to [mailabuse@cloudflare.com](mailto:mailabuse@cloudflare.com). Include:

- Full email headers
- Description of the issue
- Any relevant account information

Refer to [Postmaster contact information](https://developers.cloudflare.com/email-service/reference/postmaster/#contact-information) for the canonical address.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/faq/#page","headline":"FAQ","description":"Answers about Email Service limits, reputation, supported email, and abuse.","url":"https://developers.cloudflare.com/email-service/reference/faq/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/reference/faq/og.png?v=39a17a72000f6427","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Allowed, platform-controlled, and custom email headers for Email Service with validation rules.
title: Email headers
image: https://developers.cloudflare.com/email-service/reference/headers/og.png?v=f7f5235f5e874a71
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Email headers

Which email headers you can set, which are auto-generated, and how they are validated

Last updated Aug 25, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/reference/headers/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

When sending emails with Cloudflare Email Service, you can set custom headers using the `headers` field in the [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) or [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/). The Email Service uses a **allowlist-based approach** — only explicitly approved headers are accepted. Any header not on the allowlist (and not an `X-` prefixed custom header) is rejected at API time with a clear error.

When sending over [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/), set headers directly in the MIME message rather than through a `headers` field. The same allowlist applies.

## Platform-controlled headers

These headers are auto-generated by the Cloudflare Email Service infrastructure. You cannot set or override them. If you include any of these in the `headers` object, the API returns `E_HEADER_NOT_ALLOWED`.

| Header | Behavior |
| --- | --- |
| `Date` | UTC timestamp set at acceptance time |
| `Message-ID` | Generated with Cloudflare domain for unique tracking |
| `MIME-Version` | Always `1.0` |
| `Content-Type` | Generated from body parts provided |
| `Content-Transfer-Encoding` | Generated from content analysis |
| `DKIM-Signature` | Signed by Cloudflare infrastructure |
| `Return-Path` | Set to Cloudflare bounce processor |
| `Received` | Added per RFC 5321 at each hop |
| `Feedback-ID` | Generated for Google Postmaster Tools reputation feedback |
| `ARC-*` | Authentication chain for forwarding |
| `TLS-Required` | Platform-controlled delivery infrastructure setting |
| `TLS-Report-Domain` | TLS failure reports route to Cloudflare infrastructure |
| `TLS-Report-Submitter` | References Cloudflare sending domain |
| `CFBL-Address` | Complaint feedback loop address (RFC 9477) |
| `CFBL-Feedback-ID` | Complaint feedback loop ID (RFC 9477) |

Headers that correspond to first-class API fields — `From`, `To`, `Cc`, `Bcc`, `Subject`, `Reply-To` — are also rejected in the `headers` object with `E_HEADER_USE_API_FIELD`. Set these using the dedicated API fields (`from`, `to`, `cc`, `bcc`, `subject`, `replyTo` for Workers / `reply_to` for REST) instead.

## Allowlisted custom headers

These headers can be set via the `headers` field. Any header not listed here and not starting with `X-` is rejected with `E_HEADER_NOT_ALLOWED`.

Email Service rejects the entire send request if it contains a disallowed header. It does not remove the header and continue sending the message.

### Threading and reply headers

| Header | RFC | Notes |
| --- | --- | --- |
| `In-Reply-To` | [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) | Critical for email threading in all clients |
| `References` | [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) | Critical for email threading in all clients |
| `Thread-Index` | Microsoft (non-standard) | Conversation index used by Outlook and Exchange Online |
| `Thread-Topic` | Microsoft (non-standard) | Conversation subject used by Outlook and Exchange Online |

### List management headers

| Header | RFC | Notes |
| --- | --- | --- |
| `List-Unsubscribe` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | Must contain `<https://...>` and/or `<mailto:...>` URI(s). HTTP (non-TLS) URIs are rejected. Gmail and Yahoo require this for bulk senders. Always DKIM-signed per RFC 8058. |
| `List-Unsubscribe-Post` | [RFC 8058 ↗︎](https://datatracker.ietf.org/doc/html/rfc8058) | Must be exactly `List-Unsubscribe=One-Click` (case-sensitive). Requires `List-Unsubscribe` with an HTTPS URI. |
| `List-Id` | [RFC 2919 ↗︎](https://datatracker.ietf.org/doc/html/rfc2919) | List identification |
| `List-Archive` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | URL to list archive |
| `List-Help` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | URL for help |
| `List-Owner` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | List owner contact |
| `List-Post` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | Address for posting |
| `List-Subscribe` | [RFC 2369 ↗︎](https://datatracker.ietf.org/doc/html/rfc2369) | Subscribe URL or address |
| `Precedence` | De facto standard | Accepted values: `bulk`, `list`, `junk` |

### Automated message identification

| Header | RFC | Notes |
| --- | --- | --- |
| `Auto-Submitted` | [RFC 3834 ↗︎](https://datatracker.ietf.org/doc/html/rfc3834) | Values: `auto-generated`, `auto-replied`, `auto-notified` |

### Content and display

| Header | RFC | Notes |
| --- | --- | --- |
| `Content-Language` | [RFC 3282 ↗︎](https://datatracker.ietf.org/doc/html/rfc3282) | Language of content (for example, `en`, `fr`) |
| `Keywords` | [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) | Message keywords (comma-separated for multiple values) |
| `Comments` | [RFC 5322 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322) | Additional comments (comma-separated for multiple values) |
| `Importance` | [RFC 2156 ↗︎](https://datatracker.ietf.org/doc/html/rfc2156) | Values: `high`, `normal`, `low` |
| `Priority` | [RFC 2156 ↗︎](https://datatracker.ietf.org/doc/html/rfc2156) | Values: `normal`, `non-urgent`, `urgent` |
| `Sensitivity` | [RFC 2156 ↗︎](https://datatracker.ietf.org/doc/html/rfc2156) | Values: `personal`, `private`, `company-confidential` |
| `Organization` | [RFC 4021 ↗︎](https://datatracker.ietf.org/doc/html/rfc4021) | Sender's organization name |

### Delivery and notification

| Header | RFC | Notes |
| --- | --- | --- |
| `Require-Recipient-Valid-Since` | [RFC 7293 ↗︎](https://datatracker.ietf.org/doc/html/rfc7293) | Address reuse protection |
| `Expires` | [RFC 2156 ↗︎](https://datatracker.ietf.org/doc/html/rfc2156) | Date and time after which the message is no longer valid |
| `Reply-By` | [RFC 2156 ↗︎](https://datatracker.ietf.org/doc/html/rfc2156) | Date and time by which a reply is requested |

### Modern standards

| Header | RFC | Notes |
| --- | --- | --- |
| `Archived-At` | [RFC 5064 ↗︎](https://datatracker.ietf.org/doc/html/rfc5064) | URL where message is archived |

### Custom X-headers

Any header starting with `X-` is allowed. This covers common headers like `X-Mailer`, `X-Priority`, `X-Campaign-ID`, and any custom tracking headers your application needs.

- **Name format:** `X-[A-Za-z0-9\-_]+`, maximum 100 characters
- **Value:** UTF-8, maximum 2,048 bytes
- **No count limit** on X-headers (subject to the 16 KB total payload limit)

## Usage examples

```bash
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/email/sending/send" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "user@example.com",
    "from": "notifications@yourdomain.com",
    "subject": "Your weekly digest",
    "html": "<h1>Weekly Digest</h1>",
    "headers": {
      "In-Reply-To": "<original-message-id@yourdomain.com>",
      "References": "<original-message-id@yourdomain.com>",
      "List-Unsubscribe": "<https://yourdomain.com/unsubscribe?id=abc123>",
      "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
      "X-Campaign-ID": "weekly-digest-2026-03",
      "X-User-Segment": "premium"
    }
  }'
```

```typescript
const response = await env.EMAIL.send({
	to: "user@example.com",
	from: "notifications@yourdomain.com",
	subject: "Your weekly digest",
	html: "<h1>Weekly Digest</h1>",
	headers: {
		// Threading
		"In-Reply-To": "<original-message-id@yourdomain.com>",
		References: "<original-message-id@yourdomain.com>",

		// List management (required by Gmail/Yahoo for bulk senders)
		"List-Unsubscribe": "<https://yourdomain.com/unsubscribe?id=abc123>",
		"List-Unsubscribe-Post": "List-Unsubscribe=One-Click",

		// Custom tracking
		"X-Campaign-ID": "weekly-digest-2026-03",
		"X-User-Segment": "premium",
	},
});
```

## Header limits

| Limit | Value |
| --- | --- |
| Max allowlisted (non-X) custom headers | 20 |
| Max header name length | 100 bytes |
| Max header value length | 2,048 bytes |
| Total custom headers payload | 16 KB |

The total payload is calculated as `sum(len(name) + 2 + len(value) + 2)` for all custom headers (name + `:` + value + CRLF). Allowlisted and X-headers are counted together toward this limit.

## Validation rules

1. **Header names** — ASCII only, no spaces, no colons, 1–100 characters. Allowlisted headers must match `[A-Za-z0-9\-]+`. X-headers must match `X-[A-Za-z0-9\-_]+` (underscores allowed only in X-headers).
2. **Header values** — UTF-8 allowed, maximum 2,048 bytes, no bare CR/LF. Empty values are rejected.
3. **Case-insensitive matching** — Header names are matched case-insensitively per [RFC 5322 §2.2 ↗︎](https://datatracker.ietf.org/doc/html/rfc5322#section-2.2). The canonical casing from the allowlist is used in the generated message.
4. **Proper line folding** — Long headers are folded at 78 characters per RFC 5322 using CRLF+WSP, not MIME encoding.
5. **Single occurrence** — The `headers` type is `{ [key]: string }`, so each header name can appear at most once. For headers that support multiple values (such as `Keywords` or `Comments`), use comma-separated values in a single string.

## Error codes

| Error Code | When | Example message |
| --- | --- | --- |
| `E_HEADER_NOT_ALLOWED` | Header is platform-controlled or not on the allowlist | `Header 'Date' is not allowed. It is auto-generated by the platform.` |
| `E_HEADER_USE_API_FIELD` | Header corresponds to a first-class API field | `Header 'From' must be set via the 'from' API field, not the 'headers' object.` |
| `E_HEADER_VALUE_INVALID` | Header value is malformed or empty | `Header 'List-Unsubscribe' must contain angle-bracket HTTPS or mailto URI(s).` |
| `E_HEADER_VALUE_TOO_LONG` | Header value exceeds 2,048 byte limit | `Header 'X-Campaign-ID' value exceeds 2048 byte limit.` |
| `E_HEADER_NAME_INVALID` | Header name contains invalid characters or exceeds 100 bytes | `Header name 'Bad Header!' contains invalid characters.` |
| `E_HEADERS_TOO_LARGE` | Total custom headers payload exceeds 16 KB | `Total custom headers payload (17.2KB) exceeds 16KB limit.` |
| `E_HEADERS_TOO_MANY` | Too many allowlisted (non-X) custom headers | `21 allowlisted headers provided, maximum is 20.` |

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/headers/#page","headline":"Email headers","description":"Allowed, platform-controlled, and custom email headers for Email Service with validation rules.","url":"https://developers.cloudflare.com/email-service/reference/headers/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/reference/headers/og.png?v=f7f5235f5e874a71","dateModified":"2026-08-25","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Reference page with postmaster information for professionals, as well as configuration details for Email Service.
title: Postmaster
image: https://developers.cloudflare.com/email-service/reference/postmaster/og.png?v=5588004a72f7016d
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Postmaster

Last updated Jun 9, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/reference/postmaster/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

This page provides technical information about Email Service to professionals who administer email systems, and other email providers.

Here you will find information regarding Email Service, along with best practices, rules, guidelines, troubleshooting tools, as well as configuration details for Email Service.

## Postmaster

### Contact information

The best way to contact us is using our [community forum ↗︎](https://community.cloudflare.com/new-topic?category=Feedback/Previews%20%26%20Betas&tags=email) or our [Discord server ↗︎](https://discord.cloudflare.com).

To report email abuse, contact us at [mailabuse@cloudflare.com](mailto:mailabuse@cloudflare.com).

### Authenticated Received Chain (ARC)

Email Service supports [Authenticated Received Chain (ARC) ↗︎](https://arc-spec.org/). ARC allows intermediate email servers, such as forwarders, to attach a record of the original authentication results to a message. The destination server can then verify the authenticity of forwarded messages even when SPF or DKIM would otherwise fail due to forwarding. Major providers, including Google, also support ARC.

### DKIM signature

[DKIM (DomainKeys Identified Mail) ↗︎](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) ensures that email messages are not altered in transit between the sender and the recipient's SMTP servers through public-key cryptography.

Through this standard, the sender publishes its public key to a domain's DNS once, and then signs the body of each message before it leaves the server. The recipient server reads the message, gets the domain public key from the domain's DNS, and validates the signature to ensure the message was not altered in transit.

Email Service adds DKIM signatures to outgoing emails on behalf of the customer's sending domain to ensure email authenticity and improve deliverability.

Email Sending and Email Routing use separate DKIM selectors. You can find the DKIM keys for your domain by querying the following:

```sh
# Email Sending DKIM
dig TXT cf-bounce._domainkey.example.com +short

# Email Routing DKIM
dig TXT cf2024-1._domainkey.example.com +short
```

For forwarded emails, Email Routing adds two DKIM signatures: one for `email.cloudflare.net`, which covers [sender rewriting](#sender-rewriting), and one for the recipient domain configured by the customer. You can query the Cloudflare sender rewriting key directly:

```sh
dig TXT cf2024-1._domainkey.email.cloudflare.net +short
```

### DMARC enforcing

Email Service supports Domain-based Message Authentication, Reporting & Conformance (DMARC). When sending emails, Email Service ensures proper SPF and DKIM alignment to pass DMARC authentication. For Email Routing, incoming emails are rejected if they fail authentication according to the sender's DMARC policy. Refer to [dmarc.org ↗︎](https://dmarc.org/) for more information on this protocol.

It is recommended that all domains implement the DMARC protocol for optimal email deliverability.

### Mail authentication requirement

Cloudflare requires incoming emails to pass some form of authentication. The email must either pass SPF or be correctly signed with DKIM. Emails that fail both checks are rejected.

Outbound emails sent through Email Service are always authenticated with both SPF and DKIM to maximize deliverability and maintain sender reputation.

### IPv6 support

Email Service supports IPv6 for both inbound and outbound email delivery. For outbound, the service connects to recipient SMTP servers over IPv6 when the recipient has AAAA records for their MX servers, and falls back to IPv4 otherwise. For inbound, Email Routing accepts mail over IPv6 on its MX servers.

You can verify IPv6 connectivity for any destination using `dig`:

```sh
dig mx gmail.com
dig AAAA gmail-smtp-in.l.google.com
```

### MX and SPF records

When using Email Service for sending emails, no special MX records are required on your domain. However, if you are also using Email Routing for inbound emails, the appropriate MX records are configured automatically.

For SPF records, Email Service uses `_spf.mx.cloudflare.net`. Email Sending configures SPF on the `cf-bounce` subdomain, while Email Routing configures SPF on the root domain:

```txt
v=spf1 include:_spf.mx.cloudflare.net ~all
```

For inbound mail, Email Routing announces multiple MX servers under the `*.mx.cloudflare.net` zone with different priorities. For example:

```txt
example.com.    IN    MX    13 amir.mx.cloudflare.net.
example.com.    IN    MX    86 linda.mx.cloudflare.net.
example.com.    IN    MX    24 isaac.mx.cloudflare.net.
```

### Outbound prefixes

Email Service sends its traffic using both IPv4 and IPv6 prefixes, when supported by the recipient SMTP server.

If you are a postmaster and are having trouble receiving Email Service emails, allow the following outbound IP addresses in your server configuration:

**IPv4**

`104.30.0.0/19`

**IPv6**

`2405:8100:c000::/38`

To verify the current authoritative ranges, query the SPF record directly:

```sh
dig TXT _spf.mx.cloudflare.net +short
```

### Outbound hostnames

Email Service will use the following outbound domains for the `HELO/EHLO` command:

- `cloudflare-email.net`
- `cloudflare-email.org`
- `cloudflare-email.com`

PTR records (reverse DNS) ensure that each hostname has a corresponding IP. For example:

```sh
dig a-h.cloudflare-email.net +short
```

```sh
104.30.0.7
```

```sh
dig -x 104.30.0.7 +short
```

```sh
a-h.cloudflare-email.net.
```

### Sender rewriting

For forwarded emails, Email Routing uses the [Sender Rewriting Scheme ↗︎](https://en.wikipedia.org/wiki/Sender_Rewriting_Scheme) to rewrite the envelope sender (the SMTP `MAIL FROM` address) to a Cloudflare-controlled forwarding domain. This rewriting allows SPF to pass at the destination server even though the message is being relayed. The `From:` header of the message is not modified.

### SMTP errors

Email Service provides detailed SMTP error responses to help diagnose delivery issues. For Email Routing, upstream SMTP errors returned by the destination mail server are forwarded back to the sending server in-session rather than as a separate bounce message.

### Realtime Block Lists

Email Service monitors sender reputation and may temporarily delay or block emails from IPs that appear on Realtime Block Lists (RBLs). This helps maintain the service's overall reputation and deliverability.

For Email Routing, inbound mail from senders on RBLs is rejected with an SMTP error similar to:

```txt
554 <YOUR_IP_ADDRESS> found on one or more RBLs (abusixip). Refer to https://developers.cloudflare.com/email-service/reference/postmaster/#realtime-block-lists
```

You can use tools like [MxToolbox ↗︎](https://mxtoolbox.com/blacklists.aspx) to check a sending IP against multiple block lists at once. If you believe your emails are being incorrectly blocked, contact the RBL maintainer directly or reach out through Cloudflare support channels.

### SPF record breakdown

Email Service publishes its SPF data under `_spf.mx.cloudflare.net`. You can resolve the underlying record directly:

```sh
dig TXT _spf.mx.cloudflare.net +short
```

The record uses the format defined in [RFC 7208 ↗︎](https://datatracker.ietf.org/doc/html/rfc7208):

```txt
"v=spf1 ip4:104.30.0.0/20 ~all"
```

The `~all` mechanism is a SoftFail. Receiving servers should treat mail from IPs not listed in the record as suspicious but should not reject it outright on SPF alone.

---

## Related configuration

For full configuration details, refer to:

- [Domain configuration](https://developers.cloudflare.com/email-service/configuration/domains/) — DNS records, sending and routing setup
- [Limits](https://developers.cloudflare.com/email-service/platform/limits/) — rate limits, sending quotas, and message size limits
- [Deliverability](https://developers.cloudflare.com/email-service/concepts/deliverability/) — bounce handling and reputation management
- [Suppression lists](https://developers.cloudflare.com/email-service/concepts/suppressions/) — automatic and manual suppression management

---

## Known limitations

Below, you will find information regarding known limitations for Email Service, particularly Email Routing functionality.

### Email address internationalization (EAI)

Email Routing does not support [internationalized email addresses ↗︎](https://en.wikipedia.org/wiki/International_email). Email Routing only supports [internationalized domain names ↗︎](https://en.wikipedia.org/wiki/Internationalized_domain_name).

This means that you can have email addresses with an internationalized domain, but not an internationalized local-part (the first part of your email address, before the @ symbol). Refer to the following examples:

- `info@piñata.es` - **Supported**
- `piñata@piñata.es` - **Not supported**

### Non-delivery reports (NDRs)

Email Routing does not forward non-delivery reports to the original sender. This means the sender will not receive a notification indicating that the email did not reach the intended destination.

### Restrictive DMARC policies can make forwarded emails fail

Due to the nature of email forwarding, restrictive DMARC policies might make forwarded emails fail to be delivered. Refer to [dmarc.org ↗︎](https://dmarc.org/) for more information.

### Sending or replying to an email from your Cloudflare domain

Email Routing does not support sending or replying from your Cloudflare domain. When you reply to emails forwarded by Email Routing, the reply will be sent from your destination address (like `my-name@gmail.com`), not from the email pattern of the routing rule that delivered the message (like `info@yourdomain.com`).

### "." is treated as a normal character in email patterns

The `.` character, which performs special actions in email providers like Gmail, is treated as a normal character in routing rule email patterns.

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/postmaster/#page","headline":"Postmaster","description":"Reference page with postmaster information for professionals, as well as configuration details for Email Service.","url":"https://developers.cloudflare.com/email-service/reference/postmaster/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/reference/postmaster/og.png?v=5588004a72f7016d","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```

---

---
description: Diagnose and fix delivery, authentication, and local development issues for Email Service.
title: Troubleshooting
image: https://developers.cloudflare.com/email-service/reference/troubleshooting/og.png?v=cfb0345825341448
---

[Skip to content](#main-content)

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/email-service/llms.txt  
> Use this file to discover all available pages before exploring further.

# Troubleshooting

Last updated Oct 15, 2026|Copy as Markdown| [View as Markdown](https://developers.cloudflare.com/email-service/reference/troubleshooting/index.md)| [Agent setup](https://developers.cloudflare.com/agent-setup/)

Email authentication is critical for successful email delivery. This guide helps you troubleshoot common SPF, DKIM, and DMARC issues with Email Service.

## SPF (Sender Policy Framework) issues

### Multiple SPF records

Having multiple SPF records on your domain is not allowed and will prevent Email Service from working properly. If your domain has multiple SPF records:

1. Log in to the Cloudflare dashboard, select your account and domain, then go to **DNS** > **Records**. [Go to **Records** ↗](https://dash.cloudflare.com/?to=/:account/:zone/dns/records)
2. Look for multiple TXT records starting with `v=spf1`.
3. Delete the incorrect SPF record.
4. Ensure you have the correct SPF records:
   - For **Email Routing** (root domain): `v=spf1 include:_spf.mx.cloudflare.net ~all`
   - For **Email Sending** ( `cf-bounce` subdomain): `v=spf1 include:_spf.mx.cloudflare.net ~all`

If you are unsure which SPF record is the correct one to keep, you can remove all of them and let Cloudflare regenerate the required records:

1. In **DNS** > **Records**, delete every TXT record starting with `v=spf1` on the affected name.
2. Go to **Compute** > **Email Service** and re-onboard or re-enable the affected service. Cloudflare adds the correct SPF record back automatically.

### Missing SPF record

If emails are being rejected due to SPF failures:

1. Log in to the Cloudflare dashboard, select your account and domain, then go to **DNS** > **Records**. [Go to **Records** ↗](https://dash.cloudflare.com/?to=/:account/:zone/dns/records)
2. Add TXT records for the appropriate service:
   - For **Email Routing**: **Name**: `@` (root domain), **Content**: `v=spf1 include:_spf.mx.cloudflare.net ~all`
   - For **Email Sending**: **Name**: `cf-bounce`, **Content**: `v=spf1 include:_spf.mx.cloudflare.net ~all`
3. If you already have an SPF record on the root domain, modify it to include `include:_spf.mx.cloudflare.net`

### SPF record syntax errors

Common SPF record syntax issues:

- **Missing version**: SPF records must start with `v=spf1`
- **Multiple includes**: Combine multiple services using separate `include:` statements
- **Too many DNS lookups**: SPF records are limited to 10 DNS lookups total
- **Incorrect all mechanism**: Use `~all` (SoftFail) or `-all` (Fail), not `+all`

**Correct format:**

```txt
v=spf1 include:_spf.mx.cloudflare.net include:other-service.com ~all
```

### Checking SPF records

Verify your SPF record is configured correctly:

```sh
dig TXT example.com +short | grep spf
```

Expected result should include:

```txt
"v=spf1 include:_spf.mx.cloudflare.net ~all"
```

## DKIM (DomainKeys Identified Mail) issues

### Missing DKIM records

Email Service automatically generates DKIM keys for your domain, but the DNS records must be properly configured. Email Sending and Email Routing use separate DKIM selectors:

1. In the [Cloudflare dashboard ↗︎](https://dash.cloudflare.com/), go to **Compute** > **Email Service**.
2. Select your domain.
3. Check the **Settings** page for the appropriate service:
   - **Email Sending**: Go to **Email Sending** > **Settings** to find the sending DKIM record ( `cf-bounce._domainkey`).
   - **Email Routing**: Go to **Email Routing** > **Settings** to find the routing DKIM record ( `cf2024-1._domainkey`).
4. Copy the DKIM record details.
5. Go to **DNS** > **Records** and add the DKIM TXT record with the correct selector name and public key.

### DKIM key rotation

If you need to rotate DKIM keys:

1. Contact Cloudflare support to request key rotation.
2. Update your DNS records with the new DKIM key when provided.
3. Monitor email delivery during the transition period.

### Checking DKIM records

Verify your DKIM records are configured correctly:

```sh
# Check Email Sending DKIM
dig TXT cf-bounce._domainkey.example.com +short

# Check Email Routing DKIM
dig TXT cf2024-1._domainkey.example.com +short
```

Expected result for either:

```txt
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
```

### DKIM signature validation failures

If DKIM validation is failing:

1. Verify the DKIM record exists in DNS
2. Check that the record name matches the correct selector:
   - Email Sending: `cf-bounce._domainkey.yourdomain.com`
   - Email Routing: `cf2024-1._domainkey.yourdomain.com`
3. Ensure there are no extra spaces or characters in the DNS record
4. Wait for DNS propagation (up to 48 hours)
5. Use online DKIM validators to test your configuration

## DMARC (Domain-based Message Authentication, Reporting & Conformance) issues

### Missing DMARC policy

While not required, DMARC significantly improves email deliverability:

1. Go to **DNS** > **Records** in the Cloudflare dashboard. [Go to **Records** ↗](https://dash.cloudflare.com/?to=/:account/:zone/dns/records)
2. Add a TXT record:
   - **Name**: `_dmarc`
   - **Content**: `v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com`

### DMARC policy too strict

If a strict DMARC policy is causing delivery issues:

1. Start with a lenient policy: `p=none` (monitor only)
2. Monitor DMARC reports for several weeks
3. Gradually increase strictness: `p=quarantine` then `p=reject`
4. Ensure both SPF and DKIM are properly aligned

### DMARC alignment issues

DMARC requires either SPF or DKIM alignment:

**SPF alignment**: The domain in the `Mail From` header must align with the domain in the `From` header **DKIM alignment**: The DKIM signature domain must align with the domain in the `From` header

Email Service ensures proper alignment automatically.

### Checking DMARC records

Verify your DMARC record:

```sh
dig TXT _dmarc.example.com +short
```

Example result:

```txt
"v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; sp=quarantine"
```

## Local development issues

### "Cannot serialize value: \[object ArrayBuffer]"

This error occurs when passing `ArrayBuffer` content in attachment fields during local development with `wrangler dev`. The local email binding simulator cannot serialize `ArrayBuffer` values.

**Solution:** Deploy your Worker with `npx wrangler deploy` and test binary attachments (images, PDFs) against the deployed version. String content for text-based attachments works normally in local development. Refer to [local development for email sending](https://developers.cloudflare.com/email-service/observability/local-development/sending/#known-limitations) for more details.

## Common delivery issues

### Email going to spam

If emails are going to spam folders:

1. Check authentication: Ensure SPF, DKIM, and DMARC are properly configured
2. Domain reputation: New domains may have lower reputation initially
3. Content quality: Avoid spam trigger words and excessive HTML formatting
4. Sender reputation: Monitor bounce rates and complaint rates
5. List hygiene: Remove bounced and invalid email addresses

### High bounce rates

To reduce bounce rates:

1. Validate email addresses: Use real-time validation
2. Maintain clean lists: Remove hard bounces immediately
3. Monitor feedback loops: Subscribe to ISP feedback loops
4. Gradual warm-up: For new domains, start with small volumes

### Suppressed recipient

Each sending domain has a [**Drop suppressed recipients** setting](https://developers.cloudflare.com/email-service/configuration/domains/#drop-suppressed-recipients). The setting is off by default.

When the setting is off, the REST API returns `400`, the Workers binding throws `E_RECIPIENT_SUPPRESSED`, and SMTP rejects the message. Any suppressed recipient causes the send to fail.

When the setting is on, Email Service removes suppressed recipients and processes the remaining recipients. If none remain, SMTP may return `250 2.0.0 Ok` without a Message-ID and deliver nothing.

To investigate a suppressed recipient:

1. Query the exact recipient:

   *List account Email Sending suppressionsbash*

   

   ```bash
   curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/suppressions?email=recipient%40example.com" \
   	--request GET \
   	--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
   ```

2. Use the `reason`, `expires_at`, and `read_only` fields to choose an action. For the complete decision table, refer to [Suppression rules](https://developers.cloudflare.com/email-service/concepts/suppressions/#suppression-rules).
3. If `result` is empty, confirm that you queried the correct account. Then check the sending logs.
4. If the logs still report a suppression, [contact Cloudflare Support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).

### Recipient blocked after expiration or deletion

Expired entries stop appearing in the public list after their `expires_at` timestamp passes. Delivery enforcement can take additional time to stop.

Updates and deletions also propagate asynchronously. The management list and delivery enforcement can briefly differ.

To investigate delayed enforcement:

1. Confirm that the entry no longer appears in the account list.
2. Retry later because suppression updates propagate asynchronously.
3. If the recipient remains blocked, [contact Cloudflare Support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).

### Bounces before suppression enforcement

Bounce suppressions are created through background delivery processing. Messages already in progress can bounce before a new suppression takes effect.

### Complaint suppression does not appear

Complaint suppressions appear after Cloudflare receives and validates the provider report. The provider determines when that report arrives.

### Missing suppression after a bounce

Not every failure results in a suppression. Email Service creates automatic entries only for eligible recipient-side failures. Sender authentication, sender reputation, and unrelated infrastructure failures do not suppress the recipient. Check [Email sending logs](https://developers.cloudflare.com/email-service/observability/logs/) for the specific failure.

### ISP-specific issues

Different ISPs have specific requirements:

- Gmail: Requires strong domain reputation and authentication
- Outlook/Hotmail: Sensitive to content and sender reputation
- Yahoo: Strict DMARC enforcement
- Corporate: Often have strict filtering rules

## Testing tools

Use these tools to validate your email authentication setup:

1. MX Toolbox: Check SPF, DKIM, and DMARC records
2. DMARC Analyzer: Validate DMARC policy and alignment
3. Mail Tester: Test email deliverability and authentication
4. Google Admin Toolbox: Google's email authentication checker

## Getting help

If you continue to experience authentication issues:

1. Check the [Email Service analytics](https://developers.cloudflare.com/email-service/observability/metrics-analytics/) for delivery metrics
2. Review bounce messages for specific error codes
3. Contact [Cloudflare Support ↗︎](https://dash.cloudflare.com/?to=/:account/support) with:
   - Domain name
   - Example email headers
   - Specific error messages
   - SPF, DKIM, and DMARC record configurations

Was this helpful?

YesNo

## On this page

[![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/)

```json
{"@context":"https://schema.org/","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/troubleshooting/#page","headline":"Troubleshooting","description":"Diagnose and fix delivery, authentication, and local development issues for Email Service.","url":"https://developers.cloudflare.com/email-service/reference/troubleshooting/","inLanguage":"en","image":"https://developers.cloudflare.com/email-service/reference/troubleshooting/og.png?v=cfb0345825341448","dateModified":"2026-10-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```
