Guide · Compliance · RFC 8058

One-click unsubscribe with List-Unsubscribe-Post

To offer RFC 8058 one-click unsubscribe through tinyRelay, send both List-Unsubscribe (an HTTPS URL that accepts POST) and List-Unsubscribe-Post: List-Unsubscribe=One-Click in the request's headers object, with names spelled exactly.

"headers": {
  "List-Unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>",
  "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
}
Mail app
POST to your URL
Your system

One-click advertisedBoth present, names exact. Your URL must accept a POST, and DKIM must cover both headers.

"headers": {
  "List-Unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>"
}
Mail app
POST to your URL
Your system

No one-clickPer the tinyRelay docs, List-Unsubscribe alone does not enable one-click. Add List-Unsubscribe-Post.

"headers": {
  "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
}
Mail app
POST to your URL
Your system

Unbacked promiseIt advertises one-click that no URL of yours backs. Send your own List-Unsubscribe with it.

"headers": {
  "list-unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>",
  "list-unsubscribe-post": "List-Unsubscribe=One-Click"
}
Mail app
POST to your URL
Your system

Names not matchedHeader names are matched exactly and are case-sensitive. Send List-Unsubscribe, not list-unsubscribe.

"headers": {}
Mail app
POST to your URL
Your system

Default headertinyRelay generates its default unsubscribe header. Supply both headers to control one-click yourself.

Pick what your request sends. Rules: tinyRelay docs, RFC 8058.

What RFC 8058 requires

RFC 8058 lets a mail app unsubscribe someone with one HTTPS POST, without opening a web page. For a message to qualify, check all of these:

The mail app then sends the List-Unsubscribe-Post value as the body of a POST to your URL, only after the user asks to unsubscribe. The body may arrive as multipart/form-data or application/x-www-form-urlencoded, so let the token in the URL do the work rather than depending on the body.

What your endpoint receives · illustrativeCopy
POST /unsubscribe/7f3a9c2e5b HTTP/1.1
Host: acme.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

List-Unsubscribe=One-Click

tinyRelay's rules for these headers

From the Send Email API reference:

Both headers are required. List-Unsubscribe alone does not enable one-click. Supplying one without the other advertises a capability the other does not back.

"headers": {
  "List-Unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>",
  "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
}

Names are case-sensitive. Header names are matched exactly: send List-Unsubscribe, not list-unsubscribe.

// matched
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
// not matched
"list-unsubscribe-post": "List-Unsubscribe=One-Click"

The URL must accept a POST and is written in angle brackets.

"List-Unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>"

Your header replaces the default. Supplying List-Unsubscribe overrides the default unsubscribe header tinyRelay generates, and tinyRelay adds no unsubscribe link of its own to your HTML.

// no List-Unsubscribe in headers: tinyRelay generates its default header
"headers": {}

Add one-click unsubscribe, step by step

1. Create an unsubscribe URL that accepts POST

Generate an opaque token per recipient and list, and serve POST /unsubscribe/<token>. Answer with a status code, never a redirect.

// Mail apps POST here when the user clicks Unsubscribe.
// The token identifies the recipient and the list; nothing else is needed.
app.post("/unsubscribe/:token", async (req, res) => {
  const done = await unsubscribeByToken(req.params.token); // your own lookup
  res.sendStatus(done ? 200 : 404); // answer directly: never redirect a one-click POST
});
# Mail apps POST here when the user clicks Unsubscribe.
# The token identifies the recipient and the list; nothing else is needed.
@app.post("/unsubscribe/<token>")
def one_click_unsubscribe(token):
    if not unsubscribe_by_token(token):  # your own lookup
        return "", 404
    return "", 200  # answer directly: never redirect a one-click POST

2. Add both headers to the request

Put them in the top-level headers object. Headers apply to the whole request, so when each recipient needs their own URL, send one request per recipient.

POST /email/campaign
{
  "subject": "Your weekly Acme activity summary",
  "body": "<p>Hi [FIRST_NAME], here is what happened in your workspace this week.</p>",
  "sender": { "from": { "name": "Acme", "email": "updates@acme.com" } },
  "recipients": {
    "to": [
      { "email": "ann@example.com", "attributes": { "FIRST_NAME": "Ann" } }
    ]
  },
  "headers": {
    "List-Unsubscribe": "<https://acme.com/unsubscribe/7f3a9c2e5b>",
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
  },
  "metadata": { "user_id": "u_1042", "list": "weekly_summary" }
}
// Build one request per recipient so each gets their own unsubscribe URL
const token = await createUnsubscribeToken(user.id, "weekly_summary"); // your own opaque token

await sendEmail({
  subject: "Your weekly Acme activity summary",
  body: "<p>Hi [FIRST_NAME], here is what happened in your workspace this week.</p>",
  sender: { from: { name: "Acme", email: "updates@acme.com" } },
  recipients: { to: [{ email: user.email, attributes: { FIRST_NAME: user.firstName } }] },
  headers: {
    "List-Unsubscribe": `<https://acme.com/unsubscribe/${token}>`,
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
  },
  metadata: { user_id: user.id, list: "weekly_summary" },
});
# One request per recipient so each gets their own unsubscribe URL
token = create_unsubscribe_token(user.id, "weekly_summary")  # your own opaque token

send_email({
    "subject": "Your weekly Acme activity summary",
    "body": "<p>Hi [FIRST_NAME], here is what happened in your workspace this week.</p>",
    "sender": {"from": {"name": "Acme", "email": "updates@acme.com"}},
    "recipients": {"to": [{"email": user.email, "attributes": {"FIRST_NAME": user.first_name}}]},
    "headers": {
        "List-Unsubscribe": f"<https://acme.com/unsubscribe/{token}>",
        "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
    },
    "metadata": {"user_id": user.id, "list": "weekly_summary"},
})

Base URL from current site samples; confirm in the docs.

sendEmail and send_email are the helpers from send email with Node.js and send email with Python.

3. Sign your sending domain with DKIM

tinyRelay signs your mail once you add the CNAME tec1._domainkey.yourdomain.com pointing to tec1.dkim.tinyemail.com. To confirm one-click is eligible, open a delivered message's source and check that the DKIM-Signature h= list includes both headers. Read transactional email deliverability for the DNS steps.

4. Record the opt-out

When you supply your own URL, the one-click POST goes to your server, so your system is where the opt-out is recorded. Stop sending that list to the recipient straight away. tinyRelay also has an UNSUBSCRIBE webhook event and bounce category 90 (Unsubscribe); see email webhooks.

Troubleshooting

What do you see?4 common symptoms
No unsubscribe button in the mail appWhat to check

Both headers present, names in exact case, URL in angle brackets and HTTPS, and the DKIM h= list covering both headers.

The button appears but nothing happensWhat to check

Your endpoint must accept POST and answer directly. A redirect or a GET-only route breaks one-click.

The wrong person was unsubscribedWhat to check

The token must identify the recipient. Headers apply to the whole request, so use one request per recipient when URLs differ.

Your header did not replace the defaultWhat to check

The name is probably lowercase. Header names are matched exactly.

FAQ

What is RFC 8058 one-click unsubscribe?

A standard that lets a mail app unsubscribe someone with a single HTTPS POST. The message carries a List-Unsubscribe header with an HTTPS URL and a List-Unsubscribe-Post header set to List-Unsubscribe=One-Click.

No. The tinyRelay docs say List-Unsubscribe alone does not enable one-click. Send List-Unsubscribe-Post with the value List-Unsubscribe=One-Click as well.

Yes. Header names in the request body are matched exactly. Send List-Unsubscribe and List-Unsubscribe-Post, not lowercase versions.

Accept a POST and unsubscribe the recipient it identifies, without a redirect. RFC 8058 also requires the URL to carry enough information to identify the recipient and the list.

tinyRelay generates its default unsubscribe header. Supplying your own List-Unsubscribe overrides that default, and tinyRelay then adds no unsubscribe link of its own to your HTML.

Next steps

Sources

Get started

Send transactional email with a service that reports back.

SMTP relay or REST API, standalone or alongside tinyEmail. Tell us what you send and our team sets you up.