ChangelogStatus
Docs/Sending email

Unsubscribe handling

Marketing mail needs a working unsubscribe path. Anypost can generate a one-click unsubscribe for a message, leave it off for transactional mail, or forward an unsubscribe header you supply yourself.

Note

One-click unsubscribe is not optional for bulk mail. Gmail and Yahoo require any sender of 5,000 or more messages a day to personal accounts to offer one-click unsubscribe on promotional mail and to honor the request within two days; Microsoft requires a working unsubscribe link on bulk mail. mode: "generate" satisfies these requirements. Transactional mail is exempt.

Three behaviors

The optional unsubscribe object on POST /v1/email selects one of two modes. A third behavior, forwarding your own header, needs no mode at all.

You wantWhat to send
Anypost to generate a one-click unsubscribeunsubscribe: { "mode": "generate" }
No unsubscribe header at allunsubscribe: { "mode": "none" }, or omit unsubscribe
To use your own list-management unsubscribeA List-Unsubscribe header; see Forward your own header

Let Anypost generate it

mode: "generate" mints a signed, per-recipient unsubscribe and injects the List-Unsubscribe and List-Unsubscribe-Post headers. Mail clients that support RFC 8058, including Gmail and Apple Mail, render a native Unsubscribe button from them.

curl https://api.anypost.com/v1/email \
  -H "Authorization: Bearer $ANYPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <[email protected]>",
    "to": ["[email protected]"],
    "subject": "March newsletter",
    "html": "<p>This month at Acme.</p>",
    "topic": "newsletter",
    "unsubscribe": { "mode": "generate", "display_name": "Acme newsletter" }
  }'

A generated unsubscribe requires a topic. The topic is the bucket the recipient unsubscribes from: a send with topic: "newsletter" unsubscribes them from your newsletter, not from your receipts. A mode: "generate" send with no topic is rejected with a validation_error. See Tags, topics & campaigns.

display_name is optional. It is a human-readable label, up to 120 characters, shown on the confirmation page Anypost hosts when a recipient unsubscribes.

When a recipient unsubscribes, Anypost records a suppression scoped to that topic and emits an email.unsubscribed event. Later sends to that address on the same topic are dropped; other topics are unaffected. See Suppressions.

A native client button is not always visible, so put an unsubscribe link in the body too. On a mode: "generate" send, write {{unsubscribe_url}} in the HTML body and Anypost replaces it with the same per-recipient unsubscribe URL it put in the header:

<p><a href="{{unsubscribe_url}}">Unsubscribe</a></p>

The placeholder is filled whether or not the send carries other variables.

No unsubscribe header

mode: "none" injects no unsubscribe header. It is the default: a send that omits unsubscribe behaves the same way.

Use it for transactional mail that must not carry unsubscribe semantics, such as a password reset or a one-time code. A recipient cannot opt out of a security email, and such a send usually carries no topic at all.

Forward your own header

If you already run list management elsewhere, set your own List-Unsubscribe header in the headers map. Anypost forwards it rather than generating its own. A customer-supplied header always wins: it is used even when unsubscribe.mode is generate.

A supplied header is checked and lightly normalized before it is sent. Common, recoverable mistakes are fixed and reported back in the warnings array of the response:

  • CR, LF, and tab characters are stripped, and runs of whitespace collapsed.
  • Missing < > angle brackets around a URI are added.
  • A mailto: URI listed before an https: URI is reordered, because one-click receivers scan for the first HTTPS URI.

Shapes that cannot be made valid are rejected with 422 and an error.code. The most common causes: a List-Unsubscribe-Post header without a matching List-Unsubscribe, a one-click header whose List-Unsubscribe carries no HTTPS URL, and a non-TLS http: URL. See API conventions.

In a batch

In a batch send, an unsubscribe object in defaults applies to every entry that does not set its own. The topic-required rule for mode: "generate" is checked per entry, against each entry's merged topic.

Where to go next