# Topics & the preference center (/guides/preference-center)

A single "subscribed / unsubscribed" switch is too blunt once you send more than one kind of email. **Topics** let a contact keep your security alerts and weekly digest while opting out of promotions — instead of unsubscribing from everything. This guide covers creating topics, the hosted preference center, double opt-in, and the consent history.

## Topics

A **topic** is a kind of communication a contact can subscribe to independently — *Product updates*, *Security alerts*, *Weekly digest*, *Promotions*, *Billing notifications*, and so on. Create and manage them in **Settings › Data › Topics**.

Each topic has:

| Field                     | What it does                                                                                                                                         |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**                  | What the contact sees in the preference center (e.g. "Weekly digest").                                                                               |
| **Key**                   | A stable machine identifier (e.g. `weekly_digest`) — the contract for the API and imports. **Immutable** once created.                               |
| **Channels**              | Which channels the topic is offered on (email, SMS). Defaults to email.                                                                              |
| **Required**              | Transactional-class topics (security, billing). Shown locked in the center and **always delivered**, even to a contact who turned off all marketing. |
| **Subscribed by default** | Whether a brand-new contact counts as subscribed to this optional topic until they say otherwise.                                                    |
| **Double opt-in**         | Requires the contact to confirm via an emailed link before the opt-in counts (see below).                                                            |

Topics are **presentation and consent only** — they never change how an event is produced or how a workflow matches. A project with no topics behaves exactly as before.

### Required vs optional

* **Required** topics bypass the master switch. They're for mail a contact can't reasonably opt out of — a security alert, a billing notice. The preference center shows them as *Always on*.
* **Optional** topics need both the channel master **on** *and* a topic-level opt-in. This is the normal case.

## The hosted preference center

Every marketing (and headless) email already carries a personalized link to a hosted page — you don't build or host anything. With topics defined, that page becomes a **per-topic, per-channel** center:

* A master switch for marketing email.
* One toggle per optional topic.
* Required topics shown locked ("Always on").
* An "unsubscribe from everything" escape hatch (the one-click target for the `List-Unsubscribe` header).

The page is reached through a **signed token** — no contact id in the URL — and is rendered in the contact's language. Links already sitting in old inboxes keep working.

<Callout title="Attaching a topic to a campaign">
  When you set a campaign's topic, its audience is automatically narrowed to the contacts opted in to that topic. A campaign with no topic keeps today's behaviour (everyone subscribed to marketing).
</Callout>

## Double opt-in

Turn on **Double opt-in** for a topic and an opt-in no longer counts until the contact confirms:

1. The contact opts in (via the preference center or a form). Their state becomes **pending** — they receive no topic mail yet.
2. Bitelio emails a confirmation link from your verified sending domain.
3. Clicking it flips them to **subscribed** and stamps the active **consent version** (what they agreed to, and when).

A pending contact never receives the topic's mail, so an unconfirmed opt-in can never leak into a send.

## Consent history

Every consent change — a toggle in the center, a form submission, a bounce, a complaint, an import — is written to an **append-only consent history**. Open a contact and select the **Consent** tab to read it: what changed, when, the source, and the reason. This is the record you reach for during a compliance review.

## Building your own preference center

You don't have to use the hosted page. Mint a link for a contact and either embed it or drive the public API yourself:

```bash
# Generate a preference-center link for a contact (authenticated, secret key)
POST /contacts/{id}/preference-link
→ { "token": "v1.…", "url": "https://app.…/preferences/v1.…" }
```

Then read and write preferences against the token — no auth, the token is the capability:

```bash
GET  /preferences/{token}            # current state: channel master + topics
PUT  /preferences/{token}            # { topics: { weekly_digest: { email: true } }, channelMaster: { email: true } }
POST /preferences/{token}/unsubscribe-all
```

Opting a contact into a double opt-in topic through the API triggers the same confirmation flow.

## Related

* [Unsubscribe & preferences pages](/docs/guides/unsubscribe-pages) — the link variables injected into every send.
* [Email suppressions](/docs/guides/suppressions) — the do-not-send list.
* [Importing consent](/docs/guides/importing-consent) — bring topic consent from another platform.
