> For the complete documentation index, see [llms.txt](https://codemash.gitbook.io/codemash/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codemash.gitbook.io/codemash/cloud/emails/preferences.md).

# Preferences and unsubscribe pages

The preferences and unsubscribe pages your recipients open from a marketing email.

**Where:** not in the project menu — they open from the links in a sent email, on your Cloud address: `/notifications/preferences?hash=<signed link>` and `/notifications/email-unsubscribe?hash=<signed link>`.

Marketing email must carry a working way to unsubscribe. Every marketing email Norbix sends can carry two signed links: the link itself proves who it is for, needs no sign-in, and Norbix refuses a link that was changed.

| Link in the email | Page title                            | Template call that writes the link                 |
| ----------------- | ------------------------------------- | -------------------------------------------------- |
| Preferences       | **Email preferences**                 | `@Model.Cmf.GetEmailPreferencesLink()`             |
| Unsubscribe       | **Unsubscribe from marketing emails** | `@Model.Cmf.GetEmailUnsubscribeFromCategoryLink()` |

## Before you start

* A template or footer with the two links — usually in a [footer](/codemash/cloud/emails/footers.md) used by every marketing template:

  ```html
  <mj-text>
    <a href="@Model.Cmf.GetEmailPreferencesLink()">Manage preferences</a> ·
    <a href="@Model.Cmf.GetEmailUnsubscribeFromCategoryLink()">Unsubscribe</a>
  </mj-text>
  ```

  You never fill these on the campaign form; Norbix signs a new link for every recipient when it sends.
* A campaign to users (**All users**, **Specified Users**, or a collection of user ids). Campaigns to plain **Email Addresses** have no user behind each address: the calls write `#`.

## Steps

What the recipient does:

1. Opens **Manage preferences** from the email. **Email preferences** shows one sentence, for example `ada.lovelace@example.com receives marketing emails.`, the button **Unsubscribe from all marketing emails**, and "You may still receive emails you need, such as account or security messages."

   <figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-615e37438c498338545d6543b103709de9302bec%2Fpreferences-1.png?alt=media" alt="Email preferences: ada.lovelace@example.com receives marketing emails. Unsubscribe from all marketing emails button"><figcaption><p>The preferences page from a sent email.</p></figcaption></figure>
2. Presses **Unsubscribe from all marketing emails**.

The **Unsubscribe** link opens **Unsubscribe from marketing emails** with the same sentence and button. Opening a page alone never unsubscribes anybody — mail scanners open links on their own, so it takes a click.

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-b6a64ca67d166360eafd0d43492aa3e0347b6592%2Fpreferences-3.png?alt=media" alt="Unsubscribe from marketing emails: alan.turing@example.com receives marketing emails. Unsubscribe from all marketing emails button"><figcaption><p>The unsubscribe page.</p></figcaption></figure>

## Result

A green **Unsubscribed** box says "You have been unsubscribed from marketing emails.", and the sentence changes to `ada.lovelace@example.com is unsubscribed from marketing emails.` Norbix sends a short confirmation email through the project's default integration. Later marketing campaigns skip this address (status `BlockedByUserPreferenceBlockAll` in the [message log](/codemash/cloud/emails/campaigns/log.md)); transactional emails still arrive.

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-57adcc32a0ec4057ac82662c2ae923405cfd61b1%2Fpreferences-2.png?alt=media" alt="Email preferences after the click: ada.lovelace@example.com is unsubscribed from marketing emails, green Unsubscribed box"><figcaption><p>Unsubscribed.</p></figcaption></figure>

A changed, made-up or unsigned link shows **This link cannot be used** — "This unsubscribe link is not valid." (`CM-ERRORS-EMAILS-024`) — and nothing is read or changed. A link without a hash says "This link is incomplete. Open the link from the email again."

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-48769e32acc2a6e36350f98ced9cac0fbae216e5%2Fpreferences-4.png?alt=media" alt="Email preferences: This link cannot be used. This unsubscribe link is not valid."><figcaption><p>A changed link is refused.</p></figcaption></figure>

* **The link carries the environment.** A link from a TEST campaign unsubscribes in TEST.
* **All marketing, not one category.** There is no per-category unsubscribe or re-subscribe on these pages yet.
* **Bounces and complaints block too** with [Mailgun and its webhook](/codemash/cloud/emails/integrations/providers/mailgun.md).
* **Changing the gateway's auth key breaks old links.** The links are signed with it (`NORBIX_GATEWAY_AUTH_JWT_AESKEYBASE64` on self-hosted installs).
* **Rate limit:** the preferences read answers at most 60 requests per minute from one IP address.

## Change or remove it later

A recipient cannot re-subscribe from these pages today. The pages themselves have no settings; change the links by editing the template or footer that writes them.

## Do it from code

The signed links of a sent message are in its model (`Campaign.PreferencesHash`, `Campaign.UnsubscribeFromCategoryHash`):

```ts
const { list } = await norbix.hub.notifications.getEmailCampaignBatchNotifications({ id, batchId });
const link = list.items[0].model['Campaign.PreferencesHash']; // <payload>.<signature>
// open https://<cloud>/notifications/preferences?hash=<link>
```

```sh
norbix hub notifications email campaign batches get --id cmp_7mt3e31YJCj3DNk2oDWxNc
```

API: [Get Email Campaign Batch Notifications](/codemash/api-reference/notifications/email/get-email-campaign-batch-notifications.md)
