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 three 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. - Three production patterns: from event properties, from an enricher, from a dynamic recipient source.
- 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[]. The system iterates: for each recipient, the channel processor renders the template + dispatches.
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). Use a stable, opaque id — never the email, which is mutable. 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.Three patterns
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).
3. From a dynamic recipient source
Saved audiences and segments — defined once in the dashboard, referenced from any campaign. The- Lifecycle campaigns whose audience is a saved segment (“re-engage users inactive for 30 days”).
- Marketing campaigns where the audience is curated, not derived from the triggering event.
- Cross-event audiences (“users who completed onboarding AND have a paid plan”).
Combining patterns
A campaign can use all three. A common pattern:role custom field lets the channel template branch — different copy per role, same campaign.
Validation rules
At message creation, every recipient is checked:| Rule | Behaviour |
|---|---|
id is a non-empty string | required |
email is a non-empty string | required |
email matches the email regex | required |
send metric fires.
Custom fields
Anything beyondid + email is custom. Common patterns:
| Field | Purpose |
|---|---|
displayName | Used in salutation: "Hello {{ recipient.displayName }}". |
locale | Drives template selection (en-US vs fr-FR). |
timezone | Time-aware formatting in the template. |
role | Branch the template on the recipient’s relationship to the event. |
unsubscribeUrl | One-time per-message link, useful for marketing campaigns. |
{{ 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 the channel processor iterates. 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; split via dynamic recipient source pagination if you genuinely need that scale.
Suppression and opt-out
Notifizz doesn’t ship a built-in suppression list — the orchestrator is the right place. Pattern: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?
Use a dynamic recipient source. The dashboard surfaces saved audiences (or computed ones via a query DSL); the orchestrator calls
fetchSource(audienceId) to resolve.Empty recipient list — why?
Empty recipient list — why?
Three causes: (1) the orchestrator’s filter excluded everyone (e.g. all users have
optOutMarketing: true); (2) the enricher returned empty; (3) you returned [] literally as a placeholder. The workflow trace shows the orchestrator’s output — inspect it.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?
Not as a platform feature. Implement at the orchestrator + enricher level: read a
suppressed field per user and filter. The advantage is that the suppression rule lives next to the campaign logic and shows up in the audit trail.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.