distinct_id, and their profile stores everything SuprSend needs to reach them — channel identities ($email, $sms, push tokens, Slack access token, etc.), custom properties (plan, role, timezone), and preferences.
Without a stored profile, you would have to pass every channel identity and property on every trigger. Storing them once against a distinct_id lets you reference the user by ID from then on — SuprSend fills in the channels, applies preferences, and routes on the properties automatically.
How it works
You identify a user once — either from your backend SDK, a client-side SDK, or a third-party connector like Segment. From then on, every workflow trigger that names theirdistinct_id picks up their channels, properties, and preferences automatically.
Users are workspace-scoped — a
distinct_id in your staging workspace is a
different user from the same distinct_id in production.Types of users
SuprSend distinguishes users along two axes: whether they’re stored (identified vs anonymous), and what role they play in a trigger (recipient vs actor).Identified vs anonymous
- Identified user — the default. You create a persistent profile with a
distinct_id; channels, properties, and preferences are saved and reused across triggers. Use this for anyone you’ll notify more than once. - Anonymous (transient) user — set
is_transient: trueon therecipientsoractorpayload of a single trigger. Channels and properties are used for that send only; nothing is saved. Use this for unregistered users, one-off system emails, or notifications to raw email/phone addresses you don’t want polluting your user list.
Recipient vs actor
Every workflow trigger references users in two possible roles:- Recipient — the user (or object) receiving the notification. Passed in the
recipientsarray. Their channels, properties, and preferences drive delivery. - Actor — the user who initiated the action being announced (e.g. the person who commented, invited, or approved). Passed as
actorin the trigger. Their properties are available in the template as$actor.name,$actor.avatar, etc. — an actor is never notified themselves.
distinct_id can appear as a recipient in one trigger and as an actor in another; there is no separate “actor user” record.
Creating users
You can create or update a user from any of the routes below. All of them upsert againstdistinct_id — creating the profile the first time, patching it on subsequent calls. Keys you send are updated; keys you omit stay as they were.
A typical payload sets one or more channel identities (each is a list) plus any custom properties you want to reference in templates or workflow conditions.
Backend SDK / HTTP API
Call the Create / Update Users API or a backend SDK from your application server. Best for server-of-record patterns — sync on signup, subscription change, or profile edit.$set, $unset, $append, $remove).
Client-side SDK
Identify the user from the browser or mobile app after they’ve authenticated. Best when you also need to capture in-app events or register device push tokens. Client-side identification uses a public API key plus a short-lived user token minted by your backend — see client authentication.Inline in a trigger
Pass channel identities and properties directly on therecipients object of a workflow trigger — SuprSend upserts the user before sending. Best for lightweight onboarding flows where the trigger itself carries the freshest data, or for anonymous sends via is_transient: true.
"$skip_create": true. To send without any stored profile, use "is_transient": true and omit distinct_id.
Third-party connector
Sync users automatically from Segment. Everyidentify call from Segment maps to a SuprSend user upsert — userId becomes the distinct_id, and everything under traits syncs as properties or channel identities (with a per-key mapping you configure once). Best if user identity already flows through Segment in your stack — you get user sync without any additional integration code.
Mapping users to other entities
A user can be associated with three other primitives in SuprSend — a tenant, an object, or a list. Each mapping serves a different purpose, and a single user can participate in all three simultaneously.User ↔ Tenant
How users relate to tenants depends on a workspace-level setting,user_tenancy_mode:
shared(default for existing workspaces) — onedistinct_idcan be associated with any number of tenants. On top of a shared global profile, you can store a per-tenant profile per tenant the user belongs to. At send time, SuprSend shallow-merges the global profile with the per-tenant override for the trigger’stenant_id— per-tenant values win. Use this when the same person legitimately exists across multiple tenants and needs different channel identities or properties in each (e.g. per-tenant Slack tokens, arolethat differs per customer org).exclusive(default for new workspaces) — onedistinct_idcan be associated with at most one tenant. The first tenant a user is mapped to is final; associating them to a second tenant throws an error. Use this when tenants represent isolated customer orgs and users must never leak across them — the standard B2B SaaS multi-tenancy model.
User ↔ Object
Users subscribe to objects — non-user entities like teams, projects, or departments. A subscription can carry its own properties (e.g.role: "owner") that templates read as $recipient.subscription.role. Triggering a workflow on the object then fans out to every subscribed user (and any nested object subscribers), without listing them explicitly in the trigger.
Use object subscriptions for reusable “who’s watching this?” relationships — the membership lives on the object, not on the user, so adding or removing subscribers doesn’t require re-triggering anything. See Object subscriptions.
User ↔ List
Mapping users to a list is how you build the audience for two of the highest-volume notification patterns: campaigns and re-engagement flows.- Campaigns / broadcasts — send a one-shot message to everyone on the list at once (product updates, newsletters, launch announcements, promotional offers). Broadcasts are a single high-throughput send to every member, tuned for this pattern.
- Re-engagement workflows — trigger a workflow the moment a user enters or exits the list. Ship a welcome series to users joining the “signed up in the last 7 days” list; fire a win-back flow when a user drops out of “active in last 30 days.” The list itself becomes the signal — you don’t have to write the event.
Key behaviors and constraints
distinct_idis immutable — once created, it cannot be renamed. To merge two profiles created for the same person, use the user merge API.- Upserts, not overwrites — the user API patches by default; keys you don’t send are left untouched. Use edit operations (
$set,$unset,$append,$remove) for precise mutations. - Channel identities are lists —
$email,$sms,$whatsapp,$androidpush,$iospush,$webpush,$slackare all arrays. A user can have multiple addresses per channel; SuprSend attempts each until one succeeds unless you narrow it with Smart Delivery. - Reserved property prefix — keys starting with
$are reserved for SuprSend-managed fields ($email,$timezone,$preferred_language,$skip_create,is_transient). Use plain keys for custom properties. - Anonymous users leave no trace — a
is_transient: truerecipient is not searchable, has no preferences, and cannot be targeted in future triggers. Use it only for genuinely one-shot sends. - Workspace-scoped — the same
distinct_idin two workspaces (staging vs production, or two separate environments) refers to two independent profiles.
FAQ
What should I use as the distinct_id?
What should I use as the distinct_id?
Use a stable, unique identifier from your own system — typically your database’s user primary key. Avoid email addresses or usernames, because those can change and the
distinct_id cannot.Can one user have different channel identities per tenant?
Can one user have different channel identities per tenant?
Yes. Set them up as per-tenant overrides via user-tenant
mapping. The global profile remains untouched; the
per-tenant values apply only when a trigger carries the matching
tenant_id.How do I send to someone who isn't a registered user?
How do I send to someone who isn't a registered user?
Pass
is_transient: true on the recipients payload along with the channel
identities you want to reach them on. No profile is created, and nothing is
stored. See anonymous user
triggers.What happens if I trigger with a distinct_id that doesn't exist?
What happens if I trigger with a distinct_id that doesn't exist?
If you pass channel identities inline on the recipient, SuprSend creates the
user on the fly (unless you set
$skip_create: true). If you pass only a
distinct_id and no channels, the trigger fails because there’s no way to
reach the recipient.Can a user appear as both actor and recipient in the same trigger?
Can a user appear as both actor and recipient in the same trigger?
Yes — for example, an “I approved my own request” notification. The same
distinct_id can be listed as actor and as a member of recipients. Their
properties resolve into $actor.* and $recipient.* independently.How do I delete a user?
How do I delete a user?
Use delete user API. This removes the profile, channel identities, preferences, and per-tenant overrides for that
distinct_id in the workspace.Next steps
Update user profile
Add channels, set custom properties, and edit profiles via SDK or API.
User-tenant mapping
Give a user different properties and channel identities per tenant.
Object subscriptions
Subscribe users to teams, projects, or other group entities.
Lists
Group users into audiences for broadcasts and list-entry workflows.
User preferences
Let users control which categories and channels they receive.
Trigger a workflow
Send a notification to a user by
distinct_id.