---
title: Cloudflare Email Service
description: Send transactional emails and route incoming emails to Workers or email addresses with Cloudflare Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Cloudflare Email Service

Send transactional emails and route incoming emails to Workers or email addresses

Cloudflare Email Service provides powerful email capabilities:

* **Email Sending** Beta for outbound transactional emails  
 Available on Workers Paid plan
* **Email Routing** for handling incoming emails with Workers or routing to email 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 all plans, even when only Email Routing is configured.

Together, these two features make it possible for you to send and receive emails from your applications. For example, you can use Email Service for:

* Transactional emails (welcome messages, password resets, order confirmations)
* Authentication flows (magic links, email verification, two-factor authentication)
* Notifications and alerts
* Custom email addresses (support@, contact@, orders@)
* Emails as a mode of interaction for agents, such as send an email to create an issue in ticket tracking

Access Email Service directly from Cloudflare Workers using [bindings](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/), from any platform using the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/), or over [authenticated SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/):

* [ Workers ](#tab-panel-8810)
* [ API ](#tab-panel-8811)
* [ SMTP ](#tab-panel-8812)

Send emails with the `EMAIL` binding and handle incoming emails with the `email()` handler in `src/index.ts`:

TypeScript

```
interface Env {  EMAIL: SendEmail;}
export default {  // Handle HTTP requests (Email Sending)  async fetch(request, env, ctx): Promise<Response> {    // Send a welcome email    await env.EMAIL.send({      to: "user@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.",    });
    return new Response("Email sent successfully");  },
  // Handle incoming emails (Email Routing)  async email(message, env, ctx): Promise<void> {    // Forward to support team    if (message.to.includes("support@yourdomain.com")) {      await message.forward("team@yourdomain.com");    }
    // Send auto-reply    await env.EMAIL.send({      to: message.from,      from: "noreply@yourdomain.com",      subject: "We received your message",      html: "<h1>Thank you!</h1><p>We'll get back to you soon.</p>",    });  },} satisfies ExportedHandler<Env>;
```

Add the bindings to your Wrangler configuration file:

JSONC

```
{  "$schema": "node_modules/wrangler/config-schema.json",  "name": "<ENTER_WORKER_NAME>",  "main": "src/index.ts",  "compatibility_date": "$today",
  // Email sending  "send_email": [    {      "name": "EMAIL"    }  ],
  // Email routing  "email": [    {      "name": "EMAIL_HANDLER"    }  ]}
```

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/send" \  --header "Authorization: Bearer <API_TOKEN>" \  --header "Content-Type: application/json" \  --data '{    "to": "user@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."  }'
```

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/).

Terminal window

```
cat > mail.txt <<EOFFrom: welcome@yourdomain.comTo: user@example.comSubject: 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 "user@example.com" \  --upload-file mail.txt
```

See the full [API reference](https://developers.cloudflare.com/email-service/api/send-emails/) for the REST API, Workers binding, and SMTP.

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

---

## Features

###  Email Sending 

Send transactional emails with high deliverability and global performance.

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

###  Email Routing 

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

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

###  Deliverability 

Automatic IP reputation management and deliverability optimization.

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

###  Analytics & Observability 

Monitor email performance with comprehensive metrics and alerting.

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

###  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 ](https://developers.cloudflare.com/email-service/api/) 

---

## Related products

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

Build serverless applications that can send emails directly from the edge.

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

Process email events asynchronously with Workers Queues integration.

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

Store and analyze custom email metrics with Workers Analytics Engine.

---

## More resources

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

Learn about Email Service limits and quotas.

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

Understand Email Service pricing and plans.

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

Explore practical examples and implementation patterns.

[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.

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/email-service/#page","headline":"Cloudflare Email Service · Cloudflare Email Service docs","description":"Send transactional emails and route incoming emails to Workers or email addresses with Cloudflare Email Service.","url":"https://developers.cloudflare.com/email-service/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}}]}
```

---

---
title: Workers API
description: Process incoming emails with the email() handler in Cloudflare Workers to forward, reply, or reject messages.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Workers API

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

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:

* [  TypeScript ](#tab-panel-8813)
* [  Python ](#tab-panel-8814)

TypeScript

```
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:

TypeScript

```
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

* [ Basic properties ](#tab-panel-8815)
* [ Parse email content ](#tab-panel-8816)

TypeScript

```
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.

TypeScript

```
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:

* [ Simple forwarding ](#tab-panel-8817)
* [ Conditional forwarding ](#tab-panel-8818)
* [ Multiple forwarding ](#tab-panel-8819)

TypeScript

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

TypeScript

```
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");    }  },};
```

TypeScript

```
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.

TypeScript

```
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.

* [ Simple auto-reply ](#tab-panel-8820)
* [ Smart auto-reply ](#tab-panel-8821)

TypeScript

```
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");  },};
```

TypeScript

```
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:

* [ Simple rejection ](#tab-panel-8822)
* [ Content-based rejection ](#tab-panel-8823)

TypeScript

```
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");  },};
```

TypeScript

```
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:

TypeScript

```
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/local-development/routing/)
* Manage rules and addresses programmatically with the [Email Routing REST API](https://developers.cloudflare.com/email-service/platform/email-routing-rest-api/)
* Set up [email routing configuration](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/)
* See [email routing examples](https://developers.cloudflare.com/email-service/examples/email-routing/) for advanced email processing
* Learn about [spam filtering](https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/) with Workers

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/route-emails/email-handler/#page","headline":"Workers API · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-15","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/api/","name":"API reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/api/route-emails/","name":"Route emails"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/api/route-emails/email-handler/","name":"Workers API"}}]}
```

---

---
title: REST API
description: Send emails from any application using the Email Service REST API with standard HTTP requests.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# REST API

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%5Fsending/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:

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

## Send an email

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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, see [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).

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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, see [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.

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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:

```
{  "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 handling

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

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

REST API 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.

## Next steps

* Refer to the [Email Sending API reference](https://developers.cloudflare.com/api/resources/email%5Fsending/methods/send/) for the full request and response schemas.
* See the [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) for sending emails directly from Cloudflare Workers using bindings.
* See [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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/rest-api/#page","headline":"REST API · Cloudflare Email Service docs","description":"Send emails from any application using the Email Service REST API with standard HTTP requests.","url":"https://developers.cloudflare.com/email-service/api/send-emails/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/api/","name":"API reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/api/send-emails/","name":"Send emails"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/api/send-emails/rest-api/","name":"REST API"}}]}
```

---

---
title: SMTP
description: Send emails from any SMTP-capable application or mail client using authenticated SMTP submission on smtp.mx.cloudflare.net.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# SMTP

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

```
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 (see below) |

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.

Terminal window

```
cat > mail.txt <<EOFFrom: welcome@yourdomain.comTo: recipient@example.comSubject: 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:

Terminal window

```
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.

```
$ openssl s_client -quiet -connect smtp.mx.cloudflare.net:465 -crlf220 mx.cloudflare.net Cloudflare Email ESMTP Service ready> EHLO client.example.com250-mx.cloudflare.net greets client.example.com250-AUTH PLAIN LOGIN250-SIZE 5242880250-8BITMIME250 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> DATA354 Start mail input; end with <CR><LF>.<CR><LF>From: welcome@yourdomain.comTo: recipient@example.comSubject: Welcome
Thanks for signing up..250 2.0.0 Ok <jZTWt0pQO4p2LG7ByfkeSYUvT62k85Q12nCA@yourdomain.com>> QUIT221 mx.cloudflare.net Cloudflare Email ESMTP Service closing transmission channel
```

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

## Examples

For language-specific examples — curl, Nodemailer, Python `smtplib`, and PHPMailer — see [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. See [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. See [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.                        |

## 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, see [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.
* [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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/smtp/#page","headline":"SMTP · Cloudflare Email Service docs","description":"Send emails from any SMTP-capable application or mail client using authenticated SMTP submission on smtp.mx.cloudflare.net.","url":"https://developers.cloudflare.com/email-service/api/send-emails/smtp/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/api/","name":"API reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/api/send-emails/","name":"Send emails"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/api/send-emails/smtp/","name":"SMTP"}}]}
```

---

---
title: Workers API
description: Send emails directly from Cloudflare Workers using the Email Service binding and send() method.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Workers API

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:

* [  wrangler.jsonc ](#tab-panel-8824)
* [  wrangler.toml ](#tab-panel-8825)

JSONC

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

TOML

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

You can restrict which senders and recipients a binding may use. Refer to [Configure 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

TypeScript

```
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/local-development/sending/#known-limitations).

### Basic usage

TypeScript

```
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, see [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).

TypeScript

```
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, see [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/).

## Error handling

Handle email sending errors gracefully:

TypeScript

```
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          | Recipient is on suppression list          | Email address has bounced or reported your emails as spam                                                                   |
| 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                                                                             |

## 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.

TypeScript

```
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

* See the [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) for sending emails without Workers
* See [SMTP](https://developers.cloudflare.com/email-service/api/send-emails/smtp/) for sending from any SMTP-capable application or mail client
* See [practical examples](https://developers.cloudflare.com/email-service/examples/) of email sending patterns
* Learn about [email routing](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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#page","headline":"Workers API · Cloudflare Email Service docs","description":"Send emails directly from Cloudflare Workers using the Email Service binding and send() method.","url":"https://developers.cloudflare.com/email-service/api/send-emails/workers-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-25","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/api/","name":"API reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/api/send-emails/","name":"Send emails"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/api/send-emails/workers-api/","name":"Workers API"}}]}
```

---

---
title: Email deliverability
description: Understand bounce handling and reputation management for optimal email delivery with Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email deliverability

Understand bounce handling and reputation management for optimal email delivery.

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:

* Email address doesn't exist (`user@domain.com` → No such user)
* Domain does not exist (`user@nonexistentdomain.com`)
* Recipient server permanently blocks your domain
* Content rejected as spam by recipient filters

**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/).

Hard bounced addresses are automatically added to your [suppression list](https://developers.cloudflare.com/email-service/concepts/suppressions/) to protect your sender reputation.

### 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 over an extended period.

## 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/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/deliverability/#page","headline":"Email deliverability · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/concepts/deliverability/","name":"Email deliverability"}}]}
```

---

---
title: Email authentication
description: SPF, DKIM, and DMARC authentication for secure and deliverable email sending with Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email authentication

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

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 cf-bounce.yourdomain.com "v=spf1 include:_spf.mx.cloudflare.net ~all"  
```
* **Email Routing** SPF record on the root domain:  
```  
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 _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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/email-authentication/#page","headline":"Email authentication · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/concepts/email-authentication/","name":"Email authentication"}}]}
```

---

---
title: Email lifecycle
description: The complete email processing flow from send request to final delivery status in Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email lifecycle

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

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 the recipient against your account's suppression list, which includes bounces, complaints, and unsubscribes. Recipients found on this list are blocked from receiving the email.
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/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/email-lifecycle/#page","headline":"Email lifecycle · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/concepts/email-lifecycle/","name":"Email lifecycle"}}]}
```

---

---
title: Suppression lists
description: Manage Email Service suppression lists to prevent sending to invalid or complaining addresses.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Suppression lists

Manage email suppression lists to prevent emails from being sent to addresses that shouldn't receive them, protecting your sender reputation with automatic and manual suppression management.

Suppression lists prevent emails from being sent to addresses that should not receive them, protecting your sender reputation and ensuring compliance with anti-spam regulations.

## Account suppression list

Cloudflare automatically manages suppressions for your account to preserve your reputation as an email sender.

Cloudflare will automatically add email addresses to your account suppression list for the following reasons:

* **Hard bounces**: Invalid or non-existent email addresses are immediately suppressed.
* **Repeated soft bounces**: Addresses that repeatedly fail delivery are temporarily or permanently suppressed based on the frequency and pattern of failures.
* **Spam complaints**: Recipients who marked emails as spam. Cloudflare integrates with Postmasters to receive spam complaints and automatically updates your account suppression list to prevent you from sending emails to this email address and preserve your email sending reputation.

You may also manually add or remove email addresses from your suppression list as needed. The removal of email addresses that have been automatically added to your suppression list as a result of a spam complaint is limited to avoid abuse.

## Best practices

### List hygiene

Maintaining clean suppression lists is essential for optimal email delivery performance and sender reputation. Regular maintenance helps identify delivery issues early and ensures legitimate recipients can receive your emails.

* Review suppression lists monthly
* Remove temporary suppressions that have expired
* Identify patterns in suppressed addresses
* Update email validation rules based on common issues

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/concepts/suppressions/#page","headline":"Suppression lists · Cloudflare Email Service docs","description":"Manage Email Service suppression lists to prevent sending to invalid or complaining addresses.","url":"https://developers.cloudflare.com/email-service/concepts/suppressions/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/concepts/suppressions/","name":"Suppression lists"}}]}
```

---

---
title: Domain configuration
description: Configure and verify DNS records for Email Service sending and routing on your domain.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Domain configuration

Configure domains for Cloudflare Email Service, manage DNS records, and verify domain setup for both email sending and routing.

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.

* [ Email Sending ](#tab-panel-8835)
* [ Email Routing ](#tab-panel-8836)

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.

* [ MX records ](#tab-panel-8826)
* [ SPF record ](#tab-panel-8827)
* [ DKIM record ](#tab-panel-8828)
* [ DMARC record ](#tab-panel-8829)

**Purpose**: Route bounce emails back to Cloudflare for processing.

```
MX cf-bounce.yourdomain.com route1.mx.cloudflare.netMX cf-bounce.yourdomain.com route2.mx.cloudflare.netMX 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 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 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 _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.

* [ MX records ](#tab-panel-8830)
* [ SPF record ](#tab-panel-8831)
* [ DKIM record ](#tab-panel-8832)

**Purpose**: Route incoming emails to Cloudflare's mail servers.

```
MX yourdomain.com route1.mx.cloudflare.netMX yourdomain.com route2.mx.cloudflare.netMX 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 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 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-%5Fdc-mx-subdomains).

### Verification troubleshooting

* [ DNS propagation ](#tab-panel-8833)
* [ Record conflicts ](#tab-panel-8834)

**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:**

Terminal window

```
# Check sending SPF recorddig TXT cf-bounce.yourdomain.com | grep spf
# Check routing SPF recorddig TXT yourdomain.com | grep spf
# Check sending DKIM recorddig TXT cf-bounce._domainkey.yourdomain.com
# Check routing DKIM recorddig TXT cf2024-1._domainkey.yourdomain.com
# Check routing MX recordsdig 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.

### 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.

## 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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/domains/#page","headline":"Domain configuration · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/configuration/domains/","name":"Domain configuration"}}]}
```

---

---
title: Email routing rules and addresses
description: Create and manage routing rules, destination addresses, and the catch-all rule in Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email routing rules and addresses

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.

Warning

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.

## 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/platform/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.

```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 · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/configuration/email-routing-addresses/","name":"Email routing rules and addresses"}}]}
```

---

---
title: Configure MTA-STS
description: Enable MTA Strict Transport Security for your Email Service domain to protect against downgrade attacks.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Configure MTA-STS

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/_astro/mta-sts-record.DbwO-t_X_1Mbxza.webp)
3. Confirm that the record was created:  
Terminal window  
```  
dig txt _mta-sts.example.com  
```  
```  
_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/_astro/mta-sts-domain.UfZmAoBe_lkXVJ.webp)  
You can then confirm that your policy file is working with the following:  
Terminal window  
```  
curl https://mta-sts.example.com/.well-known/mta-sts.txt  
```  
```  
version: STSv1mode: enforcemx: *.mx.cloudflare.netmax_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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/mta-sts/#page","headline":"Configure MTA-STS · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-24","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/configuration/mta-sts/","name":"Configure MTA-STS"}}]}
```

---

---
title: Configure send bindings
description: Restrict which senders and recipients a Workers send_email binding can use with Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Configure send bindings

When you add a `send_email` binding to a Worker, you can restrict which addresses it may send from and to. Configure these restrictions in your Wrangler configuration file. For the binding API itself, refer to the [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/).

## Binding types

Each entry in `send_email` can be configured to restrict what the binding can do. The sender address must always belong to a domain you have onboarded to Email Service.

* **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.

* [  wrangler.jsonc ](#tab-panel-8837)
* [  wrangler.toml ](#tab-panel-8838)

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

* [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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/send-bindings/#page","headline":"Configure send bindings · Cloudflare Email Service docs","description":"Restrict which senders and recipients a Workers send\\_email binding can use with Email Service.","url":"https://developers.cloudflare.com/email-service/configuration/send-bindings/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/configuration/send-bindings/","name":"Configure send bindings"}}]}
```

---

---
title: Subdomains
description: Configure Email Sending and Email Routing on subdomains within your zone.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Subdomains

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 [routing rules](https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/) on the subdomain in the same way as on 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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/configuration/subdomains/#page","headline":"Subdomains · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/configuration/subdomains/","name":"Subdomains"}}]}
```

---

---
title: Email storage and processing
description: Store incoming emails in KV and process them with Queues for support tickets and workflow automation in Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email storage and processing

Store and process incoming emails using KV storage and queue systems for support tickets and workflow automation

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.

```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 · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-routing/","name":"Email routing"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-routing/email-storage/","name":"Email storage and processing"}}]}
```

---

---
title: Handle hard bounce emails
description: Detect and process hard bounce notifications in Email Service to maintain sender reputation.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Handle hard bounce emails

Detect and handle hard bounce emails to maintain sender reputation and manage undeliverable addresses

Handle hard bounce notifications to automatically remove invalid email addresses from your mailing lists and maintain good sender reputation.

## What are 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

## Configuration

Configure your worker to handle bounce notifications:

* [  wrangler.jsonc ](#tab-panel-8839)
* [  wrangler.toml ](#tab-panel-8840)

JSONC

```
{  "name": "bounce-handler",  // Set this to today's date  "compatibility_date": "2026-06-30",  "send_email": [{ "name": "EMAIL" }],  "kv_namespaces": [    {      "binding": "SUPPRESSION_LIST",      "id": "your-kv-namespace-id",    },  ],}
```

TOML

```
name = "bounce-handler"# Set this to today's datecompatibility_date = "2026-06-30"
[[send_email]]name = "EMAIL"
[[kv_namespaces]]binding = "SUPPRESSION_LIST"id = "your-kv-namespace-id"
```

## Hard bounce detection

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}`,  );}
```

## Testing hard bounce handling

Create a test bounce notification:

Terminal window

```
curl --request POST 'http://localhost:8787/cdn-cgi/handler/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.comSubject: Mail delivery failed: returning message to senderDate: Wed, 28 Aug 2024 10:30:00 +0000Message-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 itsrecipients. 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.comTo: nonexistent@example.comSubject: Welcome to our serviceMessage-ID: <original123@yourdomain.com>
Welcome! Thanks for signing up.'
```

## Checking 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 emailsexport 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

* Learn about [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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/#page","headline":"Handle hard bounce emails · Cloudflare Email Service docs","description":"Detect and process hard bounce notifications in Email Service to maintain sender reputation.","url":"https://developers.cloudflare.com/email-service/examples/email-routing/hard-bounce-handling/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-routing/","name":"Email routing"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-routing/hard-bounce-handling/","name":"Handle hard bounce emails"}}]}
```

---

---
title: Spam filtering
description: Build spam detection for Email Service with keyword analysis, domain reputation checks, and AI-based filtering.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Spam filtering

Implement intelligent spam detection with keyword analysis, domain reputation, and machine learning techniques

Build spam filtering systems with keyword matching, domain validation, and intelligent detection methods for effective email security.

## 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";}
```

## Advanced spam detection with AI

For more sophisticated spam detection, you can enhance the basic filter using [Workers AI](https://developers.cloudflare.com/workers-ai/) to analyze email content with machine learning models. This approach can identify subtle spam patterns that keyword-based filters might miss.

## 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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/#page","headline":"Spam filtering · Cloudflare Email Service docs","description":"Build spam detection for Email Service with keyword analysis, domain reputation checks, and AI-based filtering.","url":"https://developers.cloudflare.com/email-service/examples/email-routing/spam-filtering/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-routing/","name":"Email routing"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-routing/spam-filtering/","name":"Spam filtering"}}]}
```

---

---
title: Email sending
description: Advanced patterns and examples for sending transactional emails with Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email sending

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/)
* [ Magic link authentication ](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/)

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/#page","headline":"Email sending · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}}]}
```

---

---
title: Email attachments
description: Handle different types of email attachments including PDFs, inline images, and file uploads with validation and encoding.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email attachments

Send emails with various types of attachments including PDFs, images, and file uploads with proper validation.

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:

* [  wrangler.jsonc ](#tab-panel-8841)
* [  wrangler.toml ](#tab-panel-8842)

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.41 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>endobj4 0 obj<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>endobj5 0 obj<< /Length 200 >>streamBT/F1 24 Tf50 700 Td(INVOICE #${invoiceData.number}) Tj0 -50 Td/F1 12 Tf(Customer: ${invoiceData.customerName}) Tj0 -20 Td(Date: ${new Date(invoiceData.date).toLocaleDateString()}) Tj0 -20 Td(Amount: $${invoiceData.total}) TjETendstreamendobjxref0 60000000000 65535 f0000000010 00000 n0000000053 00000 n0000000100 00000 n0000000200 00000 n0000000300 00000 ntrailer<< /Size 6 /Root 1 0 R >>startxref400%%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="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="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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/#page","headline":"Email attachments · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-sending/email-attachments/","name":"Email attachments"}}]}
```

---

---
title: Magic link authentication
description: Passwordless login system using magic links sent via email with JWT tokens and session management.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Magic link authentication

Implement passwordless authentication by sending secure, time-limited login links via email.

This example demonstrates how to send a magic link email for passwordless authentication using Cloudflare Email Service.

Configure the email binding in your Wrangler configuration file:

* [  wrangler.jsonc ](#tab-panel-8843)
* [  wrangler.toml ](#tab-panel-8844)

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 that validates the submitted email address, generates a single-use token, and emails the recipient a time-limited login link. The following code implements that handler.

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 simple secure token (you would implement proper JWT/token handling)  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/email-service/examples/email-sending/magic-link/%3C/span%3E%3Cspan%20style="--0:#89DDFF;--1:#007474">${magicUrl}">Login Now</a></p>      <p>This link expires in 15 minutes.</p>    `,    text: `      Login to your account
      Click this link to log in: ${magicUrl}
      This link expires in 15 minutes.    `,  });
  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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/#page","headline":"Magic link authentication · Cloudflare Email Service docs","description":"Passwordless login system using magic links sent via email with JWT tokens and session management.","url":"https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-sending/magic-link/","name":"Magic link authentication"}}]}
```

---

---
title: Specify recipients
description: Send to multiple recipients, CC and BCC, and named addresses using the Workers binding, REST API, or SMTP.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Specify recipients

Specify multiple recipients, CC and BCC, and named addresses when sending with Email Service.

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

* [ Workers ](#tab-panel-8845)
* [ API ](#tab-panel-8846)
* [ SMTP ](#tab-panel-8847)

TypeScript

```
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",});
```

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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.

Terminal window

```
cat > mail.txt <<EOFFrom: Newsletter Team <newsletter@yourdomain.com>To: user1@example.com, user2@example.comSubject: Monthly Newsletter
This month's updatesEOF
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

* [ Workers ](#tab-panel-8848)
* [ API ](#tab-panel-8849)
* [ SMTP ](#tab-panel-8850)

TypeScript

```
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",});
```

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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.

Terminal window

```
cat > mail.txt <<EOFFrom: orders@yourdomain.comTo: customer@example.comCc: manager@example.comReply-To: support@yourdomain.comSubject: Order Confirmation #12345
Your order is confirmedEOF
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.

* [ Workers ](#tab-panel-8851)
* [ API ](#tab-panel-8852)
* [ SMTP ](#tab-panel-8853)

TypeScript

```
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!",});
```

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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.

Terminal window

```
cat > mail.txt <<EOFFrom: 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.

* [ Workers ](#tab-panel-8854)
* [ API ](#tab-panel-8855)
* [ SMTP ](#tab-panel-8856)

TypeScript

```
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",});
```

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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"  }'
```

Terminal window

```
cat > mail.txt <<EOFFrom: support@yourdomain.comTo: plain@example.com, Jane Doe <jane@example.com>Subject: Team update
Monthly updateEOF
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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/recipients/#page","headline":"Specify recipients · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-sending/recipients/","name":"Specify recipients"}}]}
```

---

---
title: User signup flow
description: Handle user registration with automated welcome emails and email verification using secure tokens.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# User signup flow

Send welcome and verification emails during user signup with secure token-based email verification.

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:

* [  wrangler.jsonc ](#tab-panel-8857)
* [  wrangler.toml ](#tab-panel-8858)

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/email-service/examples/email-sending/signup-flow/%3C/span%3E%3Cspan%20style="--0:#89DDFF;--1:#007474">${verificationUrl}" 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

* [Magic link authentication](https://developers.cloudflare.com/email-service/examples/email-sending/magic-link/) — passwordless login flow 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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/signup-flow/#page","headline":"User signup flow · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-sending/signup-flow/","name":"User signup flow"}}]}
```

---

---
title: Send email over SMTP
description: Send transactional emails through Cloudflare Email Service authenticated SMTP from curl, Node.js, Python, or PHP.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Send email over SMTP

Send transactional emails over Cloudflare Email Service SMTP using curl, Nodemailer, Python smtplib, or PHPMailer.

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`.

* [ curl ](#tab-panel-8859)
* [ Node.js (Nodemailer) ](#tab-panel-8860)
* [ Python (smtplib) ](#tab-panel-8861)
* [ PHP (PHPMailer) ](#tab-panel-8862)

### Send an email

Terminal window

```
cat > mail.txt <<EOFFrom: welcome@yourdomain.comTo: recipient@example.comSubject: 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

JavaScript

```
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

JavaScript

```
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).

JavaScript

```
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 osimport smtplibfrom 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

```
<?phpuse 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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/smtp/#page","headline":"Send email over SMTP · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/examples/email-sending/","name":"Email sending"}},{"@type":"ListItem","position":5,"item":{"@id":"/email-service/examples/email-sending/smtp/","name":"Send email over SMTP"}}]}
```

---

---
title: Route emails
description: Forward incoming emails to existing mailboxes or process them with Workers using Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Route emails

Set up email routing to forward incoming emails to existing mailboxes or process them with Workers.

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.

* [ Route to email ](#tab-panel-8865)
* [ Route to Workers ](#tab-panel-8866)

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:  
Terminal window  
```  
npm create cloudflare@latest email-processor  
```  
When prompted, select **"Hello World" Worker** as the template. Then navigate to the project directory:  
Terminal window  
```  
cd email-processor  
```
2. Install the required package for creating email replies:  
Terminal window  
```  
npm install mimetext  
```
3. Add the `nodejs_compat` compatibility flag to your Wrangler configuration file. This is required for the `mimetext` package:

  * [  wrangler.jsonc ](#tab-panel-8863)
  * [  wrangler.toml ](#tab-panel-8864)  
JSONC  
```  
{  "compatibility_flags": ["nodejs_compat"],}  
```  
TOML  
```  
compatibility_flags = [ "nodejs_compat" ]  
```
4. Create your email handler in `src/index.ts`:  
TypeScript  
```  
import { EmailMessage } from "cloudflare:email";import { createMimeMessage } from "mimetext";  
// ============================================// Configuration - Update these values// ============================================const YOUR_DOMAIN = "yourdomain.com"; // Replace with your verified domainconst 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:  
Terminal window  
```  
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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/get-started/route-emails/#page","headline":"Route emails · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/get-started/","name":"Getting started"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/get-started/route-emails/","name":"Route emails"}}]}
```

---

---
title: Send emails
description: Send your first email using the Cloudflare Email Service Workers binding, REST API, or SMTP.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Send emails

Send your first email using the Workers binding, the REST API, or SMTP.

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.

* [ Workers ](#tab-panel-8869)
* [ API ](#tab-panel-8870)
* [ SMTP ](#tab-panel-8871)

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:  
 npm  yarn  pnpm  
```  
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:

  * [  wrangler.jsonc ](#tab-panel-8867)
  * [  wrangler.toml ](#tab-panel-8868)  
JSONC  
```  
{  "send_email": [    {      "name": "EMAIL",      "remote": true,    },  ],}  
```  
TOML  
```  
[[send_email]]name = "EMAIL"remote = true  
```
3. Create your Worker code in `src/index.ts`:  
TypeScript  
```  
// Configuration - Update these valuesconst YOUR_DOMAIN = "yourdomain.com"; // Replace with your verified domainconst 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)).  
Terminal window  
```  
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:  
Terminal window  
```  
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/).

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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:

```
{  "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.

Terminal window

```
cat > mail.txt <<EOFFrom: welcome@yourdomain.comTo: recipient@example.comSubject: 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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/get-started/send-emails/#page","headline":"Send emails · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-25","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/get-started/","name":"Getting started"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/get-started/send-emails/","name":"Send emails"}}]}
```

---

---
title: Email routing
description: Test Email Service routing Workers locally using wrangler dev with simulated incoming emails.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email routing

Test email routing Workers locally using wrangler dev with simulated incoming emails

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).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

## Configuration

Configure your Wrangler file:

* [  wrangler.jsonc ](#tab-panel-8872)
* [  wrangler.toml ](#tab-panel-8873)

JSONC

```
{  "name": "email-routing-worker",  // Set this to today's date  "compatibility_date": "2026-06-30",}
```

TOML

```
name = "email-routing-worker"# Set this to today's datecompatibility_date = "2026-06-30"
```

## 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");    }  },};
```

## Testing

Start your development server:

Terminal window

```
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:

Terminal window

```
curl --request POST 'http://localhost:8787/cdn-cgi/handler/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 +0000From: "John" <sender@example.com>Reply-To: sender@example.comTo: recipient@example.comSubject: Testing Email Workers Local DevContent-Type: text/html; charset="windows-1252"X-Mailer: CurlDate: Tue, 27 Aug 2024 08:49:44 -0700Message-ID: <6114391943504294873000@ZSH-GHOSTTY>
Hi there'
```

This will output the parsed email structure in the console:

```
{  "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/)
* See advanced patterns: [Email routing examples](https://developers.cloudflare.com/email-service/examples/email-routing/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/local-development/routing/#page","headline":"Email routing · Cloudflare Email Service docs","description":"Test Email Service routing Workers locally using wrangler dev with simulated incoming emails.","url":"https://developers.cloudflare.com/email-service/local-development/routing/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/local-development/","name":"Local development"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/local-development/routing/","name":"Email routing"}}]}
```

---

---
title: Email sending
description: Test Email Service sending Workers locally using wrangler dev with simulated email delivery.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email sending

Test email sending Workers locally using wrangler dev with simulated email delivery

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).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

## Configuration

Configure your Wrangler file with the email binding:

* [  wrangler.jsonc ](#tab-panel-8874)
* [  wrangler.toml ](#tab-panel-8875)

JSONC

```
{  "name": "email-sending-worker",  // Set this to today's date  "compatibility_date": "2026-06-30",  "send_email": [{ "name": "EMAIL" }],}
```

TOML

```
name = "email-sending-worker"# Set this to today's datecompatibility_date = "2026-06-30"
[[send_email]]name = "EMAIL"
```

## Remote bindings (recommended)

Using [remote bindings](https://developers.cloudflare.com/workers/local-development/#remote-bindings) is the recommended way to develop with Email Service locally. By default, `wrangler dev` simulates the email binding locally -- emails are logged to the console but not actually sent. With remote bindings, your Worker runs locally but sends real emails through Email Service.

Set `remote: true` on the email binding in your Wrangler configuration:

* [  wrangler.jsonc ](#tab-panel-8876)
* [  wrangler.toml ](#tab-panel-8877)

JSONC

```
{  "name": "email-sending-worker",  // Set this to today's date  "compatibility_date": "2026-06-30",  "send_email": [    {      "name": "EMAIL",      "remote": true,    },  ],}
```

TOML

```
name = "email-sending-worker"# Set this to today's datecompatibility_date = "2026-06-30"
[[send_email]]name = "EMAIL"remote = true
```

Then run `wrangler dev` as usual. Calls to `env.EMAIL.send()` will send actual emails through Email Service while your Worker code runs locally.

Warning

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" },        },      );    }  },};
```

## Testing locally

Start your development server:

Terminal window

```
npx wrangler dev
```

Send a test email:

Terminal window

```
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 will show output like:

```
[wrangler:info] send_email binding called with MessageBuilder:From: sender@yourdomain.comTo: recipient@example.comSubject: 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 will see an error like:

```
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/)
* See advanced patterns: [Email sending examples](https://developers.cloudflare.com/email-service/examples/email-sending/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/local-development/sending/#page","headline":"Email sending · Cloudflare Email Service docs","description":"Test Email Service sending Workers locally using wrangler dev with simulated email delivery.","url":"https://developers.cloudflare.com/email-service/local-development/sending/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-25","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/local-development/","name":"Local development"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/local-development/sending/","name":"Email sending"}}]}
```

---

---
title: Audit logs
description: Track Email Service configuration changes such as rule edits and address additions in Cloudflare audit logs.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Audit logs

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/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/audit-logs/#page","headline":"Email Service audit logs · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/observability/","name":"Observability and logs"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/observability/audit-logs/","name":"Audit logs"}}]}
```

---

---
title: Email logs
description: View and analyze Email Service sending and routing activity logs with authentication and delivery details.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email logs

View and analyze email sending and routing activity logs with detailed authentication and delivery information

Email Service provides comprehensive logging for both email sending and routing activities. Access detailed logs through the Cloudflare dashboard to monitor email flow, troubleshoot delivery issues, and analyze authentication status.

## Activity log

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 logs

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 logs

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.

## Viewing 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.

## Best practices for log monitoring

### Regular review

* Monitor logs daily during initial setup
* Check weekly for ongoing operations
* Review immediately after configuration changes

### Key metrics to watch

* Authentication failure rates
* Bounce patterns and trends
* Delivery success rates

### Troubleshooting workflow

1. Identify the issue: Use logs to pinpoint failure types
2. Check authentication: Verify SPF, DKIM, DMARC configuration
3. Adjust configuration: Make necessary DNS or routing changes
4. Monitor improvement: Track metrics after changes

---

Email logs provide the visibility needed to maintain high deliverability and properly route incoming emails. Use this data to optimize your email configuration and quickly resolve any delivery issues.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/logs/#page","headline":"Email logs · Cloudflare Email Service docs","description":"View and analyze Email Service sending and routing activity logs with authentication and delivery details.","url":"https://developers.cloudflare.com/email-service/observability/logs/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/observability/","name":"Observability and logs"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/observability/logs/","name":"Email logs"}}]}
```

---

---
title: Metrics and analytics
description: Query Email Service sending metrics and delivery rates via the dashboard or GraphQL Analytics API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Metrics and analytics

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.

```
{  "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`):

```
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        }      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBAogWwIYEsA2BlMA7AJi7AcwCEoMAXJckAZwAoASALwHtswAVJQgLhhvIQChAIQAaGAwFII5PgBEqYcZJy4FSkQEoYAbwBQMGADcUYAO6Q9hozFbt6AM3TlIfXXbadufZl66EMAC+Oga2tmDI6Fh4wgCCuEgADuQoxmAA4hAsIEn0NuFGzmiuEO4wia4A+oRgwL7SshKVYFVodb5qwQWFaCgIKHIwAIwADOOjPeEsELiQpHwA2i1V8nAYAMIAulMwobtGAMY52OQHFf04NChsNNaFhS3nRtLUNOdBu5-h391BQA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eWAnBRAwoAE2bsuvHgGY2IAL5A)

#### Delivery failure analysis

To investigate delivery failure causes for a specific date range, grouped by `errorCause` and `sendingDomain`:

```
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        }      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBAogWwIYEsA2ARMaUDdJQBiqaIEYAzgBQAkAXgPYB2YAKkgOYBcMFALhBRMOAQgA0MGvyQQ+PDEj5hxksEwAm8xcoCUMAN4AoGDFwowAd0gHjJmIxbUAZuiUQe++8zace9b+wcMAC+ekZ2dmDI6ADKaupCHACC6kgADnx4YADiEAwgadS2ESYuaG4eMKlKAPocYMB+0rIS1WA1aA1+8RLSfCAUPABE6thZ0MToYOpDIcUlOAgocjAAjAAMm+vzEQwQoxAAQlA8ANptNRhwMQDCALo7MGGPJgDG+Ux8L1UoCGoUKGYFBsJRKbW+JkgeQgNyQAzAEN48USGAY0SY32CjyxERxc2CQA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eWAnBRAwoAE2bsuvHgGY2IAL5A)

#### Hourly volume

To query email sending volume grouped by hour, useful for identifying traffic patterns:

```
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        }      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBAogWwIYEsA2BlMA7AJi7AcwAkB7ECNKANVLRATAAoASAL1OzABUlCAuGAGcALhAKEAhABoYLUUggjB3FIxlycuFWrCSAlDADeAKBgwAbijAB3SMbPmYHLkKYAzdCMiCjzzjx8guwBvIQwAL6Gpk5OYMjoWHgSAIK4SAAOIigWYADiEOQZbo6x5p5o3hC+MOne2YxkFAD6hGDAwQpKsnVgDWBNEM1o7cFakaVlaGooyjAAjAAMy4uTsaQQuJAAQlCCANq9-YPNKRgAwgC6azDRN+YAxuTYIve1uthCKJxCDmVlR10gze5gUIhAQjeERu0NisImESAA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eBmNnDYBGVGwDsqEQBYMFEDCgATZuy68BQyeKmz5AXyA)

#### Individual email events

To query individual email events for troubleshooting specific delivery issues. This uses the `emailSendingAdaptive` dataset and filters by `datetime` (Time type):

```
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      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBASmAxmAdgFwKIFsCGBLAGwwDdU0BnACgBIAvAexTABUcBzALhnLQjxTYBCADQxqPHBDRdmeLGBFjUAExlyFAShgBvAFAwYxPGADukHfoMwGTKgDNCaSF23XGLdlzrvWbGAF8tPSsrMFxCAGUVfjYAQWUcAAc0PFJKSxCDBwInCBcYBKcU+QB9NjBgLwkpUUKwYrASggqvFQCMzII5PGkYAFYABg6Q+ghlSAAhKC4AbTqGkoARDAiAYQBdYZggrYN59V2YOwh6LEO0ekPyEAAjACskNCu0HDQQckOwUnRmKESwK7RASLU74FCHeTkcjsMAASWUnwgJwgqxw7wBmVCSNGi3q+AIh2UAGs5ITcBBEFdEnZDnhyBFEjgzpj-B1WQZWf4gA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eBmNnDYBGVGwDsqEQBYMFEDCgATZuy68BQyeKmz5AXyA)

#### Email routing operations

To query the count of routed emails for a given date range, grouped by `date` and `status`:

```
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        }      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBAogWwIYEsA2AlA9iALigOwHMAhKAZVyVxAGcAKAEgC8sCwAVJIgLhltwRCRAIQAaGIwFIIuPgBFqYcZLAEAJgqUiAlDADeAKBgwAbijAB3SAeMmYrdgwBm6XJD76HbTtz4sfLiIYAF89I3t7MGR0bDxhAEF1JAAHfFMwAHEIHBSGO0iTVzR3CE8YZPcAfSIwYH9pWQlKsCq0Ov81dVCCwrQUBBQ5GABGAAYJsd7IrAh1SDI+AG0Wqvk4cgBhAF1pmHC9kwBjHAJcQ4qBtVoUNlpbQsKWi5NpGloLkL2vyJ+ekKAA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eWAnBRAwoAE2bsuvHgGY2IAL5A)

#### Routing rule activity

To see which routing rules are matching emails, grouped by `ruleMatched` and `action`:

```
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        }      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBAogWwIYEsA2AlA9iALigOwHMMQ0wBBAY3wDcVcoAKAEgC8sCwAVJIgLhgBnXBEJEAhABoYLEUgi5BAESS4w02WAIATFWo0BKGAG8AUDBj0wAd0imLlmBy5CmAM3TqIgk8848fILsAbxEMAC+xuZOTmDI6Nh44hQ6SAAOdGAA4hA46W6OsZaeaN6+MGnqAPpEYMDB8ooyVWDV5A1aupFFxWgoCAyCAIwADOOjvbFYEDqQAEJQggDardXKcADKAMIAulMw0QeWVDgEuMeVA9pCKJxCDsXFrZeWEGRgALJqVAAWYDpXjAkDQ7gRLhEDpDYtCehEgA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eWAnBRAwoAE2bsuvHgGY2IAL5A)

#### Individual routing events

To query individual routing events for troubleshooting:

```
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      }    }  }}
```

[Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBASmAxmAdgFzgexGgligcwFEA3VNAZwAoASAL0xTABUBDAgLhgrQnwICEAGhg0erCGi7NcAWzDDRqACbS5CgJQwA3gCgYMErjAB3SDv0GYDJtQBmuADZpIXbdcYt2Xep7YEYAF8tPSsrMFlWJywcfgBBZVYABzwyKkswgwdnVx0YRJc8eQB9AjBgH3FJEQKwIrBix3KfFSCMzMc5XCkYAFYABnawzAhlSAAhKC4AbVr64oARIgBlAGEAXSGYEK2DOfVdmDsITFlDtExDihAAIwArJDQrtFY0EApD1kQ8RkOIECaAFlXogABZgZSHeQUCjsMAASUhmXCEBOEAWdSijkOygA1nIcZEIIgrkk7IdcBRlklWGdkYF2gyDAzAkA&variables=N4IgXg9gdgpgKgQwOYgFwgFoHkByBRAfQEkAREAGhAGcAXBAJxrRACYAGFgNgFo2eBmNnDYBGVGwDsqEQBYMFEDCgATZuy68BQyeKmz5AXyA)

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

* [Email logs](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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/observability/metrics-analytics/#page","headline":"Metrics and analytics · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/observability/","name":"Observability and logs"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/observability/metrics-analytics/","name":"Metrics and analytics"}}]}
```

---

# Email Routing

## Get Email Routing settings

**get** `/zones/{zone_id}/email/routing`

Get information about the settings for your Email Routing zone.

### Path Parameters

- `zone_id: string`

  Identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Disable Email Routing

**post** `/zones/{zone_id}/email/routing/disable`

Disable your Email Routing zone. Also removes additional MX records previously required for Email Routing to work.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `body: unknown`

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/disable \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{}'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Enable Email Routing

**post** `/zones/{zone_id}/email/routing/enable`

Enable you Email Routing zone. Add and lock the necessary MX and SPF records.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `body: unknown`

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/enable \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{}'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Unlock Email Routing

**post** `/zones/{zone_id}/email/routing/unlock`

Unlock MX records previously locked by Email Routing. Deprecated - use PATCH /zones/{zone_id}/email/routing/dns instead.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `name: optional string`

  Domain of your zone.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/unlock \
    -X POST \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Domain Types

### Settings

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

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

# DNS

## Email Routing - DNS settings

**get** `/zones/{zone_id}/email/routing/dns`

Show the DNS records needed to configure your Email Routing zone.

### Path Parameters

- `zone_id: string`

  Identifier.

### Query Parameters

- `subdomain: optional string`

  Domain of your zone.

### Returns

- `EmailEmailRoutingDNSQueryResponse object { errors, messages, success, 2 more }`

  - `errors: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `messages: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `success: true`

    Whether the API call was successful.

    - `true`

  - `result: optional object { errors, record }`

    - `errors: optional array of object { code, missing }`

      - `code: optional string`

      - `missing: optional DNSRecord`

        List of records needed to enable an Email Routing zone.

        - `content: optional string`

          DNS record content.

        - `name: optional string`

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

        - `priority: optional number`

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

        - `ttl: optional number or 1`

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

          - `number`

          - `1`

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

            - `1`

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

          DNS record type.

          - `"A"`

          - `"AAAA"`

          - `"CNAME"`

          - `"HTTPS"`

          - `"TXT"`

          - `"SRV"`

          - `"LOC"`

          - `"MX"`

          - `"NS"`

          - `"CERT"`

          - `"DNSKEY"`

          - `"DS"`

          - `"NAPTR"`

          - `"SMIMEA"`

          - `"SSHFP"`

          - `"SVCB"`

          - `"TLSA"`

          - `"URI"`

    - `record: optional array of DNSRecord`

      - `content: optional string`

        DNS record content.

      - `name: optional string`

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

      - `priority: optional number`

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

      - `ttl: optional number or 1`

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

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

        DNS record type.

  - `result_info: optional object { count, page, per_page, 2 more }`

    - `count: optional number`

      Total number of results for the requested service.

    - `page: optional number`

      Current page within paginated list of results.

    - `per_page: optional number`

      Number of results per page of results.

    - `total_count: optional number`

      Total results available without any search parameters.

    - `total_pages: optional number`

      The number of total pages in the entire result set.

- `EmailDNSSettingsResponseCollection object { errors, messages, success, 2 more }`

  - `errors: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `messages: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `success: true`

    Whether the API call was successful.

    - `true`

  - `result: optional array of DNSRecord`

    - `content: optional string`

      DNS record content.

    - `name: optional string`

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

    - `priority: optional number`

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

    - `ttl: optional number or 1`

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

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

      DNS record type.

  - `result_info: optional object { count, page, per_page, 2 more }`

    - `count: optional number`

      Total number of results for the requested service.

    - `page: optional number`

      Current page within paginated list of results.

    - `per_page: optional number`

      Number of results per page of results.

    - `total_count: optional number`

      Total results available without any search parameters.

    - `total_pages: optional number`

      The number of total pages in the entire result set.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/dns \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "errors": [
      {
        "code": "code",
        "missing": {
          "content": "route1.mx.cloudflare.net",
          "name": "example.com",
          "priority": 12,
          "ttl": 1,
          "type": "NS"
        }
      }
    ],
    "record": [
      {
        "content": "route1.mx.cloudflare.net",
        "name": "example.com",
        "priority": 12,
        "ttl": 1,
        "type": "NS"
      }
    ]
  },
  "result_info": {
    "count": 1,
    "page": 1,
    "per_page": 20,
    "total_count": 2000,
    "total_pages": 100
  }
}
```

## Enable Email Routing

**post** `/zones/{zone_id}/email/routing/dns`

Enable you Email Routing zone. Add and lock the necessary MX and SPF records.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `name: optional string`

  Domain of your zone.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/dns \
    -X POST \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Unlock Email Routing

**patch** `/zones/{zone_id}/email/routing/dns`

Unlock MX Records previously locked by Email Routing.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `name: optional string`

  Domain of your zone.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Settings`

  - `id: string`

    Email Routing settings identifier.

  - `enabled: true or false`

    State of the zone settings for Email Routing.

    - `true`

    - `false`

  - `name: string`

    Domain of your zone.

  - `created: optional string`

    The date and time the settings have been created.

  - `modified: optional string`

    The date and time the settings have been modified.

  - `skip_wizard: optional true or false`

    Flag to check if the user skipped the configuration wizard.

    - `true`

    - `false`

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

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

    - `"ready"`

    - `"unconfigured"`

    - `"misconfigured"`

    - `"misconfigured/locked"`

    - `"unlocked"`

  - `support_subaddress: optional true or false`

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

    - `true`

    - `false`

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/dns \
    -X PATCH \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "75610dab9e69410a82cf7e400a09ecec",
    "enabled": true,
    "name": "example.net",
    "created": "2014-01-02T02:20:00Z",
    "modified": "2014-01-02T02:20:00Z",
    "skip_wizard": true,
    "status": "ready",
    "support_subaddress": true,
    "tag": "75610dab9e69410a82cf7e400a09ecec"
  }
}
```

## Disable Email Routing

**delete** `/zones/{zone_id}/email/routing/dns`

Disable your Email Routing zone. Also removes additional MX records previously required for Email Routing to work.

### Path Parameters

- `zone_id: string`

  Identifier.

### Returns

- `EmailAPIResponseCommon object { errors, messages, success }`

  - `errors: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `messages: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `success: true`

    Whether the API call was successful.

    - `true`

- `EmailDNSSettingsResponseCollection object { errors, messages, success, 2 more }`

  - `errors: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `messages: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `success: true`

    Whether the API call was successful.

    - `true`

  - `result: optional array of DNSRecord`

    - `content: optional string`

      DNS record content.

    - `name: optional string`

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

    - `priority: optional number`

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

    - `ttl: optional number or 1`

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

      - `number`

      - `1`

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

        - `1`

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

      DNS record type.

      - `"A"`

      - `"AAAA"`

      - `"CNAME"`

      - `"HTTPS"`

      - `"TXT"`

      - `"SRV"`

      - `"LOC"`

      - `"MX"`

      - `"NS"`

      - `"CERT"`

      - `"DNSKEY"`

      - `"DS"`

      - `"NAPTR"`

      - `"SMIMEA"`

      - `"SSHFP"`

      - `"SVCB"`

      - `"TLSA"`

      - `"URI"`

  - `result_info: optional object { count, page, per_page, 2 more }`

    - `count: optional number`

      Total number of results for the requested service.

    - `page: optional number`

      Current page within paginated list of results.

    - `per_page: optional number`

      Number of results per page of results.

    - `total_count: optional number`

      Total results available without any search parameters.

    - `total_pages: optional number`

      The number of total pages in the entire result set.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/dns \
    -X DELETE \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true
}
```

## Domain Types

### DNS Record

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

  List of records needed to enable an Email Routing zone.

  - `content: optional string`

    DNS record content.

  - `name: optional string`

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

  - `priority: optional number`

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

  - `ttl: optional number or 1`

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

    - `number`

    - `1`

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

      - `1`

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

    DNS record type.

    - `"A"`

    - `"AAAA"`

    - `"CNAME"`

    - `"HTTPS"`

    - `"TXT"`

    - `"SRV"`

    - `"LOC"`

    - `"MX"`

    - `"NS"`

    - `"CERT"`

    - `"DNSKEY"`

    - `"DS"`

    - `"NAPTR"`

    - `"SMIMEA"`

    - `"SSHFP"`

    - `"SVCB"`

    - `"TLSA"`

    - `"URI"`

### DNS Get Response

- `DNSGetResponse = object { errors, messages, success, 2 more }  or object { errors, messages, success, 2 more }`

  - `EmailEmailRoutingDNSQueryResponse object { errors, messages, success, 2 more }`

    - `errors: array of object { code, message, documentation_url, source }`

      - `code: number`

      - `message: string`

      - `documentation_url: optional string`

      - `source: optional object { pointer }`

        - `pointer: optional string`

    - `messages: array of object { code, message, documentation_url, source }`

      - `code: number`

      - `message: string`

      - `documentation_url: optional string`

      - `source: optional object { pointer }`

        - `pointer: optional string`

    - `success: true`

      Whether the API call was successful.

      - `true`

    - `result: optional object { errors, record }`

      - `errors: optional array of object { code, missing }`

        - `code: optional string`

        - `missing: optional DNSRecord`

          List of records needed to enable an Email Routing zone.

          - `content: optional string`

            DNS record content.

          - `name: optional string`

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

          - `priority: optional number`

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

          - `ttl: optional number or 1`

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

            - `number`

            - `1`

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

              - `1`

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

            DNS record type.

            - `"A"`

            - `"AAAA"`

            - `"CNAME"`

            - `"HTTPS"`

            - `"TXT"`

            - `"SRV"`

            - `"LOC"`

            - `"MX"`

            - `"NS"`

            - `"CERT"`

            - `"DNSKEY"`

            - `"DS"`

            - `"NAPTR"`

            - `"SMIMEA"`

            - `"SSHFP"`

            - `"SVCB"`

            - `"TLSA"`

            - `"URI"`

      - `record: optional array of DNSRecord`

        - `content: optional string`

          DNS record content.

        - `name: optional string`

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

        - `priority: optional number`

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

        - `ttl: optional number or 1`

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

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

          DNS record type.

    - `result_info: optional object { count, page, per_page, 2 more }`

      - `count: optional number`

        Total number of results for the requested service.

      - `page: optional number`

        Current page within paginated list of results.

      - `per_page: optional number`

        Number of results per page of results.

      - `total_count: optional number`

        Total results available without any search parameters.

      - `total_pages: optional number`

        The number of total pages in the entire result set.

  - `EmailDNSSettingsResponseCollection object { errors, messages, success, 2 more }`

    - `errors: array of object { code, message, documentation_url, source }`

      - `code: number`

      - `message: string`

      - `documentation_url: optional string`

      - `source: optional object { pointer }`

        - `pointer: optional string`

    - `messages: array of object { code, message, documentation_url, source }`

      - `code: number`

      - `message: string`

      - `documentation_url: optional string`

      - `source: optional object { pointer }`

        - `pointer: optional string`

    - `success: true`

      Whether the API call was successful.

      - `true`

    - `result: optional array of DNSRecord`

      - `content: optional string`

        DNS record content.

      - `name: optional string`

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

      - `priority: optional number`

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

      - `ttl: optional number or 1`

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

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

        DNS record type.

    - `result_info: optional object { count, page, per_page, 2 more }`

      - `count: optional number`

        Total number of results for the requested service.

      - `page: optional number`

        Current page within paginated list of results.

      - `per_page: optional number`

        Number of results per page of results.

      - `total_count: optional number`

        Total results available without any search parameters.

      - `total_pages: optional number`

        The number of total pages in the entire result set.

# Rules

## Get routing rule

**get** `/zones/{zone_id}/email/routing/rules/{rule_identifier}`

Get information for a specific routing rule already created.

### Path Parameters

- `zone_id: string`

  Identifier.

- `rule_identifier: string`

  Routing rule identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional EmailRoutingRule`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of Action`

    List actions patterns.

    - `type: "drop" or "forward" or "worker"`

      Type of supported action.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of Matcher`

    Matching patterns to forward to your actions.

    - `type: "all" or "literal"`

      Type of matcher.

      - `"all"`

      - `"literal"`

    - `field: optional "to"`

      Field for type matcher.

      - `"to"`

    - `value: optional string`

      Value for matcher.

  - `name: optional string`

    Routing rule name.

  - `priority: optional number`

    Priority of the routing rule.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules/$RULE_IDENTIFIER \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "literal",
        "field": "to",
        "value": "test@example.com"
      }
    ],
    "name": "Send to user@example.net rule.",
    "priority": 0,
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Create routing rule

**post** `/zones/{zone_id}/email/routing/rules`

Rules consist of a set of criteria for matching emails (such as an email being sent to a specific custom email address) plus a set of actions to take on the email (like forwarding it to a specific destination address). Forward actions require all destination addresses to be verified.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `actions: array of Action`

  List actions patterns.

  - `type: "drop" or "forward" or "worker"`

    Type of supported action.

    - `"drop"`

    - `"forward"`

    - `"worker"`

  - `value: optional array of string`

- `matchers: array of Matcher`

  Matching patterns to forward to your actions.

  - `type: "all" or "literal"`

    Type of matcher.

    - `"all"`

    - `"literal"`

  - `field: optional "to"`

    Field for type matcher.

    - `"to"`

  - `value: optional string`

    Value for matcher.

- `enabled: optional true or false`

  Routing rule status.

  - `true`

  - `false`

- `name: optional string`

  Routing rule name.

- `priority: optional number`

  Priority of the routing rule.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional EmailRoutingRule`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of Action`

    List actions patterns.

    - `type: "drop" or "forward" or "worker"`

      Type of supported action.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of Matcher`

    Matching patterns to forward to your actions.

    - `type: "all" or "literal"`

      Type of matcher.

      - `"all"`

      - `"literal"`

    - `field: optional "to"`

      Field for type matcher.

      - `"to"`

    - `value: optional string`

      Value for matcher.

  - `name: optional string`

    Routing rule name.

  - `priority: optional number`

    Priority of the routing rule.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "actions": [
            {
              "type": "forward"
            }
          ],
          "matchers": [
            {
              "type": "literal"
            }
          ],
          "enabled": true,
          "name": "Send to user@example.net rule."
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "literal",
        "field": "to",
        "value": "test@example.com"
      }
    ],
    "name": "Send to user@example.net rule.",
    "priority": 0,
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Update routing rule

**put** `/zones/{zone_id}/email/routing/rules/{rule_identifier}`

Update actions and matches, or enable/disable specific routing rules. Forward actions require all destination addresses to be verified.

### Path Parameters

- `zone_id: string`

  Identifier.

- `rule_identifier: string`

  Routing rule identifier.

### Body Parameters

- `actions: array of Action`

  List actions patterns.

  - `type: "drop" or "forward" or "worker"`

    Type of supported action.

    - `"drop"`

    - `"forward"`

    - `"worker"`

  - `value: optional array of string`

- `matchers: array of Matcher`

  Matching patterns to forward to your actions.

  - `type: "all" or "literal"`

    Type of matcher.

    - `"all"`

    - `"literal"`

  - `field: optional "to"`

    Field for type matcher.

    - `"to"`

  - `value: optional string`

    Value for matcher.

- `enabled: optional true or false`

  Routing rule status.

  - `true`

  - `false`

- `name: optional string`

  Routing rule name.

- `priority: optional number`

  Priority of the routing rule.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional EmailRoutingRule`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of Action`

    List actions patterns.

    - `type: "drop" or "forward" or "worker"`

      Type of supported action.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of Matcher`

    Matching patterns to forward to your actions.

    - `type: "all" or "literal"`

      Type of matcher.

      - `"all"`

      - `"literal"`

    - `field: optional "to"`

      Field for type matcher.

      - `"to"`

    - `value: optional string`

      Value for matcher.

  - `name: optional string`

    Routing rule name.

  - `priority: optional number`

    Priority of the routing rule.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules/$RULE_IDENTIFIER \
    -X PUT \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "actions": [
            {
              "type": "forward"
            }
          ],
          "matchers": [
            {
              "type": "literal"
            }
          ],
          "enabled": true,
          "name": "Send to user@example.net rule."
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "literal",
        "field": "to",
        "value": "test@example.com"
      }
    ],
    "name": "Send to user@example.net rule.",
    "priority": 0,
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Delete routing rule

**delete** `/zones/{zone_id}/email/routing/rules/{rule_identifier}`

Delete a specific routing rule.

### Path Parameters

- `zone_id: string`

  Identifier.

- `rule_identifier: string`

  Routing rule identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional EmailRoutingRule`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of Action`

    List actions patterns.

    - `type: "drop" or "forward" or "worker"`

      Type of supported action.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of Matcher`

    Matching patterns to forward to your actions.

    - `type: "all" or "literal"`

      Type of matcher.

      - `"all"`

      - `"literal"`

    - `field: optional "to"`

      Field for type matcher.

      - `"to"`

    - `value: optional string`

      Value for matcher.

  - `name: optional string`

    Routing rule name.

  - `priority: optional number`

    Priority of the routing rule.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules/$RULE_IDENTIFIER \
    -X DELETE \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "literal",
        "field": "to",
        "value": "test@example.com"
      }
    ],
    "name": "Send to user@example.net rule.",
    "priority": 0,
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Domain Types

### Action

- `Action object { type, value }`

  Actions pattern.

  - `type: "drop" or "forward" or "worker"`

    Type of supported action.

    - `"drop"`

    - `"forward"`

    - `"worker"`

  - `value: optional array of string`

### Email Routing Rule

- `EmailRoutingRule object { id, actions, enabled, 4 more }`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of Action`

    List actions patterns.

    - `type: "drop" or "forward" or "worker"`

      Type of supported action.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of Matcher`

    Matching patterns to forward to your actions.

    - `type: "all" or "literal"`

      Type of matcher.

      - `"all"`

      - `"literal"`

    - `field: optional "to"`

      Field for type matcher.

      - `"to"`

    - `value: optional string`

      Value for matcher.

  - `name: optional string`

    Routing rule name.

  - `priority: optional number`

    Priority of the routing rule.

  - `tag: optional string`

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

### Matcher

- `Matcher object { type, field, value }`

  Matching pattern to forward your actions.

  - `type: "all" or "literal"`

    Type of matcher.

    - `"all"`

    - `"literal"`

  - `field: optional "to"`

    Field for type matcher.

    - `"to"`

  - `value: optional string`

    Value for matcher.

# Catch Alls

## Get catch-all rule

**get** `/zones/{zone_id}/email/routing/rules/catch_all`

Get information on the default catch-all routing rule.

### Path Parameters

- `zone_id: string`

  Identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional object { id, actions, enabled, 3 more }`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of CatchAllAction`

    List actions for the catch-all routing rule.

    - `type: "drop" or "forward" or "worker"`

      Type of action for catch-all rule.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of CatchAllMatcher`

    List of matchers for the catch-all routing rule.

    - `type: "all"`

      Type of matcher. Default is 'all'.

      - `"all"`

  - `name: optional string`

    Routing rule name.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules/catch_all \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "all"
      }
    ],
    "name": "Send to user@example.net rule.",
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Update catch-all rule

**put** `/zones/{zone_id}/email/routing/rules/catch_all`

Enable or disable catch-all routing rule, or change action to forward to specific destination address. Forward actions require all destination addresses to be verified.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `actions: array of CatchAllAction`

  List actions for the catch-all routing rule.

  - `type: "drop" or "forward" or "worker"`

    Type of action for catch-all rule.

    - `"drop"`

    - `"forward"`

    - `"worker"`

  - `value: optional array of string`

- `matchers: array of CatchAllMatcher`

  List of matchers for the catch-all routing rule.

  - `type: "all"`

    Type of matcher. Default is 'all'.

    - `"all"`

- `enabled: optional true or false`

  Routing rule status.

  - `true`

  - `false`

- `name: optional string`

  Routing rule name.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional object { id, actions, enabled, 3 more }`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of CatchAllAction`

    List actions for the catch-all routing rule.

    - `type: "drop" or "forward" or "worker"`

      Type of action for catch-all rule.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of CatchAllMatcher`

    List of matchers for the catch-all routing rule.

    - `type: "all"`

      Type of matcher. Default is 'all'.

      - `"all"`

  - `name: optional string`

    Routing rule name.

  - `tag: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules/catch_all \
    -X PUT \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "actions": [
            {
              "type": "forward"
            }
          ],
          "matchers": [
            {
              "type": "all"
            }
          ],
          "enabled": true,
          "name": "Send to user@example.net rule."
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "a7e6fb77503c41d8a7f3113c6918f10c",
    "actions": [
      {
        "type": "forward",
        "value": [
          "destinationaddress@example.net"
        ]
      }
    ],
    "enabled": true,
    "matchers": [
      {
        "type": "all"
      }
    ],
    "name": "Send to user@example.net rule.",
    "tag": "a7e6fb77503c41d8a7f3113c6918f10c"
  }
}
```

## Domain Types

### Catch All Action

- `CatchAllAction object { type, value }`

  Action for the catch-all routing rule.

  - `type: "drop" or "forward" or "worker"`

    Type of action for catch-all rule.

    - `"drop"`

    - `"forward"`

    - `"worker"`

  - `value: optional array of string`

### Catch All Matcher

- `CatchAllMatcher object { type }`

  Matcher for catch-all routing rule.

  - `type: "all"`

    Type of matcher. Default is 'all'.

    - `"all"`

### Catch All Get Response

- `CatchAllGetResponse object { id, actions, enabled, 3 more }`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of CatchAllAction`

    List actions for the catch-all routing rule.

    - `type: "drop" or "forward" or "worker"`

      Type of action for catch-all rule.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of CatchAllMatcher`

    List of matchers for the catch-all routing rule.

    - `type: "all"`

      Type of matcher. Default is 'all'.

      - `"all"`

  - `name: optional string`

    Routing rule name.

  - `tag: optional string`

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

### Catch All Update Response

- `CatchAllUpdateResponse object { id, actions, enabled, 3 more }`

  - `id: optional string`

    Routing rule identifier.

  - `actions: optional array of CatchAllAction`

    List actions for the catch-all routing rule.

    - `type: "drop" or "forward" or "worker"`

      Type of action for catch-all rule.

      - `"drop"`

      - `"forward"`

      - `"worker"`

    - `value: optional array of string`

  - `enabled: optional true or false`

    Routing rule status.

    - `true`

    - `false`

  - `matchers: optional array of CatchAllMatcher`

    List of matchers for the catch-all routing rule.

    - `type: "all"`

      Type of matcher. Default is 'all'.

      - `"all"`

  - `name: optional string`

    Routing rule name.

  - `tag: optional string`

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

# Addresses

## List destination addresses

**get** `/accounts/{account_id}/email/routing/addresses`

Lists existing destination addresses.

### Path Parameters

- `account_id: string`

  Identifier.

### Query Parameters

- `direction: optional "asc" or "desc"`

  Sorts results in an ascending or descending order.

  - `"asc"`

  - `"desc"`

- `page: optional number`

  Page number of paginated results.

- `per_page: optional number`

  Maximum number of results per page.

- `verified: optional true or false`

  Filter by verified destination addresses.

  - `true`

  - `false`

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional array of Address`

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

- `result_info: optional object { count, page, per_page, 2 more }`

  - `count: optional number`

    Total number of results for the requested service.

  - `page: optional number`

    Current page within paginated list of results.

  - `per_page: optional number`

    Number of results per page of results.

  - `total_count: optional number`

    Total results available without any search parameters.

  - `total_pages: optional number`

    The number of total pages in the entire result set.

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/routing/addresses \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": [
    {
      "id": "ea95132c15732412d22c1476fa83f27a",
      "created": "2014-01-02T02:20:00Z",
      "email": "user@example.com",
      "modified": "2014-01-02T02:20:00Z",
      "tag": "ea95132c15732412d22c1476fa83f27a",
      "verified": "2014-01-02T02:20:00Z"
    }
  ],
  "result_info": {
    "count": 1,
    "page": 1,
    "per_page": 20,
    "total_count": 1,
    "total_pages": 100
  }
}
```

## Get a destination address

**get** `/accounts/{account_id}/email/routing/addresses/{destination_address_identifier}`

Gets information for a specific destination email already created.

### Path Parameters

- `account_id: string`

  Identifier.

- `destination_address_identifier: string`

  Destination address identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Address`

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/routing/addresses/$DESTINATION_ADDRESS_IDENTIFIER \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "ea95132c15732412d22c1476fa83f27a",
    "created": "2014-01-02T02:20:00Z",
    "email": "user@example.com",
    "modified": "2014-01-02T02:20:00Z",
    "tag": "ea95132c15732412d22c1476fa83f27a",
    "verified": "2014-01-02T02:20:00Z"
  }
}
```

## Create a destination address

**post** `/accounts/{account_id}/email/routing/addresses`

Create a destination address to forward your emails to. Destination addresses need to be verified before they can be used.

### Path Parameters

- `account_id: string`

  Identifier.

### Body Parameters

- `email: string`

  The contact email address of the user.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Address`

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/routing/addresses \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "email": "user@example.com"
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "ea95132c15732412d22c1476fa83f27a",
    "created": "2014-01-02T02:20:00Z",
    "email": "user@example.com",
    "modified": "2014-01-02T02:20:00Z",
    "tag": "ea95132c15732412d22c1476fa83f27a",
    "verified": "2014-01-02T02:20:00Z"
  }
}
```

## Update destination address

**patch** `/accounts/{account_id}/email/routing/addresses/{destination_address_identifier}`

Updates the status of a specific destination address.

### Path Parameters

- `account_id: string`

  Identifier.

- `destination_address_identifier: string`

  Destination address identifier.

### Body Parameters

- `status: "unverified" or "verified"`

  Destination address status. Non-admin callers may only set verified addresses back to unverified; setting to verified requires admin privileges.

  - `"unverified"`

  - `"verified"`

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Address`

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/routing/addresses/$DESTINATION_ADDRESS_IDENTIFIER \
    -X PATCH \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "status": "verified"
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "ea95132c15732412d22c1476fa83f27a",
    "created": "2014-01-02T02:20:00Z",
    "email": "user@example.com",
    "modified": "2014-01-02T02:20:00Z",
    "tag": "ea95132c15732412d22c1476fa83f27a",
    "verified": "2014-01-02T02:20:00Z"
  }
}
```

## Delete destination address

**delete** `/accounts/{account_id}/email/routing/addresses/{destination_address_identifier}`

Deletes a specific destination address.

### Path Parameters

- `account_id: string`

  Identifier.

- `destination_address_identifier: string`

  Destination address identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional Address`

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/routing/addresses/$DESTINATION_ADDRESS_IDENTIFIER \
    -X DELETE \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "id": "ea95132c15732412d22c1476fa83f27a",
    "created": "2014-01-02T02:20:00Z",
    "email": "user@example.com",
    "modified": "2014-01-02T02:20:00Z",
    "tag": "ea95132c15732412d22c1476fa83f27a",
    "verified": "2014-01-02T02:20:00Z"
  }
}
```

## Domain Types

### Address

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

  - `id: optional string`

    Destination address identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `email: optional string`

    The contact email address of the user.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `tag: optional string`

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

  - `verified: optional string`

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

---

# Email Sending

## Send an email

**post** `/accounts/{account_id}/email/sending/send`

Send an email

### Path Parameters

- `account_id: string`

  Identifier of the account.

### Body Parameters

- `from: string or object { address, name }`

  Sender email address. Either a plain string or an object with address and name.

  - `EmailSendingEmailAddressString = string`

    An email address as a plain string.

  - `EmailSendingEmailAddressObject object { address, name }`

    - `address: string`

      Email address (e.g., 'user@example.com').

    - `name: string`

      Display name for the email address (e.g., 'John Doe').

- `subject: string`

  Email subject line.

- `attachments: optional array of object { content, content_id, disposition, 2 more }  or object { content, disposition, filename, type }`

  File attachments and inline images.

  - `Inline object { content, content_id, disposition, 2 more }`

    - `content: string`

      Base64-encoded content of the attachment.

    - `content_id: string`

      Content ID used to reference this attachment in HTML via cid: URI (e.g., <img src="cid:logo">).

    - `disposition: "inline"`

      Must be 'inline'. Indicates the attachment is embedded in the email body.

      - `"inline"`

    - `filename: string`

      Filename for the attachment.

    - `type: string`

      MIME type of the attachment (e.g., 'image/png', 'text/plain').

  - `Attachment object { content, disposition, filename, type }`

    - `content: string`

      Base64-encoded content of the attachment.

    - `disposition: "attachment"`

      Must be 'attachment'. Indicates a standard file attachment.

      - `"attachment"`

    - `filename: string`

      Filename for the attachment.

    - `type: string`

      MIME type of the attachment (e.g., 'application/pdf', 'text/plain').

- `bcc: optional string or array of string`

  BCC recipient(s). A single email string or an array of email strings.

  - `EmailSendingEmailAddressString = string`

    An email address as a plain string.

  - `EmailSendingEmailAddressList = array of string`

    A list of email address strings.

- `cc: optional string or array of string`

  CC recipient(s). A single email string or an array of email strings.

  - `EmailSendingEmailAddressString = string`

    An email address as a plain string.

  - `EmailSendingEmailAddressList = array of string`

    A list of email address strings.

- `headers: optional map[string]`

  Custom email headers as key-value pairs.

- `html: optional string`

  HTML body of the email. At least one of text or html must be provided (non-empty).

- `reply_to: optional string or object { address, name }`

  Reply-to address. Either a plain string or an object with address and name.

  - `EmailSendingEmailAddressString = string`

    An email address as a plain string.

  - `EmailSendingEmailAddressObject object { address, name }`

    - `address: string`

      Email address (e.g., 'user@example.com').

    - `name: string`

      Display name for the email address (e.g., 'John Doe').

- `text: optional string`

  Plain text body of the email. At least one of text or html must be provided (non-empty).

- `to: optional string or array of string`

  Recipient(s). Optional if cc or bcc is provided. A single email string or an array of email strings.

  - `EmailSendingEmailAddressString = string`

    An email address as a plain string.

  - `EmailSendingEmailAddressList = array of string`

    A list of email address strings.

### Returns

- `errors: array of object { code, message }`

  - `code: number`

  - `message: string`

- `messages: array of object { code, message }`

  - `code: number`

  - `message: string`

- `result: object { delivered, message_id, permanent_bounces, queued }`

  - `delivered: array of string`

    Email addresses to which the message was delivered immediately.

  - `message_id: string`

    Message ID of the sent email.

  - `permanent_bounces: array of string`

    Email addresses that permanently bounced.

  - `queued: array of string`

    Email addresses for which delivery was queued for later.

- `success: true`

  - `true`

- `result_info: optional object { count, per_page, total_count, 2 more }`

  - `count: number`

  - `per_page: number`

  - `total_count: number`

  - `cursor: optional string`

  - `page: optional number`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/send \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "from": "sender@example.com",
          "subject": "Monthly Report",
          "headers": {
            "X-Custom-Header": "value"
          },
          "html": "<h1>Hello</h1><p>Please find your report attached.</p>",
          "text": "Hello\\n\\nPlease find your report attached."
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 0,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 0,
      "message": "message"
    }
  ],
  "result": {
    "delivered": [
      "recipient@example.com"
    ],
    "message_id": "<aB3xK9mP2qR5sT8uV0wX1yZ4cD6fG7hJ9kL0@example.com>",
    "permanent_bounces": [
      "string"
    ],
    "queued": [
      "string"
    ]
  },
  "success": true,
  "result_info": {
    "count": 0,
    "per_page": 0,
    "total_count": 0,
    "cursor": "cursor",
    "page": 0
  }
}
```

## Send a raw MIME email

**post** `/accounts/{account_id}/email/sending/send_raw`

Send a raw MIME email

### Path Parameters

- `account_id: string`

  Identifier of the account.

### Body Parameters

- `from: string`

  Sender email address.

- `mime_message: string`

  The full MIME-encoded email message. Should include standard RFC 5322 headers such as From, To, Subject, and Content-Type. The from and recipients fields in the request body control SMTP envelope routing; the From and To headers in the MIME message control what the recipient's email client displays.

- `recipients: array of string`

  List of recipient email addresses.

### Returns

- `errors: array of object { code, message }`

  - `code: number`

  - `message: string`

- `messages: array of object { code, message }`

  - `code: number`

  - `message: string`

- `result: object { delivered, message_id, permanent_bounces, queued }`

  - `delivered: array of string`

    Email addresses to which the message was delivered immediately.

  - `message_id: string`

    Message ID of the sent email.

  - `permanent_bounces: array of string`

    Email addresses that permanently bounced.

  - `queued: array of string`

    Email addresses for which delivery was queued for later.

- `success: true`

  - `true`

- `result_info: optional object { count, per_page, total_count, 2 more }`

  - `count: number`

  - `per_page: number`

  - `total_count: number`

  - `cursor: optional string`

  - `page: optional number`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/send_raw \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "from": "sender@example.com",
          "mime_message": "From: sender@example.com\\r\\nTo: recipient@example.com\\r\\nSubject: Hello\\r\\nContent-Type: text/plain\\r\\n\\r\\nHello, World!",
          "recipients": [
            "recipient@example.com"
          ]
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 0,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 0,
      "message": "message"
    }
  ],
  "result": {
    "delivered": [
      "recipient@example.com"
    ],
    "message_id": "<aB3xK9mP2qR5sT8uV0wX1yZ4cD6fG7hJ9kL0@example.com>",
    "permanent_bounces": [
      "string"
    ],
    "queued": [
      "string"
    ]
  },
  "success": true,
  "result_info": {
    "count": 0,
    "per_page": 0,
    "total_count": 0,
    "cursor": "cursor",
    "page": 0
  }
}
```

## Domain Types

### Email Sending Send Response

- `EmailSendingSendResponse object { delivered, message_id, permanent_bounces, queued }`

  - `delivered: array of string`

    Email addresses to which the message was delivered immediately.

  - `message_id: string`

    Message ID of the sent email.

  - `permanent_bounces: array of string`

    Email addresses that permanently bounced.

  - `queued: array of string`

    Email addresses for which delivery was queued for later.

### Email Sending Send Raw Response

- `EmailSendingSendRawResponse object { delivered, message_id, permanent_bounces, queued }`

  - `delivered: array of string`

    Email addresses to which the message was delivered immediately.

  - `message_id: string`

    Message ID of the sent email.

  - `permanent_bounces: array of string`

    Email addresses that permanently bounced.

  - `queued: array of string`

    Email addresses for which delivery was queued for later.

# Subdomains

## List sending subdomains

**get** `/zones/{zone_id}/email/sending/subdomains`

Lists all sending-enabled subdomains for the zone.

### Path Parameters

- `zone_id: string`

  Identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional array of object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

- `result_info: optional object { count, page, per_page, 2 more }`

  - `count: optional number`

    Total number of results for the requested service.

  - `page: optional number`

    Current page within paginated list of results.

  - `per_page: optional number`

    Number of results per page of results.

  - `total_count: optional number`

    Total results available without any search parameters.

  - `total_pages: optional number`

    The number of total pages in the entire result set.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/sending/subdomains \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": [
    {
      "enabled": true,
      "name": "sub.example.com",
      "tag": "aabbccdd11223344aabbccdd11223344",
      "created": "2014-01-02T02:20:00Z",
      "dkim_selector": "cf-bounce",
      "modified": "2014-01-02T02:20:00Z",
      "return_path_domain": "cf-bounce.sub.example.com"
    }
  ],
  "result_info": {
    "count": 1,
    "page": 1,
    "per_page": 20,
    "total_count": 2000,
    "total_pages": 100
  }
}
```

## Get a sending subdomain

**get** `/zones/{zone_id}/email/sending/subdomains/{subdomain_id}`

Gets information for a specific sending subdomain.

### Path Parameters

- `zone_id: string`

  Identifier.

- `subdomain_id: string`

  Sending subdomain identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/sending/subdomains/$SUBDOMAIN_ID \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "enabled": true,
    "name": "sub.example.com",
    "tag": "aabbccdd11223344aabbccdd11223344",
    "created": "2014-01-02T02:20:00Z",
    "dkim_selector": "cf-bounce",
    "modified": "2014-01-02T02:20:00Z",
    "return_path_domain": "cf-bounce.sub.example.com"
  }
}
```

## Create a sending subdomain

**post** `/zones/{zone_id}/email/sending/subdomains`

Creates a new sending subdomain or re-enables sending on an existing subdomain that had it disabled. If zone-level Email Sending has not been enabled yet, the zone flag is automatically set when the entitlement is present.

### Path Parameters

- `zone_id: string`

  Identifier.

### Body Parameters

- `name: string`

  The subdomain name. Must be within the zone.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/sending/subdomains \
    -H 'Content-Type: application/json' \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
    -d '{
          "name": "sub.example.com"
        }'
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": {
    "enabled": true,
    "name": "sub.example.com",
    "tag": "aabbccdd11223344aabbccdd11223344",
    "created": "2014-01-02T02:20:00Z",
    "dkim_selector": "cf-bounce",
    "modified": "2014-01-02T02:20:00Z",
    "return_path_domain": "cf-bounce.sub.example.com"
  }
}
```

## Delete a sending subdomain

**delete** `/zones/{zone_id}/email/sending/subdomains/{subdomain_id}`

Disables sending on a subdomain and removes its DNS records. If routing is still active on the subdomain, only sending is disabled.

### Path Parameters

- `zone_id: string`

  Identifier.

- `subdomain_id: string`

  Sending subdomain identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/sending/subdomains/$SUBDOMAIN_ID \
    -X DELETE \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true
}
```

## Domain Types

### Subdomain List Response

- `SubdomainListResponse object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

### Subdomain Get Response

- `SubdomainGetResponse object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

### Subdomain Create Response

- `SubdomainCreateResponse object { enabled, name, tag, 4 more }`

  - `enabled: boolean`

    Whether Email Sending is enabled on this subdomain.

  - `name: string`

    The subdomain domain name.

  - `tag: string`

    Sending subdomain identifier.

  - `created: optional string`

    The date and time the destination address has been created.

  - `dkim_selector: optional string`

    The DKIM selector used for email signing.

  - `modified: optional string`

    The date and time the destination address was last modified.

  - `return_path_domain: optional string`

    The return-path domain used for bounce handling.

### Subdomain Delete Response

- `SubdomainDeleteResponse object { errors, messages, success }`

  - `errors: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `messages: array of object { code, message, documentation_url, source }`

    - `code: number`

    - `message: string`

    - `documentation_url: optional string`

    - `source: optional object { pointer }`

      - `pointer: optional string`

  - `success: true`

    Whether the API call was successful.

    - `true`

# DNS

## Get sending subdomain DNS records

**get** `/zones/{zone_id}/email/sending/subdomains/{subdomain_id}/dns`

Returns the expected DNS records for a sending subdomain.

### Path Parameters

- `zone_id: string`

  Identifier.

- `subdomain_id: string`

  Sending subdomain identifier.

### Returns

- `errors: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `messages: array of object { code, message, documentation_url, source }`

  - `code: number`

  - `message: string`

  - `documentation_url: optional string`

  - `source: optional object { pointer }`

    - `pointer: optional string`

- `success: true`

  Whether the API call was successful.

  - `true`

- `result: optional array of DNSRecord`

  - `content: optional string`

    DNS record content.

  - `name: optional string`

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

  - `priority: optional number`

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

  - `ttl: optional number or 1`

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

    - `number`

    - `1`

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

      - `1`

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

    DNS record type.

    - `"A"`

    - `"AAAA"`

    - `"CNAME"`

    - `"HTTPS"`

    - `"TXT"`

    - `"SRV"`

    - `"LOC"`

    - `"MX"`

    - `"NS"`

    - `"CERT"`

    - `"DNSKEY"`

    - `"DS"`

    - `"NAPTR"`

    - `"SMIMEA"`

    - `"SSHFP"`

    - `"SVCB"`

    - `"TLSA"`

    - `"URI"`

- `result_info: optional object { count, page, per_page, 2 more }`

  - `count: optional number`

    Total number of results for the requested service.

  - `page: optional number`

    Current page within paginated list of results.

  - `per_page: optional number`

    Number of results per page of results.

  - `total_count: optional number`

    Total results available without any search parameters.

  - `total_pages: optional number`

    The number of total pages in the entire result set.

### Example

```http
curl https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/sending/subdomains/$SUBDOMAIN_ID/dns \
    -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
    -H "X-Auth-Key: $CLOUDFLARE_API_KEY"
```

#### Response

```json
{
  "errors": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message",
      "documentation_url": "documentation_url",
      "source": {
        "pointer": "pointer"
      }
    }
  ],
  "success": true,
  "result": [
    {
      "content": "route1.mx.cloudflare.net",
      "name": "example.com",
      "priority": 12,
      "ttl": 1,
      "type": "NS"
    }
  ],
  "result_info": {
    "count": 1,
    "page": 1,
    "per_page": 20,
    "total_count": 2000,
    "total_pages": 100
  }
}
```

---

---
title: Limits
description: Email Service sending quotas, rate limits, message size limits, and compliance requirements.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Limits

Email sending quotas, rate limits, and how to request higher limits for production use

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                                                                                                                             |

## 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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/platform/limits/#page","headline":"Limits · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/platform/","name":"Platform"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/platform/limits/","name":"Limits"}}]}
```

---

---
title: Pricing
description: Email Service pricing for outbound sending and inbound routing across Workers Free and Paid plans.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Pricing

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/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/platform/pricing/#page","headline":"Pricing · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/platform/","name":"Platform"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/platform/pricing/","name":"Pricing"}}]}
```

---

---
title: FAQ
description: Common questions about Email Service limits, sender reputation, marketing email support, and abuse reporting.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# FAQ

Common questions about Cloudflare Email Service.

## Limits and usage

Sending limits exist to prevent abuse and spam and to ensure high deliverability for all users. If you need higher limits, you can request a limit increase by contacting support or reaching out in the [Cloudflare Developers Discord ↗](https://discord.cloudflare.com). If you exceed your limits, emails may be queued or rejected, and you will receive error responses with rate limit information.

### What is sender reputation?

Sender reputation refers to how much inbox providers trust you to send good email to their users and not spam or scam. It is influenced by factors such as your email authentication setup, bounce and complaint rates, sending volume patterns, recipient engagement, and domain and IP history.

### Can I use this for marketing emails?

Email Service is intended only for transactional emails. We plan to support marketing emails and bulk sender tooling in the future.

### Where can I report abuse or spam?

Report abuse to: [abuse@cloudflare.com](mailto:abuse@cloudflare.com)

Include:

* Full email headers
* Description of the issue
* Any relevant account information

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/faq/#page","headline":"FAQ · Cloudflare Email Service docs","description":"Common questions about Email Service limits, sender reputation, marketing email support, and abuse reporting.","url":"https://developers.cloudflare.com/email-service/reference/faq/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/reference/","name":"Reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/reference/faq/","name":"FAQ"}}]}
```

---

---
title: Email headers
description: Allowed, platform-controlled, and custom email headers for Email Service with validation rules.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Email headers

Which email headers you can set, which are auto-generated, and how they are validated

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`.

### 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 |

### 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                                 |
| 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 |

### 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

* [ REST API (curl) ](#tab-panel-8878)
* [ Workers binding ](#tab-panel-8879)

Terminal window

```
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/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.                               |

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/headers/#page","headline":"Email headers · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/reference/","name":"Reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/reference/headers/","name":"Email headers"}}]}
```

---

---
title: Postmaster
description: Reference page with postmaster information for professionals, as well as configuration details for Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Postmaster

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%5FIdentified%5FMail) 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:

Terminal window

```
# Email Sending DKIMdig TXT cf-bounce._domainkey.example.com +short
# Email Routing DKIMdig 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:

Terminal window

```
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`:

Terminal window

```
dig mx gmail.comdig 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:

```
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:

```
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:

Terminal window

```
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:

Terminal window

```
dig a-h.cloudflare-email.net +short
```

```
104.30.0.7
```

Terminal window

```
dig -x 104.30.0.7 +short
```

```
a-h.cloudflare-email.net.
```

### Sender rewriting

For forwarded emails, Email Routing uses the [Sender Rewriting Scheme ↗](https://en.wikipedia.org/wiki/Sender%5FRewriting%5FScheme) 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:

```
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:

Terminal window

```
dig TXT _spf.mx.cloudflare.net +short
```

The record uses the format defined in [RFC 7208 ↗](https://datatracker.ietf.org/doc/html/rfc7208):

```
"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%5Femail). Email Routing only supports [internationalized domain names ↗](https://en.wikipedia.org/wiki/Internationalized%5Fdomain%5Fname).

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.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/postmaster/#page","headline":"Postmaster · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/reference/","name":"Reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/reference/postmaster/","name":"Postmaster"}}]}
```

---

---
title: Troubleshooting
description: Diagnose and fix delivery, authentication, and local development issues for Email Service.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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. 

[Skip to content](#%5Ftop) 

# Troubleshooting

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:**

```
v=spf1 include:_spf.mx.cloudflare.net include:other-service.com ~all
```

### Checking SPF records

Verify your SPF record is configured correctly:

Terminal window

```
dig TXT example.com +short | grep spf
```

Expected result should include:

```
"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:

Terminal window

```
# Check Email Sending DKIMdig TXT cf-bounce._domainkey.example.com +short
# Check Email Routing DKIMdig TXT cf2024-1._domainkey.example.com +short
```

Expected result for either:

```
"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:

Terminal window

```
dig TXT _dmarc.example.com +short
```

Example result:

```
"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/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

### 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

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/reference/troubleshooting/#page","headline":"Troubleshooting · Cloudflare Email Service docs","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/dev-products-preview.png","dateModified":"2026-06-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/email-service/","name":"Email Service"}},{"@type":"ListItem","position":3,"item":{"@id":"/email-service/reference/","name":"Reference"}},{"@type":"ListItem","position":4,"item":{"@id":"/email-service/reference/troubleshooting/","name":"Troubleshooting"}}]}
```
