$androidpush / $iospush token per mobile app under the same workspace. When you trigger with tenant_id, SuprSend resolves the user against that tenant’s overrides layered on top of their global profile.
How it works
Every user still has a global profile — the one you create today by calling the user API without atenant_id. On top of that, you can now create a per-tenant profile for any tenant the user is associated with. At send time, SuprSend produces the effective profile by shallow-merging the two: per-tenant values win, and any key not set at the tenant level is picked from global.
The merge is shallow — one level deep, per key. If a key exists in the per-tenant profile it replaces the global value entirely; if it doesn’t, the global value is used. This applies to both properties (custom keys like role, plan) and channel identities ($email, $slack, $androidpush, etc.).
tenant_id continues to resolve to the global profile exactly as before — no behavior change for existing customers who don’t adopt this feature.When to use it
Reach for user-tenant mapping when a singledistinct_id legitimately represents the same person across multiple tenants, but their notification setup differs per tenant. Common scenarios:
- Same user, different push tokens per app. A workspace runs multiple mobile apps (each a tenant); the user has separate
$androidpush/$iospushtokens per app install, and a push for one app should never be routed to another app’s token. - Tenant-scoped role or plan properties. The user is “admin” on
projectAand “viewer” onprojectB— template branches onrole. - Different work email per business line.
$emailon the global profile is the personal address; per-tenant$emailis the tenant-specific work address.
When NOT to use it — user-tenant mapping vs neighboring approaches
User tenancy mode — shared vs exclusive
A workspace-level setting,user_tenancy_mode, controls whether a user can belong to more than one tenant. Configure it under Settings → General → Multi-Tenant User Mapping in the dashboard, or via the workspace config API (PATCH /v1/{workspace}/config/).
shared for workspaces created before this feature shipped, exclusive for new workspaces.

Channel fallback — controlling what leaks from global
The shallow merge is convenient but leaky. If a user has$androidpush on their global profile and only overrides $email on the suprstack tenant, a push send under suprstack still uses the global Android token — which is exactly what you don’t want when the two tenants are separate mobile apps installed on the same device.
Channel fallback is the workspace-level control that decides which channels are allowed to fall back to the global profile when the tenant profile has no override. Configure it under Settings → General → Multi-Tenant User Mapping or via PATCH /v1//config/.
allowlist mode with ["$email", "$sms", "$whatsapp"] — safe channels fall back, while $androidpush, $iospush, $webpush, $inbox, and $slack are dropped unless the tenant profile carries its own value.
Default-tenant exemption
A trigger withtenant_id = null (or no tenant_id at all) uses the default-tenant path. By default, this path is exempt from the fallback rule — every channel falls back, since the default tenant represents “no scoping.” Set user_tenancy_channel_fallback_applies_to_default_tenant: true if you want the same allowlist to govern default-tenant sends too (useful when the default tenant is treated as a real customer environment rather than a catch-all).
Associating a user to a tenant
Every route below writes to the same per-tenant profile store — pick whichever fits your flow. The body accepts the same fields as Edit User Profile (channels, properties,$timezone, $locale, $preferred_language).
- Dashboard
- REST API
Open the tenant's Associated Users tab

Add the user

Save


exclusive mode, calling this on a user already mapped to a different tenant returns 400 with a message like user is already associated with a different tenant. Delete the first mapping before creating the new one.?tenant_id=<tenant_id> — the response includes each user’s tenant-scoped properties and channel overrides alongside their global profile.
Triggering a workflow with tenant context
Any trigger — workflow API, event, broadcast, or object fan-out — accepts tenant context and produces the effective profile before evaluating the workflow. Settenant_id at the top level of the trigger. It applies to every recipient and the actor:
$email, $sms, properties) to be written against a specific tenant rather than the user’s global profile, add $context.tenant_id on that recipient. The same $context.tenant_id is accepted on the actor object.
tenant_id at the trigger level is the send-time tenant context. It does not need to match a tenant the user is already associated with — under shared, an unrelated tenant_id simply resolves to the global profile since there’s nothing to merge. Under exclusive, an unrelated tenant_id returns 400.
Deleting a user-tenant mapping
Removes the per-tenant profile fragment for that user-tenant pair. The global profile is untouched, and the user remains in the workspace. Delete it either from the dashboard or via API.- Dashboard
- REST API
Key behaviors and constraints
- Global profile is untouched — creating per-tenant profiles never modifies the global one. A user with no per-tenant profile behaves identically to today.
- Shallow merge, per-tenant wins — object-typed values are replaced, not deep-merged. If
properties.addressexists on both profiles, the per-tenant object replaces the global one entirely. - Isolation is per-workspace —
user_tenancy_modeapplies to every tenant in the workspace; it can’t be set per tenant. - Fallback is per-channel — governed by the workspace-level
user_tenancy_channel_fallback_mode+user_tenancy_channel_fallbacklist; not settable per tenant or per user. - The default tenant behaves like global on the trigger path — unless
user_tenancy_channel_fallback_applies_to_default_tenantis enabled. - Preferences are separate — per-tenant user preferences already exist and are governed by the preference APIs. User-tenant mapping stores properties and channel identities, not preferences.
Troubleshooting
400 — 'user is already associated with a different tenant'
400 — 'user is already associated with a different tenant'
exclusive mode and this user already has a per-tenant profile on another tenant. Either delete the existing mapping first, switch the workspace to shared (contact support@suprsend.com), or use a different distinct_id for the new tenant.404 — 'tenant not found' on the upsert call
404 — 'tenant not found' on the upsert call
tenant_id in the URL doesn’t exist in the workspace. Create the tenant first via Managing Tenants, then retry.Notification went to the wrong push device or channel identity
Notification went to the wrong push device or channel identity
$androidpush token or $email) to the per-tenant profile so it wins during the merge.Trigger with tenant_id silently used the global profile
Trigger with tenant_id silently used the global profile
shared mode, an unrelated tenant_id (one the user isn’t mapped to) simply falls through to the global profile — there’s nothing to merge. Double-check the tenant_id spelling and confirm the user is actually associated with that tenant on the Associated Users tab. If you want the API to reject unrelated tenants outright, use exclusive mode.Merged profile in the dashboard doesn't match what got sent
Merged profile in the dashboard doesn't match what got sent
tenant_id you passed in the trigger — a mismatched tenant filter shows a different merged profile than the send actually used.FAQ
Do I have to map every user to a tenant?
Do I have to map every user to a tenant?
What happens if I trigger with a tenant_id the user isn't associated with?
What happens if I trigger with a tenant_id the user isn't associated with?
shared, the send resolves against the user’s global profile (nothing to merge). Under exclusive, the API throws an error because the user-tenant combination is invalid.How does this interact with tenant properties like logo and colors?
How does this interact with tenant properties like logo and colors?
$brand.logo, $brand.primary_color, etc.) is unchanged — it comes from the tenant object itself, not from the user’s per-tenant profile. See Tenants.Does deleting a user's tenant mapping delete the user?
Does deleting a user's tenant mapping delete the user?
Does deleting a tenant remove all its user mappings?
Does deleting a tenant remove all its user mappings?
What if I use Segment group calls?
What if I use Segment group calls?
Can I auto-detect the tenant for a user with only one association?
Can I auto-detect the tenant for a user with only one association?
tenant_id (or omit it to use the global profile).