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

# Audience — The Person Behind Your Sends

> The per-person record: channel identities, live profile, campaign history. How to find someone, answer a right-of-access request, and erase a person.

# Audience

**Audience** is the list of people your campaigns have actually reached, one record per person, per environment. Open a record and you see who they are on each channel, what your own systems say about them right now, and every campaign that ever landed on them.

This page is written for **Product and Ops** — the people who answer "did this customer get the email?" and "we have a deletion request, what happens now?". The developer-facing view of the same subject — how a recipient list is produced, validated and filtered before a send — is [recipients](/docs/concepts/recipients).

## TL;DR

* **One record per person, per environment.** A record appears the first time a notification is delivered to that person; nobody is imported beforehand.
* **A person can hold several channel identities.** An application user id and an email address are separate identities until your code says they belong to the same person — see [audience identity](#one-person-several-identities).
* **The profile you see is fetched live from your systems** at the moment you open the record. Notifizz stores no traits.
* **Every campaign that reached the person is listed**, channel by channel, with what happened to each message.
* **This is the surface for a GDPR request.** Right of access is a structured export; right to erasure is a one-way purge that also writes the address to the suppression list.
* **Export and erasure are API operations today** — the screen shows the record, it carries no export or erase button.

## What lands in the list

A record is created the first time a notification is **delivered** to someone in that environment. Nothing else creates one: there is no import step, no contact upload, no CRM sync. If a person has never been sent anything in this environment, they are not in the list, and that is the honest answer to "why can't I find them?".

Each row shows the person's display name — whatever your profile resolver returned, falling back to their email address, falling back to your own user id — with the address underneath, a channel count when the record holds more than one identity, a badge when one of those identities has an active web push subscription, and the date they were last reached.

The list is **scoped to one environment** at a time — the selector sits at the top of the page. A person reached in production and in your sandbox has two independent records, which is what keeps test traffic out of your production answers.

<Note>
  **Audience** is a top-level entry in the dashboard sidebar. If you do not see it, it has been hidden from your own sidebar — the sidebar is customisable per user.
</Note>

## One person, several identities

Notifizz separates two things most platforms conflate:

* **A person** — one record, the thing you open on the Audience screen.
* **A channel identity** — how that person is addressable on one channel, in one environment.

| Identity type    | Identified by                                                                                   | Created when                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `AppUserSubject` | Your own user id                                                                                | A notification goes to a recipient carrying an `id`          |
| `EmailSubject`   | The email address — held hashed, plus the plain address while it is inside the retention window | A notification goes to a recipient known only by their email |

The identity API accepts two further types, for a browser identifier and for a custom channel; neither is produced by a delivery today.

When a delivery carries both your user id and an email address, one identity is created and it holds both. When the same person is reached later by email alone — a mailing built from a different source, say — a second, unrelated identity appears, and the list shows **two rows** for what you know to be one human being. Once the two are linked, they collapse into a single row carrying a channel count.

**Notifizz never guesses that two identities are the same person.** Merging is *declared*: your backend calls `identify()`, and only then do the two records become one. That is a deliberate refusal of probabilistic matching, which is fragile, hard to audit, and risks showing one person's notification to another. The developer reference is in the SDK pages — [Node](/docs/sdks/event-tracking/node-js#audience-identity), [Java](/docs/sdks/event-tracking/java#audience-identity), [PHP](/docs/sdks/event-tracking/php#audience-identity).

Every link and every effective unlink is journaled with the actor, the declared basis and the moment — the accountability trail a regulator asks for when you claim two identities were the same person.

## Reading a record

Opening a row shows a header — the person's name and address as far as they can be resolved — and then two blocks: what your systems say about them, and what Notifizz has sent them.

### The live profile

Notifizz holds **no customer traits** — no name, no plan, no segment, no custom fields. To show you a real profile, the record calls a resolver **your** backend exposes, at the moment you open it, and displays whatever it returns.

You declare it once in your backend SDK:

```ts theme={null}
notifizz.enrichProfileData({
  handler: async ({ id, email }) => {
    const user = await db.users.findByIdOrEmail(id, email);
    return {
      name: user.fullName,
      plan: user.plan,
      locale: user.locale,
      signedUpAt: user.createdAt,
    };
  },
});
```

The resolver receives `{ id, email }` — one person per call — and returns whatever fields you want on screen. Responses are cached for 24 hours, so opening the same record twice in an afternoon does not hammer your database.

Three states, and the screen tells you which one you are in:

| State                                               | What you see                                                                                    |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| No resolver declared                                | A placeholder with a **Configure enricher** button that shows the snippet to add                |
| Resolver declared, returned data                    | The fields, laid out as a grid                                                                  |
| Resolver declared, returned nothing for this person | A one-line note saying exactly that — the call succeeded, the person is unknown to your systems |

The distinction in the third row matters: "your resolver has nothing on them" and "you have no resolver" are different problems, and conflating them sends you debugging the wrong one.

### Campaign history

Every campaign that reached this person, each with its current status, and one line per channel underneath: which channel, how far the message got — **Sent**, **Opened**, **Clicked**, **Read**, or **Failed** with its reason — and the date it went out. This is the answer to "did they get it?", per person, without going through the whole [delivery history](/docs/operations/delivery-history-and-stats).

## Finding someone

The search box takes **an exact email address** or **an exact user id from your own system** — whichever the requester gave you. It matches one record or none; it is not a fuzzy search, and it does not search the profile fields your resolver returns (those are fetched per record, not indexed).

Search is scoped to the selected environment, like the rest of the page. A support request that says "I never got the reset email" is answered in the production environment; the same address in your sandbox is a different record.

## Answering a right-of-access request

GDPR Article 15 asks you to tell a person what you hold about them. For Notifizz, that is a short list — and the export endpoint returns it whole:

```http theme={null}
GET /v1/audiences/{audienceId}/export?environmentId={environmentId}
```

The `audienceId` is in the address bar once you open the person's record, together with the environment.

```json theme={null}
{
  "audienceId": "…",
  "exportedAt": 1755859200000,
  "subjects": [
    {
      "subjectId": "sbj_…",
      "type": "AppUserSubject",
      "identifier": "u_8421",
      "firstSeenAt": 1748736000000,
      "lastSeenAt": 1755772800000,
      "messageCount": 37,
      "subscriptions": { "cat_…": { "email": false, "inApp": true } },
      "rattachement": {
        "source": "api",
        "consentBasis": "marketing",
        "attachedAt": 1748736000000,
        "expiresAt": 1780272000000
      }
    },
    {
      "subjectId": "sbj_…",
      "type": "EmailSubject",
      "identifierHash": "…",
      "identifierClear": "alice@example.com",
      "firstSeenAt": 1748736000000,
      "lastSeenAt": 1755772800000,
      "messageCount": 37
    }
  ]
}
```

Read it as: one entry per channel identity, each carrying its identifier, its activity counters, its subscription choices, and the attachment record — how that identity came to belong to this person, on what declared basis, and until when.

Two details are worth understanding before you forward it to a requester:

* **Only an email identity carries `identifierHash` and `identifierClear`.** The other identities — your own user id, a browser subscription — carry the identifier they are addressed by, and nothing else. An entry without those two fields is not a truncated export.
* **`identifierClear` is present only while the plain address is still held.** Past the retention window it is purged and only `identifierHash` remains. An export that shows a hash and no address is not a bug; it is the retention policy having done its job.
* **The export covers identities, attachments and preferences.** The delivery detail — which campaign, which channel, what happened — is the campaign history on the record itself, and message content past its own retention window has been redacted. Timestamps are epoch milliseconds.

The policy behind all of that — what is stored, hashed, redacted, and for how long — is [privacy friendly](/docs/concepts/privacy-friendly).

## Answering an erasure request

GDPR Article 17 asks you to remove the person. Three routes, depending on how much you are erasing:

| Route                                                                           | Erases                                                                          |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `DELETE /v1/audiences/{audienceId}?environmentId={environmentId}`               | The whole person — every channel identity, every attachment, every subscription |
| `DELETE /v1/subjects/{subjectId}`                                               | One channel identity                                                            |
| `DELETE /v1/subjects/by-external-id/{externalId}?environmentId={environmentId}` | One channel identity, addressed by **your** user id                             |

Erasing the whole person removes, in one pass: the channel identities, their attachments, their subscription choices, the delivery history held for them, and any web push subscriptions registered against their identity. The response reports the counts — identities, attachments, subscription entries — and whether the suppression list was updated, so the operation leaves you something to file.

Erasing a single identity keeps the rest of the person. If it was the last one, the person's record goes too; otherwise the record's counters are recomputed so it keeps telling the truth.

<Warning>
  **Erasure writes to the suppression list, and it is one-way.** Before purging, the address is hashed and the hash is written to the suppression list for the configured number of years. This is deliberate: a person erased today must not be emailed again tomorrow because a fresh import reintroduced them. The address itself is gone — a hash cannot be emailed. An erasure-driven suppression is never lifted programmatically.
</Warning>

<Note>
  **These are console operations, not SDK calls.** The export and erasure routes authenticate as a signed-in member of your organisation — the same session the dashboard uses — not with an SDK key. There is no SDK shorthand for erasure, and no export or erase button on the Audience screen today.
</Note>

### Erasure is not an unsubscribe

They answer different questions and they are not interchangeable:

|                       | Unsubscribe                            | Erasure                                 |
| --------------------- | -------------------------------------- | --------------------------------------- |
| What the person asked | Stop sending me *this kind* of message | Remove me                               |
| What survives         | The whole record, minus a preference   | An address hash on the suppression list |
| Transactional mail    | Still delivered                        | Blocked as well                         |
| Reversible            | Yes, from the preference centre        | No                                      |

A person who unsubscribes should not be erased — you would destroy the delivery history you may need to prove you honoured them. See [notification preferences](/docs/concepts/notification-preferences).

## What the record does not hold

* **No traits.** Name, plan, segment, custom fields — fetched live when you open the record, never stored.
* **No profile graph.** Beyond the channel identities and their declared links, there is nothing to browse.
* **No cross-organisation correlation.** Email hashes are scoped to your organisation: the same address in another Notifizz customer's account produces an unrelated hash.
* **No inferred identity.** Two identities become one person because your code declared it, never because two payloads looked alike.

## FAQ

<AccordionGroup>
  <Accordion title="Someone contacted support saying they never got an email. Where do I start?">
    Search their address in the production environment. If there is no record, no notification was ever delivered to that address — the question moves upstream, to the campaign and its recipient resolution. If there is a record, the campaign history says whether a message went out, on which channel, and what became of it.
  </Accordion>

  <Accordion title="The same person appears twice. Why?">
    They hold two channel identities that have never been declared as the same person — typically an application user id from one campaign and a bare email address from another. Call `identify()` from your backend to merge them; the two records become one, and the counters are recombined.
  </Accordion>

  <Accordion title="The profile section is empty but I did declare a resolver.">
    The call succeeded and returned nothing for this person — the screen says so explicitly. Check that the identifier the record holds is the one your resolver looks up: it receives `{ id, email }`, and a resolver keyed on an internal id will find nothing for someone known only by their address.
  </Accordion>

  <Accordion title="Can I edit someone's preferences from the record?">
    Not from this screen. Preferences are the person's own, and they change them from the hosted preference centre reached from any promotional email — see [notification preferences](/docs/concepts/notification-preferences). The record is a read surface.
  </Accordion>

  <Accordion title="Does erasing someone remove them from campaign statistics?">
    The messages held for them are purged, so their individual delivery history disappears. Aggregate counters already recorded for a campaign are not rewritten — a statistic is not personal data once it no longer names anyone.
  </Accordion>

  <Accordion title="Why is the list per environment rather than per organisation?">
    Because a person reached in a sandbox is test traffic, and mixing it into the production answer would make every support and compliance question ambiguous. The suppression list is the deliberate exception: it applies across the whole organisation, so a person who opted out can never be reached from another environment.
  </Accordion>
</AccordionGroup>

## See also

<CardGroup cols={2}>
  <Card title="Notification preferences" icon="sliders" href="/docs/concepts/notification-preferences">
    Categories, the hosted preference centre, and what is checked before every send.
  </Card>

  <Card title="Privacy friendly" icon="shield-halved" href="/docs/concepts/privacy-friendly">
    What is stored, hashed, redacted — and the retention dials behind it.
  </Card>

  <Card title="Recipients" icon="user" href="/docs/concepts/recipients">
    The developer view: how a recipient list is produced and filtered.
  </Card>

  <Card title="Delivery history & stats" icon="chart-line" href="/docs/operations/delivery-history-and-stats">
    The campaign-wide view of the same deliveries.
  </Card>
</CardGroup>
