Recipients
Recipients are the audience a campaign delivers to. They’re produced server-side by the orchestrator, never picked at the SDK call site. This page covers the two patterns for producing them, the validation rules, and the gotchas.TL;DR
- Each recipient is at minimum
{ id: string, email: string }. Extra fields flow into the channel template. - Two production patterns inside a campaign: from the event’s own properties, or from an enricher. A one-off mailing to a CRM segment is a broadcast, a different kind of campaign.
idmust be stable over time — it’s how the sends of one sequence are tied to the same person. An id that changes mid-sequence drops that person from it.- Empty recipient lists short-circuit — no message is created and no metric fires.
- Invalid recipients (missing email or bad format) are skipped with a logged warning, not failed.
The shape
Recipient[]. For each recipient, the channel renders the template and delivers it.
Recipient validation runs server-side at message creation. Invalid recipients are skipped — see “Validation rules” below.
For the Notification Center channel,
recipient.id must exactly equal the identity your widget authenticates with (the userId it mounts). A mismatch delivers the notification but it never appears in the user’s inbox. Declare the field your widget uses under Settings → Notification Center → Authentification.How recipients are recognised
Notifizz does not keep your user base: your identifier is the source of truth, at every send. A multi-message sequence follows each recipient by theid your application returns — the identifier is what makes “the same person” the same person between two sends.
Consequence: if that identifier changes for someone mid-sequence, they’re no longer recognised as the same recipient and don’t receive the follow-ups. Use an identifier that is stable over time — your users’ internal id rather than any value that can change.
The anti-pattern to avoid is falling back between identity fields:
u.id gets set — an account creation, a CRM sync — the recipient’s identifier flips from their email to their business id, and the sequence loses them. It’s the heterogeneity that breaks, not the absence: an audience keyed entirely on emails is perfectly stable.
If your recipients have no internal identifier and are known by their email, Notifizz follows them on that email: creating their account mid-sequence doesn’t drop them out.
Where recipients come from
1. From event properties (direct)
The simplest case — the event already carries the recipient identifier.track(). Most “single-recipient” campaigns fit this pattern.
2. Via an enricher (live lookup)
Pass a stable id, fetch the rest at notification time.- The event payload should stay narrow (just identifiers), with detail fetched live.
- Multiple campaigns need different fields from the same user — define one enricher, reuse it.
- Email or other PII shouldn’t be in the event payload (compliance, privacy-friendly by default).
Audiences that don’t come from the event
Some mailings aren’t triggered by a user action at all — a product announcement, a newsletter, a migration notice. That’s a broadcast: the audience is a CRM segment, read live at launch. The orchestrator still runs, but its job there is to map each record onto the template’s variables, not to find the audience. Inside an event-driven campaign, an audience wider than the event’s subject — every admin of a team, every watcher of a document — is still pattern 2: an enricher that returns a list instead of a single record.Combining both
A campaign can use both patterns at once. A common one:role custom field lets the channel template branch — different copy per role, same campaign.
Validation rules
At message creation, every recipient is checked:
Recipients that fail are skipped with a logged warning — the campaign doesn’t fail, but you don’t get a message for that recipient. Inspect the workflow trace and the application logs to identify skipped recipients.
If the recipient list is empty, the system also short-circuits with a warning — no message is created, no
send metric fires.
Custom fields
Anything beyondid + email is custom. Common patterns:
The channel template has access to the full recipient object as
{{ recipient }} (or your variable system’s equivalent). See brands and variables for variable resolution rules.
Audience size
Recipients are processed sequentially per workflow instance — the orchestrator returns the full list, then each recipient is delivered in turn. There’s no hard cap, but practical limits apply:- Per-workflow-instance — sub-second per recipient on healthy infrastructure. A 10,000-recipient campaign takes around 10 seconds end-to-end.
- Total throughput — bound by the queue’s parallelism. Burst-fire scenarios benefit from
transactionalpriority for the urgent campaigns. - Memory — the orchestrator’s return value is held in memory. Avoid > 100k recipients per single instance; paginate the enricher that produces the audience if you genuinely need that scale.
Suppression and opt-out
Opt-out is a platform feature — you don’t re-implement it in the orchestrator. It is enforced after the orchestrator returns its list and before fan-out, recipient by recipient. On email, two layers apply, in this order:
The suppression check runs first and always overrides a preference. It is fail-closed: if the list can’t be read, the recipient is dropped rather than emailed. Emailing a suppressed address because of a transient glitch is the one failure that can’t be taken back.
The same promotional preferences apply to web push. Notification Center consent is applied when the inbox is read, not at send time.
Nothing is swallowed: recipients dropped this way are counted in the campaign’s stats, under Address suppressed (bounce, spam or deletion) and Unsubscribed, next to the other drop reasons.
What gets stored to make this work — hashes only, and for how long — is covered in privacy friendly.
Your own business rules
The platform layer answers “this person must never be emailed again”. A business rule — “skip accounts on the free plan”, “skip anyone who already received this offer this month” — is campaign logic: expose the flag on the enricher that already fetches the recipient, then prompt the AI to gate the send on it. It complements the suppression list; it never replaces it.FAQ
Send to one specific user from the SDK?
Send to one specific user from the SDK?
Put
userId in the event properties and let the orchestrator return that user as the only recipient. The SDK never picks recipients — that’s the deliberate design.Send to a segment?
Send to a segment?
A mailing to a segment is a broadcast — the segment lives in your CRM and is read live at launch. Inside an event-driven campaign, a wider audience comes from an enricher that returns a list.
Empty recipient list — why?
Empty recipient list — why?
Three causes: (1) the enricher returned nothing; (2) the campaign’s conditions excluded everyone; (3) every resolved recipient was dropped downstream — suppression, unsubscribe, or failed validation. The workflow trace shows the orchestrator’s output, and the campaign’s stats break the drops down by reason.
Recipient missing `email` field — what happens?
Recipient missing `email` field — what happens?
Skipped with a logged warning. The campaign doesn’t fail; the other (valid) recipients still receive the message. Inspect the warning to identify the broken recipient and fix upstream (data, enricher, or orchestrator logic).
Cap audience size?
Cap audience size?
No platform-imposed cap, but practical limits apply (see “Audience size” above). For very large audiences, split via pagination or fire one event per recipient. For “soft caps” (e.g. “max 1000 recipients per campaign per day”), implement at the orchestrator level — fetch the count, refuse if over.
Exclude a user (suppression list) — built in?
Exclude a user (suppression list) — built in?
Yes. The suppression list is a platform feature: per organisation, holding email hashes only, fed automatically by hard bounces, spam complaints and erasure requests, and applied before fan-out — fail-closed, and ahead of any subscription preference. See Suppression and opt-out above. A business exclusion (“not to free-plan accounts”) is a different thing, and that one does belong in the campaign logic.
See also
Orchestrator
Where the recipient logic lives.
Enrichers tutorial
Live data lookups for recipient details.
Events
What gets passed in
event.properties.Brands and variables
Where recipient fields are referenced from templates.