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

[Skip to content](#main-content)

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

# Send email over SMTP

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

Last updated Aug 24, 2026|Copy as Markdown|[View as Markdown](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/examples/email-sending/smtp/index.md)|[Agent setup](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/agent-setup/)

Send transactional emails over Cloudflare Email Service [authenticated SMTP](https://docs-durable-objects-instance-replaced-errors.previews.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://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/configuration/domains/).
* A [Cloudflare API token](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the **Email Sending: Edit** permission. Set it as `CF_API_TOKEN` in your environment. The token is used as the SMTP password; the username is the literal string `api_token`.

### Send an email

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

Thanks for signing up.
EOF

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

The sender domain must be onboarded for Email Sending on the account that owns the API token.

Install [Nodemailer ↗](https://nodemailer.com/) with `npm install nodemailer`.

### Send an email

```js
import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
	host: "smtp.mx.cloudflare.net",
	port: 465,
	secure: true, // implicit TLS
	auth: {
		user: "api_token",
		pass: process.env.CF_API_TOKEN,
	},
});

const info = await transporter.sendMail({
	from: '"Acme" <welcome@yourdomain.com>',
	to: "user@example.com",
	subject: "Welcome to Acme",
	text: "Thanks for signing up.",
	html: "<h1>Welcome to Acme</h1><p>Thanks for signing up.</p>",
});

console.log("Message sent:", info.messageId);
```

### Send with an attachment

```js
const info = await transporter.sendMail({
	from: '"Acme Billing" <billing@yourdomain.com>',
	to: "customer@example.com",
	subject: "Your invoice",
	text: "Please find your invoice attached.",
	attachments: [
		{
			filename: "invoice-2026-04.pdf",
			path: "./invoices/invoice-2026-04.pdf",
			contentType: "application/pdf",
		},
	],
});
```

Total message size (including base64-encoded attachments) must not exceed 5 MiB. See [Limits](https://docs-durable-objects-instance-replaced-errors.previews.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://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/api/send-emails/smtp/#response-codes) and [Troubleshooting](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/api/send-emails/smtp/#troubleshooting).

```js
try {
	await transporter.sendMail({
		/* ... */
	});
} catch (err) {
	console.error(err.responseCode, err.message);
}
```

Uses the standard-library [smtplib ↗](https://docs.python.org/3/library/smtplib.html) (Python 3.8 or later).

### Send an email

```python
import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "Acme <welcome@yourdomain.com>"
msg["To"] = "user@example.com"
msg["Subject"] = "Welcome to Acme"
msg.set_content("Thanks for signing up.")
msg.add_alternative(
    "<h1>Welcome to Acme</h1><p>Thanks for signing up.</p>",
    subtype="html",
)

with smtplib.SMTP_SSL("smtp.mx.cloudflare.net", 465) as s:
    s.login("api_token", os.environ["CF_API_TOKEN"])
    s.send_message(msg)
```

`smtplib.SMTP_SSL` opens an implicit-TLS connection on port `465`, which is what Cloudflare's SMTP endpoint requires. Do not use `smtplib.SMTP` with `starttls()`; `STARTTLS` is not supported.

### Send to multiple recipients

```python
msg["To"] = ", ".join([
    "user1@example.com",
    "user2@example.com",
    "user3@example.com",
])
```

A single SMTP session can deliver to up to 50 `RCPT TO` addresses. See [Limits](https://docs-durable-objects-instance-replaced-errors.previews.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://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/api/send-emails/smtp/#response-codes) and [Troubleshooting](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/api/send-emails/smtp/#troubleshooting).

```python
try:
    with smtplib.SMTP_SSL("smtp.mx.cloudflare.net", 465) as s:
        s.login("api_token", os.environ["CF_API_TOKEN"])
        s.send_message(msg)
except smtplib.SMTPAuthenticationError as e:
    print(f"Auth failed: {e.smtp_code} {e.smtp_error!r}")
except smtplib.SMTPResponseException as e:
    print(f"SMTP error: {e.smtp_code} {e.smtp_error!r}")
```

### Send an email

```php
<?php
use PHPMailer\PHPMailer\PHPMailer;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host       = 'smtp.mx.cloudflare.net';
$mail->Port       = 465;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->SMTPAuth   = true;
$mail->Username   = 'api_token';
$mail->Password   = getenv('CF_API_TOKEN');

$mail->setFrom('welcome@yourdomain.com', 'Acme');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'Welcome to our service!';
$mail->Body    = 'Thanks for signing up.';
$mail->send();
```

## Next steps

* [SMTP reference](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/api/send-emails/smtp/) — connection details, authentication, response codes, and troubleshooting.
* [Specify recipients](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/examples/email-sending/recipients/) — multiple recipients, CC and BCC, and named addresses.
* [Limits](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/email-service/platform/limits/) — account, message, and session limits.

Was this helpful?

YesNo

## On this page

[![](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/email-service/examples/email-sending/smtp/#page","headline":"Send email over SMTP · 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/og-docs.png","dateModified":"2026-08-24","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```
