> A publication gets a subscriber list with double opt-in, a weekly roundup an admin approves, and alerts when items publish. Released in Papyrus 5.21.0; running on shipped.anth.us. Setup and the sending prerequisite.

Canonical: https://p.apyr.us/newsletters.html

# Newsletters for a Papyrus publication

A publication on Papyrus can send its own email. Readers subscribe on the site, the newsroom assembles a weekly roundup from what was published, an admin approves it, and Papyrus sends it. A publication can also send an alert when a matching item is published. Every issue that is sent also becomes a page on the site that people and agents can read.

The newsletter is released in Papyrus 5.21.0 as the `newsletter` plugin. It is running on [shipped.anth.us](https://shipped.anth.us/), where the issues are archived at [/roundup/](https://shipped.anth.us/roundup/). It is off by default: a site turns it on in its configuration, as described under Setup below.

## What a publication gets

- **Subscribers who asked for the email.** Signing up is double opt-in. The reader gets a confirmation email, and the subscription starts when they confirm. The links are signed, expire, and work once. Papyrus keeps a consent record with the exact wording the form showed.
- **Easy ways out.** Every issue carries a one-click unsubscribe, as RFC 8058 describes, and a preferences page where a reader can change lists, re-subscribe, or delete their data.
- **Bounces and complaints handled.** Hard bounces and complaints reported by Amazon SES suppress the address, so the publication stops mailing it.
- **A weekly roundup.** On a schedule, Papyrus drafts an issue from the items published since the last one, grouped by section. An editor can rewrite it in the ordinary newsroom editor. Nothing is written by a model.
- **An approval step.** An admin approves each issue for a time. Papyrus checks the content again when the time comes, and if the issue was edited after approval, it goes back to draft and nothing is sent.
- **Alerts.** A list can send one short email when an item is published that matches a rule on its section, tag or type. Each item is sent once per list. An admin approves each alert, unless the site has allowed instant sends and the list is set to send without approval.
- **A page for every issue.** A sent issue is published to the archive with its own page, a Markdown twin and an entry in `llms.txt`.
- **A newsroom panel.** Subscriber counts, issues and their status, a preview, a test send to your own address, delivery history, pause and resume, and, for admins, export and erasure.
- **Little tracking.** There are no open pixels and no click tracking. The email footer carries the publication's postal address.

## How it works

1. A reader fills in the signup form and gets a confirmation email. Following the link opens a page with a Confirm button; the button confirms, so a mail scanner that opens the link cannot subscribe anyone.
2. On the roundup's schedule, Papyrus drafts the issue from the published items. An alert list instead queues an alert when a matching item is published.
3. An editor reads the preview and sends a test to their own address. An admin approves the issue for a time.
4. At that time Papyrus checks that the content is unchanged, that the postal address is set and that sending is not paused. Then it sends, paced to the account's SES send rate. Each subscriber is sent each issue at most once, even if a worker restarts.
5. When every address is accounted for, the issue is marked sent and published to the archive.

Each site also has alarms that pause that site's sending before the account's limits are reached, so one publication's problem does not stop another. An admin resumes sending from the newsroom panel.

## Readable by agents

The archive is published the same way as the rest of a Papyrus site. Each sent issue has an HTML page, a [Markus](https://markus.anth.us/) `.md` twin, and a line in `llms.txt`, so an agent can read the back issues without scraping email. See [Readable by agents and people alike](/agent-accessible.html).

## Setup

Sending needs a stack of its own, one per site. It creates the Amazon SES identity for the sending domain with DKIM and a custom MAIL FROM domain, the DNS records for them and for SPF and DMARC, and a configuration set with open and click tracking off. Deploy it before turning the plugin on. The postal address is read from the operator's AWS configuration, not from the repository, and sending is blocked until it is set. While the AWS account is in the SES sandbox, SES delivers only to verified addresses, so reaching ordinary readers needs production sending access for the account.

Then enable the plugin in the site's `papyrus.config.ts`. The values below are placeholders:

```ts
backend: {
  plugins: [
    {
      name: "newsletter",
      siteId: "example",
      siteOrigin: "https://news.example.com",
      computeRoleArn: "arn:aws:iam::<account>:role/<CmsComputeRole>",
      sendingDomain: "news.example.com",
      fromAddress: "<mailbox>@news.example.com",
      fromName: "Example News",
      instantAlerts: false,
      theme: { accent: "#...", text: "#...", background: "#...", logoUrl: "https://..." },
      lists: [
        { id: "weekly", name: "Weekly roundup", description: "...", kind: "roundup",
          schedule: { draft: "cron(0 13 ? * FRI *)", timezone: "America/New_York" },
          issueType: "newsletter_issue", archivePath: "/roundup/" },
        { id: "alerts", name: "Alerts", description: "...", kind: "alert",
          rule: { sections: ["..."], tags: ["..."], types: ["..."] }, approval: "admin" },
      ],
    },
  ],
},
```

The site's `projection.json` lists the issue type and a section for the archive, and the page carries the `<papyrus-newsletter>` signup component. A configuration that is wrong, such as a `fromAddress` outside the sending domain or a list with an invalid schedule, stops the backend build with a message that says what to fix.

The details for the people who run a publication are in the Papyrus repository, in `docs/newsletter.md`. See also [Built with Papyrus](/built-with/).
