@suprsend/react SDK. If you want a ready-made preference UI without building from scratch, see the Embedded Preference Centre.1. Channel opt-in / opt-out
Users control what they receive on each channel. For every channel your app uses (Email, SMS, WhatsApp, Push, etc.), the user picks one of two options:- All - receive all notifications on this channel, except categories they’ve explicitly turned off
- Required - receive only notifications from categories marked as mandatory (Can’t Unsubscribe)
channel_preferences array in the API response drives the global All / Required control. Each item has a channel name and an is_restricted boolean: false means All, true means Required. The per-category channel preferences live under sections[].subcategories[].channels.

Channel-level preferences
- Go to Developers → Preference categories and select a root-category (System, Transactional, or Promotional)
- Optionally, click
+Sectionto create a section (for example “Product”, “Billing”). Sections are used to group sub-categories on the preference page. Section doesn’t have it’s own preference. - Click
+Sub-Category, fill in the name, select the section (if set), and set a default preference (On, Off, or Can’t Unsubscribe) - Click
Publish Changes
getPreferences() in your parent component on page load and pass the result down as preferenceData. The response gives you two independent parts:
channel_preferences- render one row per channel with All / Required radio buttons. Readis_restrictedto know which is currently selected:false= All,true= Required. CallupdateOverallChannelPreferencewithChannelLevelPreferenceOptions.ALLorChannelLevelPreferenceOptions.REQUIREDwhen the user changes their selection.sections[].subcategories[].channels- per-category channel checkboxes that let users turn individual channels on or off within a specific notification type. This is covered in detail in Section 2.
2. Preference category toggles
Users turn each notification type on or off individually - for example, keeping “Invoices & receipts” on while turning off “Sales Newsletter” - and optionally choose which channels receive each type. This is the standard notification settings page that most apps need. The response gives you sections (top-level groupings like Product, Billing, Sales) containing subcategories (the individual notification types). Each subcategory has a toggle and a list of channel checkboxes.
Category-level preferences
- Go to Developers → Preference categories and select a root-category
- Click
+Sectionto create sections (for example “Product”, “Billing”, “Sales”). Sections are used to group sub-categories on the preference page. Section doesn’t have it’s own preference. - Click
+Sub-Categoryfor each notification type, select its section (if set), and set a default preference (On, Off, or Can’t Unsubscribe). You can also choose which channels should be opt_in by default in case ofOnpreference. - Click
Publish Changes
getPreferences() in your parent component and pass preferenceData as a prop. Render sections as headings and subcategories as rows, each with a toggle and channel checkboxes below it.
Three things to handle carefully:
is_editable: falseon a subcategory means the category is set to Can’t Unsubscribe in SuprSend. Disable the toggle - users cannot opt out.is_editable: falseon a channel inside a subcategory means that specific channel is mandatory for that category and cannot be turned off. Disable the channel checkbox independently of the subcategory toggle. A category can haveis_editable: true(user can toggle it off) while individual channels within it haveis_editable: false(those channels cannot be removed).channelsmay be empty or absent (for example whenshow_opt_out_channelsisfalsefor opted-out categories). Use optional chaining (subcategory?.channels?.map(...)) to handle this safely.
updateCategoryPreference, updateChannelPreferenceInCategory, updateOverallChannelPreference) are optimistic - the UI updates immediately and the actual API call happens in the background with a 1-second debounce. Set up these emitter listeners once when loading preferences to handle the confirmed result and catch server-side errors:3. Digest frequency
Instead of one notification per event, users choose how often they want a summary - Daily, Weekly, or Off. A user who picks Daily gets one email at 09:00 summarising everything that happened that day instead of a notification for every single event. This works by storing the user’s chosen schedule as a custom property on their profile usinguser.set(). The workflow’s Digest node reads this property at runtime via a dynamic schedule and fires on the schedule the user set.

Digest preferences
- Go to Developers → Preference categories and create a sub-category for the notification type you want to digest (for example “Activity Summary”). Click
+Sub-Category, fill in the name, and set a default preference - Click
Publish Changes - In your workflow, add a Digest node and set it to use a dynamic schedule. Point the dynamic schedule key at the user profile property where you store the schedule — for example
."$recipient".digestSchedule.category-slug(wherecategory-slugis the slug of preference category). The Digest node will automatically use whatever schedule the user has saved for that category. See Dynamic schedule
user.set().
4. Hide categories by tenant plan
- The opted-out categories and channels are removed from
getPreferences()responses for all users in that tenant - they won’t see them on the preference page. - Notifications for those categories are not delivered to the tenant’s users.
- These are hard overrides - neither the tenant admin’s default preferences nor individual user preferences can re-enable them. See Preference Evaluation for the full resolution order.

Free-plan users see only core categories; paid-plan users see the full set
- Global Channel Opt-out — block one or more channels (for example Email, In-App Inbox) across all categories for the tenant. Notifications on blocked channels are never delivered to any user in the tenant.
- Category Opt-out — toggle a category off for the tenant. The category is removed from the tenant’s preference API responses and notifications for that category are not delivered.
- Opt-out Channels in the Category — block specific channels within a single category (for example disable SMS for “Newsletter” but keep it enabled for other categories).

Global tenant preference page in the SuprSend dashboard
tenantId to getPreferences(). Categories opted out at the tenant level are excluded from the response server-side - your rendering code stays the same regardless of tenant plan.
5. Filter categories with modules, roles, or departments
Not every user needs to see every preference category. A sales user only cares about Sales Newsletter, Cart & checkout, and New Leads - they don’t need Budget Alerts or Expense Reports. A finance user is the opposite. And in a multi-module product, each module’s alerts should live on its own tab. This uses tags. You assign tags to preference categories or sections in SuprSend, then pass the relevant tag togetPreferences() - SuprSend returns only the matching categories. When a tag is assigned at the section level, it automatically applies to all subcategories under that section via effective_tags.
Tags are flexible - they can drive two common patterns:
- Automatic filtering by role or department: Your app reads the user’s department/role/team from their profile or session and passes the tag silently. No filter UI is needed.
- User-selected filtering by module: Your app shows a visible tab selector and re-fetches preferences with the selected tag.
- Tag per section: Click
+Sectionto create a section (for example “Sales”, “Finance”). Add a tag to the section (for examplesales,finance). All sub-categories under that section inherit the tag automatically viaeffective_tags. ClickPublish Changes. - Tag per sub-category: Create a section with all sub-categories under it. Select each sub-category and add its own tag (for example
sales,finance,product). Filtering by tag returns that section with only the matching sub-categories.
getPreferences() via the tags parameter. Where the tag comes from depends on your use case - either derived from the user’s profile (automatic) or from a tab selector (user-selected). The tags parameter also supports logical filtering for more complex cases - see Filter categories with tags for supported operators (or, and, not, exists).
- Automatic (by role/department)
- User-selected (by module)

Jane D. (Sales team) sees Sales Newsletter, Cart & checkout, and New Leads

M. Kim (Finance team) sees Budget Alerts, Expense Reports, Invoice Reminders, and more
6. Tenant admin controls
visible_to_subscriber in the API.

Tenant admin customizing notification preferences for their organization
- First ensure your preference categories are set up under Developers → Preference categories and published
- Provide tenant admins access to configure categories via the Tenant Preference API. They can enable/disable categories, set default On/Off, and control channel availability - all scoped to their tenant
- Default preference (
preference: opt_in / opt_out / cant_unsubscribe) - sets whether the category is on, off, or mandatory by default for all users in this tenant. SuprSend evaluates preferences in the order: user preference → tenant default → category default. If a user hasn’t set their own preference, the tenant default applies. - Visibility (
visible_to_subscriber: true / false) - controls whether the category appears on the end user’s preference page at all. Set tofalseto hide and turn off categories that aren’t relevant to this tenant’s users. - Channel controls -
mandatory_channelsmakes specific channels non-unsubscribable (when preference iscant_unsubscribe),opt_in_channelssets which channels are on by default, andblocked_channelsdisables channels across all categories for the tenant regardless of individual user preferences.
tenantId to getPreferences() - the rendering is the same as Section 2.
7. User-defined alert threshold
Some notifications are only useful when they cross a user-defined threshold. A storage alert at 1% full is noise; at 80% it’s urgent. Users set their own trigger point, and the workflow only fires when the incoming metric crosses it. The threshold value is a custom user profile property - not a preference setting. The preference controls whether the alert is on or off and which channels it goes to. The threshold controls when the workflow fires.
Threshold alerts
- Go to Developers → Preference categories. Click
+Sub-Category, name it (for example “Usage alerts”) and set a default preference. ClickPublish Changes - Add a threshold condition in your workflow. You have two options depending on what you want to skip:
- Skip a step in the workflow — add a Branch node inside the workflow that checks the threshold (for example
usage_percent >= ."$recipient".pref_conditions.usage-alerts.threshold). If the condition isn’t met, the branch doesn’t execute but the rest of the workflow still runs. - Do not execute the workflow — add the threshold check as a trigger condition at the start of the workflow. If the condition isn’t met, the entire workflow is skipped and no notification is sent at all.
- Skip a step in the workflow — add a Branch node inside the workflow that checks the threshold (for example
getPreferences() in your parent component and pass preferenceData as a prop. The preference page for this pattern has two independent parts rendered together:
- Threshold picker - a number input. Saved via
user.set()underpref_conditions.usage-alerts.threshold(replaceusage-alertswith your actual category slug). - Category preference - the standard toggle and channel checkboxes for the “Usage alerts” category. Saved via
updateCategoryPreferenceandupdateChannelPreferenceInCategoryas usual.
8. Per-project preferences
tenantId passed to getPreferences(), and all reads and writes happen against that project’s tenant.

Project-level preferences - each project = tenant
- Create one tenant per project in SuprSend (via Tenants in the dashboard or the Tenants API). You can also create tenants via API
- For each tenant, configure which categories are enabled, what the defaults are, and which channels are available via the Tenant Default Preference API
- Ensure your preference categories are published under Developers → Preference categories
getPreferences(). Render the full preference UI - channel-level (All / Required per channel) and category-level (toggles + channel checkboxes).
updateCategoryPreference, updateChannelPreferenceInCategory, updateOverallChannelPreference) are automatically scoped to the tenantId from the most recent getPreferences() call - you do not pass tenantId directly to update methods. This means the SDK tracks one active tenant at a time globally. Avoid making concurrent getPreferences() calls with different tenantId values while updates are in flight - a concurrent fetch would change the active tenant context and cause any in-flight updates to be written against the wrong tenant.