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

## 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](https://mailpiece.dev/docs/relay)
- 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.

```sh
// 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](https://mailpiece.dev/docs/mail).

```sh
# 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. [Install Mailpiece](https://mailpiece.dev/docs/install)
