# Unsubscribe links

Marketing mail needs a way for recipients to opt out, and bulk senders are now required by Gmail and Yahoo to offer [one-click unsubscribe](/docs/knowledge-base/deliverability/gmail-yahoo-requirements). There are two paths a recipient can take — the unsubscribe button the mailbox provider renders from your `List-Unsubscribe` header, and a link you put in the message body — and Bird reports both as webhook events and acts on them by [suppressing](/docs/guides/email/suppressions) the address. This guide covers wiring up both, the events Bird fires, and how suppression closes the loop.

## Set the category first

Unsubscribes only mean something for mail a recipient can opt out of, so the first thing to get right is the [category](/docs/guides/email/categories). A send is `category: "marketing"` by default, so marketing, newsletter, and announcement mail is treated correctly with no extra work: a recipient who opts out is suppressed with reason `unsubscribe`, which blocks `marketing` sends but still lets `transactional` mail (password resets, receipts) through. Set `category: "transactional"` explicitly for operational mail, so it is never gated by an unsubscribe and carries no opt-out affordance.

## One-click List-Unsubscribe (RFC 8058)

The one-click mechanism is the unsubscribe button mailbox providers render in their own UI, driven by two headers on your message:

```text
List-Unsubscribe: <https://yourapp.com/unsubscribe?token=abc123>, <mailto:unsubscribe@yourdomain.com?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
```

`List-Unsubscribe` lists where to send the opt-out — an HTTPS URL, a `mailto:`, or both. `List-Unsubscribe-Post: List-Unsubscribe=One-Click` is what makes it _one-click_ (RFC 8058): the provider sends a plain `POST` to your HTTPS URL when the recipient taps unsubscribe, with no confirmation page. Your endpoint must accept that unauthenticated `POST` and record the opt-out without requiring the recipient to log in or confirm — that is the contract Gmail and Yahoo check for.

On a `marketing` send Bird sets these headers for you, so the compliant one-click button appears with no work on your side. This is true however you send: a broadcast from the dashboard, or a `marketing` message through the API (`POST /v1/email/messages`), which is now the default category. Because Bird owns the header on marketing mail, supplying your own `List-Unsubscribe` or `List-Unsubscribe-Post` on a `marketing` send is rejected with a `422`. On a `transactional` send Bird adds no unsubscribe header, and you may set your own through the [`headers`](/docs/guides/email/sending-email#reply-to-and-custom-headers) field if you have a reason to.

When a recipient uses the provider's button, Bird fires an [`email.list_unsubscribed`](/docs/guides/email/events#emaillistunsubscribed) event.

## An in-body unsubscribe link

The other path is a plain unsubscribe link in the message body, the visible opt-out that marketing mail is expected to carry on top of the header button. Bird adds one to the HTML of your `marketing` sends automatically, so a recipient reading the HTML always has a link to click without you building or hosting anything. You are free to add your own link as well (for example to a preference center you host), and the platform link remains as the compliant floor.

### Choosing where the link goes

By default the link arrives as a small footer appended to the end of your HTML. To place it yourself, put the reserved `{{unsubscribe_url}}` variable in your HTML where the link belongs:

```html
<p>
  You received this because you subscribed to our newsletter. You can {{unsubscribe_url}} at any
  time.
</p>
```

When the body contains `{{unsubscribe_url}}`, Bird renders the unsubscribe link there — an anchor whose text is "unsubscribe" — instead of appending the footer. Two details worth knowing: the link is substituted into the outgoing copy only, so reading the message back through the API returns your original content with the variable intact; and it applies to the HTML part only — the plain-text part of a message carries no unsubscribe link, so recipients reading plain text rely on the header button.

When a recipient unsubscribes through the in-body link, Bird fires an [`email.unsubscribed`](/docs/guides/email/events#emailunsubscribed) event, distinct from `email.list_unsubscribed` so you can tell a footer-link opt-out apart from a provider-button one.

## The webhook events

Bird delivers both unsubscribe events to your [webhook endpoint](/docs/guides/webhooks) in the standard event envelope. Subscribe to them the same way you subscribe to any other email event — there is no separate unsubscribe webhook to configure:

| Event                     | Fires when                                                                       |
| :------------------------ | :------------------------------------------------------------------------------- |
| `email.unsubscribed`      | The recipient clicked the unsubscribe link in your message body.                 |
| `email.list_unsubscribed` | The recipient used the mailbox provider's one-click button (`List-Unsubscribe`). |

Both carry the identity base every email event carries — `email_id`, `recipient_id`, `workspace_id`, the `recipient` address and its `recipient_role`, plus the `tags` and `metadata` from the original send — so you can reconcile the opt-out against your own records without an extra lookup:

```json
{
  "type": "email.list_unsubscribed",
  "timestamp": "2026-06-10T14:30:00Z",
  "data": {
    "email_id": "em_01krdgeqcxet5s7t44vh8rt9mg",
    "recipient_id": "er_01krdgeqcxet5s7t44vh8rt9mg",
    "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
    "recipient": "subscriber@example.com",
    "recipient_role": "to",
    "tags": [{ "name": "category", "value": "newsletter" }],
    "metadata": { "list_id": "weekly-digest" }
  }
}
```

Use these events to update preferences in your own system — flip the subscription off, log the opt-out, stop including the address in your sends. The field set and the rest of the email event catalog are in the [events reference](/docs/guides/email/events).

## Suppression closes the loop

You don't have to act on the webhook to stop Bird from mailing an unsubscribed recipient again — Bird does it automatically. Both `email.unsubscribed` and `email.list_unsubscribed` add the address to your workspace [suppression list](/docs/guides/email/suppressions) with reason `unsubscribe` and `applies_to: non_transactional`:

- Future **`marketing`** sends to the address are rejected up front with `email.rejected` and `rejection_reason: recipient_suppressed`.
- Future **`transactional`** sends still go through — an unsubscribe is a preference about marketing mail, not a dead address.

That makes the webhook events a mirror for your own database rather than the thing that protects your reputation; Bird already blocks the send. If you ever need to inspect or undo an unsubscribe, the suppression record carries `source_email_id` and `source_recipient_id` linking back to the message that caused it, and you can remove it through the [suppressions API](/docs/guides/email/suppressions#managing-suppressions-via-the-api).

## Next steps

- [Categories](/docs/guides/email/categories) — why `marketing` is what makes an unsubscribe take effect
- [Suppressions](/docs/guides/email/suppressions) — the list unsubscribes land on, and how to manage it
- [Email events](/docs/guides/email/events) — the full `email.unsubscribed` / `email.list_unsubscribed` payloads and the rest of the catalog
- [Gmail & Yahoo requirements](/docs/knowledge-base/deliverability/gmail-yahoo-requirements) — where one-click unsubscribe is required for bulk senders
- [Webhooks](/docs/guides/webhooks) — subscribing an endpoint, signature verification, and retries