> ## Documentation Index
> Fetch the complete documentation index at: https://notifizz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscriptions — Follow a Resource, Get Notified

> How people subscribe to a project, a thread or a document in your app, what Notifizz stores, and how a subscription turns into a notification.

# Subscriptions

Most campaigns decide *who* to notify from the event: the buyer, the account owner, the person whose password expired. A **subscription** flips that around. Someone tells you, once, that they want to hear about a specific thing in your product — a project, a thread, a document, a build pipeline — and every later event about that thing reaches them.

This page is the shared vocabulary: what a subscription is, what Notifizz keeps, and how one turns into a message. Developers will find the calls in the [backend reference](/docs/sdks/subscriptions/backend) and the [subscribe widget](/docs/sdks/subscriptions/widget).

<Warning>
  **Closed beta.** Embedding the subscribe widget in your own app is enabled per organisation and is off by default. If the **Subscribe widget** screen in your dashboard shows a preview instead of a form, ask your Notifizz contact to switch it on.
</Warning>

## TL;DR

* A subscription is a link between a **subscriber id** (whoever your app says the current user is) and a **resource id** (whatever they chose to follow). Nothing more.
* **People subscribe themselves**, from your own interface, by clicking the subscribe button. That click is the consent record.
* Notifizz stores the two ids and a timestamp. **No name, no email**, unless your application explicitly supplies them.
* **Unsubscribing deletes the record**, display data included. There is no tombstone and no archive.
* A subscription becomes a notification when a campaign whose recipient is **Subscribers** receives an event carrying the resource id. Membership is resolved **at notification time**, never from a list inside the event.
* Subscriptions are not [unsubscribe categories](/docs/concepts/privacy-friendly). A subscription says *notify me about this thing*; a category says *how much promotional mail this address accepts*.

## What a subscription holds

| Field         | What it is                                                                                                | Who decides it   |
| ------------- | --------------------------------------------------------------------------------------------------------- | ---------------- |
| Subscriber id | Opaque identifier of the person following. Your user id, or their email — whatever your app already uses. | Your application |
| Resource id   | Opaque identifier of the thing being followed.                                                            | Your application |
| Subscribed at | When the person clicked. Used for point-in-time resolution.                                               | Notifizz         |
| Display data  | Optional name, email and avatar URL. **Empty for anything the subscribe widget creates.**                 | Your application |

One person can subscribe to any number of resources, and the pair *(subscriber, resource)* is unique: clicking subscribe twice produces one record, not two. Unsubscribing something that was never subscribed does nothing and reports success.

Both ids are opaque strings minted by your application. Notifizz never parses them, never validates them against a list, and never invents one.

### A resource is a group

The widget calls it a `resourceId` because that is what it means on the page — the card, the thread, the project that carries the button. On the campaign side the same value is called a **group**, because that is what it has become: the set of people who subscribed to it.

They are the same string. `proj_4a19f` on the button is `proj_4a19f` in the event that later notifies its subscribers. Pick ids that are stable for the lifetime of the thing they name — renaming a project must not renumber its group, or its subscribers are silently orphaned.

## Who subscribes, and how

Subscriptions are created by the person subscribing, through the [subscribe widget](/docs/sdks/subscriptions/widget) embedded in your own interface. There is no import, no CSV, no admin screen that subscribes somebody on their behalf.

That is a deliberate constraint rather than a missing feature. The click *is* the consent record: the person was signed in to your product, on the page of the thing they chose to follow, and they pressed the button. Nothing else in the chain has to reconstruct intent afterwards.

## What Notifizz stores

Very little, and only for as long as the subscription lasts.

* **The subscribe widget sends the subscriber id and nothing else.** A subscription created from a page in your app carries no name, no address, no avatar.
* Display data exists in the record only if your application supplies it explicitly. When it does, it was collected with the same click.
* **Unsubscribing deletes the row.** The subscription and any display data attached to it disappear together — there is no soft delete, so a re-subscription later is a genuinely new record with a new timestamp.

This is the same posture as the rest of the platform: see [privacy friendly](/docs/concepts/privacy-friendly) for how Notifizz avoids holding a copy of your user base.

### Reaching a subscriber on a channel

The consequence of storing so little is worth stating plainly: **a subscriber id is not an address**. Before a subscriber can receive an email, something has to turn that id into one.

Two ways, both normal:

1. **The id already is the identity the channel needs.** If your app passes the signed-in user's email as the subscriber id, an email campaign has what it needs. If it passes the same user id your notification-center widget authenticates with, the notification center has what it needs — see the identity note in [recipients](/docs/concepts/recipients).
2. **An [enricher](/docs/why/enrichers) resolves it.** The orchestrator hands your enricher the subscriber ids and gets back the addresses and the personalisation fields, live, at notification time. Nothing is copied into Notifizz beforehand.

Choose once, per channel, and stay consistent: [recipient ids must be stable over time](/docs/concepts/recipients#how-recipients-are-recognised) or a multi-message sequence loses track of the person mid-way.

## From a subscription to a notification

```mermaid theme={null}
sequenceDiagram
    participant User as Your user
    participant App as Your application
    participant N as Notifizz
    User->>App: clicks Subscribe on a resource
    App->>N: subscription recorded (subscriber id + resource id)
    Note over App,N: later — something happens to that resource
    App->>N: event carrying the resource id
    N->>N: campaign resolves the group's members
    N->>User: notification
```

Four things have to line up:

1. **A campaign whose recipient is Subscribers.** Notifizz provisions this recipient type for your organisation automatically once the subscribe widget is enabled; you pick it when configuring the campaign, like any other recipient.
2. **An event that carries the resource id** in one of its properties. Any event will do — your own `project.comment_added` is as good as the shortcut the SDKs ship. What matters is that a property holds the group id, or an array of them.
3. **An orchestrator that has been generated since.** The orchestrator is what reads that property and resolves the group; a campaign whose configuration changed after generation must be regenerated.
4. **A way to reach each subscriber on the campaign's channel**, as described above.

The resolution itself happens **at send time**, on the members the group has at that moment. This is the same principle as enrichers, applied to audiences: Notifizz does not keep a materialised list waiting.

### Never a list inside the event

It is tempting to compute the audience in your application and put it in the event payload. Notifizz refuses that shape on purpose, and the orchestrator is instructed to ask you which property carries the group id rather than accept a list of people.

The reason is timing. A list built when the event fires is a snapshot of that instant, which may be minutes or hours before the message is actually rendered and sent — long enough for someone to have unsubscribed. Resolving from the group id at send time closes that window, and it keeps your application from having to know who is subscribed to anything.

### Deduplication across groups

An event can name several groups at once — a comment on a document that belongs to two projects, a deployment that touches three services. Someone subscribed to more than one of them is resolved **once**, not once per group, and the notification records every group they matched.

Without that rule, the person following your product most closely is exactly the person who gets three copies of the same message.

### Point in time

Membership is normally resolved as of *now*. When your event carries an `occurredAt` — a replay, an offline batch, a history backfill — the group is resolved **as it stood at that moment** instead, so a catch-up run notifies the people who were actually following the resource back then rather than the people following it today.

`occurredAt` cannot be in the future, beyond a small tolerance for clock drift: a value that has not happened yet would let a send target a state that does not exist. Omit it in the ordinary case, where you emit the event as the thing happens.

One edge is worth knowing, because unsubscribing erases rather than archives: someone who unsubscribed and re-subscribed *after* the instant you are replaying is treated as not subscribed then. The record that would have proved otherwise was deleted, deliberately.

## Subscriptions are not notification preferences

Two different mechanisms, both about consent, easy to confuse:

|                     | Subscription                            | Unsubscribe categories                                  |
| ------------------- | --------------------------------------- | ------------------------------------------------------- |
| Question it answers | Do I want to hear about **this thing**? | How much **promotional mail** does this address accept? |
| Granularity         | One resource                            | One category of campaign                                |
| Who sets it         | The person, in your product             | The person, from the unsubscribe page in an email       |
| Where it lives      | Attached to a subscriber id             | Attached to an email address                            |
| Effect when off     | The person is not in the group          | The address is dropped as the mailing fans out          |

They stack rather than override. A subscriber to `proj_4a19f` who has opted out of promotional mail still receives the transactional notifications about that project, and still does not receive the promotional ones. The consent check runs after the audience is resolved and before the mailing fans out — being in a group has never been permission to mail somebody.

## FAQ

<AccordionGroup>
  <Accordion title="Can I subscribe someone from my backend, in bulk?">
    No. Subscriptions are created by the person subscribing, through the widget in your own interface — that click is what makes the record meaningful. If you need an audience you assemble yourself, that is a different mechanism: resolve it with an [enricher](/docs/why/enrichers), or launch a [broadcast](/docs/concepts/broadcasts) against a CRM segment.
  </Accordion>

  <Accordion title="What identifier should I use as the subscriber id?">
    Whatever your application already treats as the person, provided it is stable. If your notification center is the target channel, use the identity your widget authenticates with — the two must match exactly or the message is delivered and never appears in the inbox. If email is the target and you have no better id, the address itself is a legitimate choice.
  </Accordion>

  <Accordion title="Someone unsubscribed. Can I see that they used to follow the project?">
    No, and that is the design. Unsubscribing deletes the record and everything attached to it. What you can see is who is subscribed right now.
  </Accordion>

  <Accordion title="Where can I see who is subscribed to something?">
    In your own interface. Every mounted subscribe widget already exposes the list for its resource — that is what the avatar stack renders, and the [React hook](/docs/sdks/subscriptions/widget#custom-ui-with-usesubscription) hands you the same data without any markup. The list is per resource, because a subscription is a link to one resource rather than an entry in a directory.
  </Accordion>

  <Accordion title="What happens if the event names a group nobody subscribed to?">
    The group resolves to nobody and contributes nothing. An unknown or stale group id is not an error — the ids come from your application, and a campaign should not fail because a project was deleted. If every named group is empty, the campaign has no recipients and no message is created.
  </Accordion>

  <Accordion title="Can one person be subscribed to hundreds of resources?">
    Yes. The pair (subscriber, resource) is what is unique, so there is no per-person ceiling. What deduplication protects is the opposite case: one event naming many groups the same person belongs to.
  </Accordion>
</AccordionGroup>

## See also

<CardGroup cols={2}>
  <Card title="Subscribe widget" icon="code" href="/docs/sdks/subscriptions/widget">
    Installing the button, the React and Vanilla packages, appearance.
  </Card>

  <Card title="Subscriptions from your backend" icon="server" href="/docs/sdks/subscriptions/backend">
    Minting the secure-mode token and notifying a group.
  </Card>

  <Card title="Recipients" icon="user" href="/docs/concepts/recipients">
    How an audience is produced, and why ids must be stable.
  </Card>

  <Card title="Privacy friendly" icon="shield-halved" href="/docs/concepts/privacy-friendly">
    Consent, suppression and what Notifizz deliberately does not keep.
  </Card>
</CardGroup>
