Mailpiece with Cloudflare Workers
Run your mail relay on a Cloudflare Worker, and send through your provider's API.
Mailpiece hands each issue to a small relay on a Cloudflare Worker, which passes it to your mail provider's API and remembers what it sent in Workers KV, so a message posted twice is sent once. Your provider's key lives in Cloudflare, not on your server.
With Mailpiece
What you get
- Issues, test issues, welcome emails and confirmations
- Any provider with an HTTP API: the Worker below calls Resend, and your agent can change it for another from the relay guide
- Each issue sent once, however often Mailpiece posts it
- Your provider's key kept as a Worker secret
- No SMTP port needed, so it suits a host that blocks one
Set up your provider
Verify your sending domain with the provider and create an API key that can send. For Resend, that is a key with Sending access.
Create the KV namespace
In the Cloudflare dashboard, open Workers KV, choose Create instance and name it, such as mailpiece-sent. The Worker keeps there the Idempotency-Key of each message it has sent, for a week.
Deploy the Worker
In Workers & Pages choose Create application, start from Hello World and deploy it, then choose Edit code, paste the relay below and deploy again. Its address is shown on the Worker's page, such as https://mailpiece-relay.example.workers.dev.
A message whose key the Worker has already sent is answered as sent and not sent again. KV can take up to a minute to show a new key in other locations, so the Worker also hands the key to Resend, which keeps it for 24 hours.
// A week: Mailpiece stops posting a message once it is answered as sent.
const KEPT_FOR = 7 * 24 * 60 * 60
const address = (email, name) => name ? `${JSON.stringify(name)} <${email}>` : email
export default {
async fetch(request, env) {
if (request.headers.get("Authorization") !== `Bearer ${env.RELAY_TOKEN}`) {
return new Response("Unauthorized", { status: 401 })
}
const key = request.headers.get("Idempotency-Key")
if (key) {
const sent = await env.SENT.get(key)
if (sent !== null) return Response.json({ message_id: sent })
}
const message = await request.json()
const headers = { ...message.headers }
if (message.list_unsubscribe) {
headers["List-Unsubscribe"] = message.list_unsubscribe
headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click"
}
const response = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${env.RESEND_API_KEY}`,
"Content-Type": "application/json",
...(key && { "Idempotency-Key": key })
},
body: JSON.stringify({
from: address(message.from, message.from_name),
to: message.to,
cc: message.cc,
bcc: message.bcc,
reply_to: message.reply_to && address(message.reply_to, message.reply_to_name),
subject: message.subject,
html: message.html,
text: message.text,
headers,
tags: message.tags?.map(name => ({ name, value: "true" }))
})
})
if (!response.ok) return new Response(await response.text(), { status: response.status })
const { id } = await response.json()
if (key) await env.SENT.put(key, id, { expirationTtl: KEPT_FOR })
return Response.json({ message_id: id })
}
}
Bind the namespace and add the secrets
On the Worker's Bindings tab choose Add binding, then KV namespace, name the variable SENT and pick your namespace. Under Settings › Variables and Secrets add two secrets and deploy: RELAY_TOKEN, a long random string you choose, and RESEND_API_KEY, your provider's key. The Worker reads them as env.SENT, env.RELAY_TOKEN and env.RESEND_API_KEY.
Set the outgoing mail
In Mailpiece, on Settings › Outgoing mail, set up a relay: the Relay address is the Worker's, and the Token the same string as the Worker's RELAY_TOKEN. Every setting is in the mail guide.
# Or from the CLI
mailpiece settings sending relay update --url https://mailpiece-relay.example.workers.dev --token RELAY_TOKEN
Send a test issue
Set a from address on your verified domain in Settings › Sending and send a test issue from any post. If it does not arrive, open the Worker's logs in the dashboard, then your provider's email log.
Set it up this afternoon
Install Mailpiece on a server, connect Cloudflare Workers, and send your next issue.