# Product Updates Source: https://docs.suprsend.com/changelog/overview Logs of all the feature releases, improvements, and bug fixes in SuprSend.
## Preference Category Management APIs You can now programmatically **create, update, and commit preference categories** using the Management APIs — no dashboard required.\ This makes it easy to integrate category management into your existing workflows, scripts, and deployment pipelines. 👉 Also available via the [SuprSend CLI](/reference/cli-intro).\ 📘 See the [API documentation](/reference/create-update-category) to get started. ## 🚀 SuprSend CLI Beta - Ship Notification changes like code We're excited to announce the **[public beta of SuprSend CLI](https://docs.suprsend.com/reference/cli-intro)**, bringing full notification management to your terminal. Using CLI, you can manage and promote assets across workspaces, integrate with CI/CD, and treat notification changes just like code. SuprSend CLI ### What You Can Do * **Promote assets across workspaces** — move workflows, schemas, events, and categories between environments (e.g., staging → production) with `suprsend sync` or targeted pull/push commands. * **Automate with CI/CD Deployment** – Release notification changes through feature or bugfix branches, just like any other piece of code: version it, test it, and deploy it. * **Manage notification changes in Git** — pull assets locally, version them alongside your application code, and push updates as feature branches or bugfix releases. * **Treat notification infrastructure just like code** — review, branch, merge, and release with the same version control workflows you already use. ### Built for developers * **Code reviews for notifications** — keep your notification infrastructure in Git, track changes, and roll back when needed. * **Approval gates for production** — ensure no change goes live without review and approval. * **Work with assets locally** — create, edit, and test workflows, schemas, and translation files on your machine. * **Version control & rollback** — maintain change log and safely revert changes when required. *** This is a **beta release** — we’re actively gathering feedback and making improvements. So, feel free to report an issue and contribute to the project.\ 📘 Check out the [CLI documentation](/reference/cli-intro) to get started. ## 🤖 SuprSend MCP Server (Beta) — AI-Powered Notification Management Your AI agents, copilots, and LLM tools can now directly interact with SuprSend through natural language, making notification management as simple as having a conversation. MCP ### What You Can Do with SuprSend MCP **Everyday workflows with AI:** * **Trigger workflows on demand**\ *“Run the approval-required workflow for user John Doe to test my setup.”* * **Bootstrap test data**\ *“Create a sample user named John Doe and a tenant called acme-corp in my workspace.”* * **Manage preferences**\ *“Enable email notifications for marketing and disable SMS.”* * **Configure branding**\ *“Update the logo and primary color for the enterprise tenant.”* **Vibe-code with AI:** * Ask AI to fetch **setup guides, code examples, or integration snippets** directly from SuprSend docs and apply it in your application code. * Expose **safe, scoped endpoints** (via MCP) that wrap APIs with context, reducing guesswork and hallucinations. * Integrate with **LLM-based assistants** (Claude, Copilot, Cursor, Windsurf, etc.) to simplify notification setup with SuprSend. *** ### Compatible AI Tools Works with Claude, Cursor, Windsurf, and any MCP-compatible AI agent. ### Notes & Caveats (Beta) - * APIs, behavior, or scopes may change based on feedback. * We restrict destructive operations (e.g. deletes) initially to reduce risk. * We welcome your feedback — report issues and share feedback to help us harden MCP for production. *** ### Getting Started Start the MCP server and configure it with your AI tool. See our [MCP setup guide](/reference/mcp-overview) for detailed instructions. ## Send Notifications Only to Verified Channels in Sandbox Sandbox workspaces come pre-configured with SuprSend vendors for quick testing. However, we noticed some cases of misuse where test messages were being sent to unintended recipients. To prevent accidental spam and keep Sandbox safe, notifications can now only be sent to **verified channels**. You can set upto 5 verified channels for each channel type. Reach out to us if you need more. You can add and manage your verified channels from [developers -> Verified Channels page](https://app.suprsend.com/en/staging/developers/verified-channels). Verified Channels in Sandbox ## Test Mode: Test Notifications safely without sending to real users Testing notifications shouldn't mean worrying about accidentally pinging your customers. In most companies, teams end up redirecting notifications to shared inboxes like [qa@company.com](mailto:qa@company.com) or [dev@company.com](mailto:dev@company.com) just to avoid delivery to real users—while still being able to debug the full notification flow. Test Mode With [Test Mode](/docs/developer/test-mode), you can now replicate this real-world testing flow directly in our platform: * **Test end-to-end notification flow**: Add channels belonging to internal testers as test channels. In test mode, notifications to these channels are delivered normally—so you can preview messages on real devices. * **Set Up Test Channels**: You can add channels belonging to your internal testers as test channels. Delivery will not be blocked for test channels in test mode. This helps you see preview of the notification in your real device. * **Catch-All Routing**: Redirect all non-test notifications to a common channel (e.g., a QA inbox), making it easy to trace and debug every message in one place. This ensures you can confidently test notification workflows in an environment that mirrors production—without the risk of real users getting test messages. ## Validate workflow triger Payload using JSON Schema We’ve introduced **API-level JSON Schema validation** for workflow trigger payloads. This catches payload mismatches before execution, preventing runtime failures and ensuring consistent, correct notifications. JSON Schema Validation ### Why it matters When you trigger a workflow, you pass data (payload) that is used to resolve workflow variables and populate dynamic content in templates. Currently, If the payload does not include all the variables expected in the workflow, the execution may fail at different stages. With this change, Validation will happen at API level and there'll be: * **Fewer runtime failures**: Stop workflows from starting with missing or malformed data. * **Faster debugging**: Get a clear, structured error list at request time—no more hunting through multi-step logs. * **More reliable messaging**: Prevent partial runs, inconsistent behavior, and incorrect or incomplete notifications. *** ### How it works You can add JSON schema from [Schema page](https://app.suprsend.com/en/staging/developers/schema) and then link it to the workflow [Trigger step](https://docs.suprsend.com/docs/validate-workflow-payload#linking-schema-to-workflow) or trigger Event from [events page](https://app.suprsend.com/en/staging/events). * When you trigger a workflow, the payload is validated against a JSON Schema that describes the expected data used to resolve variables and populate dynamic content. * If the payload doesn’t match the schema, the Trigger API returns error response with a list of validation errors (e.g., path, expected type, missing fields). * If validation passes, the workflow proceeds as usual. *** ### Fixes and Improvements: Workflow slug validation at the API layer: If a referenced workflow slug isn’t available, the error is now returned directly in the API response (in addition to request logs) for faster debugging. This validation will only apply to new workflows created after this change. If you want to apply it all your existing workflows, reach out to SuprSend support. ## Tenancy social links update * Added support for TikTok in tenant `social_links`. * Twitter renamed to **x** in descriptions and examples (field name remains compatible as per API changes). * Updated social link icons for better visual consistency. ## Message logs revamp * **Redesigned UI** for seamless tracking of notification lifecycles. Quickly view delivery status, opens, clicks, and errors across all channels in a single log view. * **Entity-level visibility**: Drill down into logs by workflow, user, object, or list to understand exactly what happened in context. * **Advanced filtering**: Filter logs by status, workflow, template, channel, category, or time range to debug faster. * **Consistent date range filter** across all log pages, making it easier to trace the journey of a notification from request → workflow → final message delivery and it's interaction state. ## Message Logs Revamp ### Fixes and Improvements * **react-sdk (v0.3.0)** - Introduced a custom infinite-scroll component with robust Shadow DOM compatibility. * **web-components (v0.3.0)** - Enhanced Shadow DOM rendering support to ensure component isolation and consistent styling. ## Analytics 2.0 - faster, real-time, with one click filters to drill down into insights Analytics 2.0 Dashboard * **Real-time insights** → Trends update as messages go out. Track performance across channels and spot dips in engagement instantly. * **Workflow-level comparisons** → Compare workflows, templates, channels, and categories side by side to spot under performers and validate experiments. * **Know when your users opt-out** → See which channels/categories drive opt-outs so you can adjust before churn sets in. * **Over-messaging trends** → Track avg notifications per user, find patterns by category, and identify fatigue triggers to keep comms helpful—not noisy. * **Granular filtering** → Multi-select filters for workflow, tenant, template, channel, category, time range * **Centralized error tracking** → All API, workflow, and provider delivery errors in one place. Filter by tenant/workflow/template/channel, open the exact log, and debug in seconds. *** ## Sendgrid IP Pool support Enabled creation and management of SendGrid IP Pools, allowing granular control over email delivery, IP reputation, and segmentation of email traffic base on notification category. ### Fixes and Improvements * Added support to send slack messages using broadcast. ## Workflow Management APIs Released comprehensive Management APIs to programmatically create, update, and commit workflows. Supports dynamic workflow orchestration — from your platform or third-party systems — to automate creation and modification of workflows from your codebase. You can checkout the [documentation here](/docs/management-api-overview). Workflow Management APIs ## Proxy support in Java SDK Java SDK can now route outbound requests through HTTP/S proxies, enabling deployments behind corporate firewalls and network controls. ## iOS Native SDK Revamp with JWT based authentication & Preferences support The new iOS SDK now has our latest JWT authentication. You can use it to: * JWT-based auth for secure event ingestion, profile updates and push token management. * Support to add In-app Preferences Center in mobile apps with UI and example code available for quick setup. ### Fixes and Improvements * Flutter sdk released (v2.5.0) - Fixed an Android push client issue and added silent push support for background updates. ## Role based auth in AWS SNS In line with our ongoing efforts to enhance platform security, we’ve also enabled IAM Role- based auth in AWS SNS vendor. Previously, authentication required creating an **IAM User** and sharing long-term access keys. With **IAM Role-based auth**, you can grant **temporary, scoped access** without exposing sensitive credentials. ## New SMS Vendor: Bird We’ve added support for sending SMS using the new Bird APIs. The setup is straightforward with a simple vendor form to fill to get started, and full integration details are available here. ## SuprSend tracked Properties Now Available in Recipients Payload Recipient payloads now include key internal properties—like user type and their unique identifier—making them readily accessible for use in templates and workflows. → For users: `{“$type”: “user”, “distinct_id”: “xxxx”}` → For objects: `{“$type”: “object”, “object_type”: “xxx”, “id”: “xxx”}"` Use these properties to pre-fill form values, add conditional branching based on user type, or Create dynamic links using unique user IDs ## Workflow Conditions - Array Comparison Operators Now, find an element in array or find intersections between two arrays in workflow conditions. Example Use cases: * Send a notification to users whose role is one of `["admin","manager"]` * Notify tournament followers who have subscribed to any of the playing teams or players. Intersects & not-intersects operators ## Introducing Preference Tags Filter notification categories shown to users based on tags like role, team, or department—so Finance sees billing alerts, and Engineers see only error and anomaly categories. You can assign multiple tags to each preference category or section, and define complex logical expressions (e.g. role == "manager" && department in \["sales", "marketing"]) to dynamically show relevant preference categories per user. Great for building clean, personalized preference centers without bloating the UI. Preference Tag within Categories ## Documentation Revamp–Cleaner, Smarter, More Interactive We’ve overhauled our documentation experience to make it more consistent, intelligent, and user-friendly: * **Brand-Aligned UI**: The docs now match the look and feel of the SuprSend platform. * **AI-Powered Search**: Get smarter, faster answers with AI-supported search. You can also open documentation directly in **ChatGPT or Claude** for conversational, AI-driven assistance. * **Improved Readability**: Upgraded UI components provide a cleaner layout and better readability, helping you navigate and understand complex topics more easily. * **Interactive API Reference**: Try out API requests directly from the docs and view live responses in real-time—no need to switch tools. This revamp is part of our ongoing effort to make implementation faster, smoother, and more intuitive for developers. ## Cross Lookup User Subscriptions Easily view all of a user’s subscriptions—whether to **lists or objects**—in one place. The **Subscriptions tab** on the user details page now provides a centralized view for easier access to user subscriptions. User Subscription ### Fixes in workflows UI * Resolved an issue where newly published workflow versions wouldn’t appear without a page refresh (introduced after version history was added). * Fixed a bug in the test trigger modal where object suggestions incorrectly appeared when switching from API to event trigger. * Removed the success metric from delivery nodes where it's not relevant (except for Smart Delivery Nodes). ## Workflow Trigger Overrides Event-Based triggers now support overriding the actor, recipient, tenant, and object—directly within the workflow. This removes the need to resolve recipients in your code, allowing you to pass internal events as-is and dynamically resolve users and related context per workflow. Perfect for use cases like sending a daily digest to tenant admins or notifying internal account managers at a parent company—all from the same event trigger. Trigger Override Settings ## Clone content across template versions and languages Editing multi-lingual templates or doing A/B with different template content? Now, rollback to a version or copy designs between different languages by cloning within template. Clone Within Template GIF ### Fixes and Improvements * iOS Integration - Fixed the bitcode issue in xcode16 ## Role based auth in AWS SES and S3 connector In line with our ongoing efforts to enhance platform security, we’ve now enabled IAM Role- based auth in AWS connectors. Previously, authentication required creating an **IAM User** and sharing long-term access keys. With **IAM Role-based auth**, you can grant **temporary, scoped access** without exposing sensitive credentials. *** ### Fixes and Improvements * Added API name filter in request logs. This will help you drill down logs based on event and workflow name. ## In-App Inbox: French translation support The Inbox UI now supports automatic French translation! Just pass `language="fr"` when initializing the Inbox, and all static content will render in French—no extra setup needed. Available in `@suprsend/web-inbox` ≥ v0.6.0. More languages coming soon *** ### Fixes and Improvements * Released `suprsend-py-sdk==0.13.0` with latest user and object management APIs. * Fixed Email issue where tenant button was not showing cursor clickable on hover. ## In-App: Fetch cross tenant feed We’ve recently been hearing multi-tenant use cases where a user belong to multiple tenants and would want to see Inbox feed for all tenants in a single product. e.g., an account manager is handling multiple client accounts and need to see updates or daily reports linked to all their accounts in a single feed. You can now achieve this by passing `tenantId = *` while initializing the Inbox. ```javascript SuprSendInbox theme={"system"} interface ISuprSendInbox { workspaceKey: string distinctId: string | null subscriberId: string | null tenantId?: "*" ... } ``` ## Workflow - Step-by-Step Analytics You can now track consolidated view of users' workflow journey at each workflow step directly in the workflow graph. Track user entry, exit, drop-offs, branch followed, and node failures. You can also see workflow edit history and compare analytics across different workflow versions and time range. Workflow Analytics **Next up:** Deeper analysis into each workflow step - notification engagement (deliver, seen, click), failures, and AI-powered insights. ## Improvements: * Added data centre field in account settings to check where your data centre region. ## Batch - Flush First Item Immediately We've introduced a new setting in batch processing: [Flush First Item in Batch](/docs/batch#flush-first-item-immediately). Previously, batches were only sent once the batch window closed. Now, this setting allows the first trigger to flow past the batch immediately while subsequent triggers are batched within the specified time window. This helps you to build leading debounce logic in workflows, where users are notified immediately about critical updates like anomaly alerts, while other alerts are batched and sent at regular intervals until the issue is resolved. You can find this option in **batch -> advanced configuration**. ## Workflow - Relative Delay and Batch window Added the ability to set relative delays and batch windows in workflows. Previously, delays were fixed or dynamic, with the time difference always being based on the current time. With this update, you can now define delays relative to a future timestamp, often provided by your trigger payload. For instance, send a reminder 30 minutes after a task's due time or send feedback 5 minutes after an event or webinar. Relative Delay and Batch window ### Fixes and Improvements: * In Inbox drop-in popover component, we fixed scroll bar causing empty padding UI issue in macOS when **Show Scroll bars: Always** is enabled. * In Inbox drop-in popover component, action menu popup of last notification item was getting cropped. We have fixed this issue. * In Inbox drop-in popover component, in mobile view actions menu icon (3 dots icon) only appears on touching notification. After the bug fix, the actions menu icon will appear on all notifications in mobile view by default, removing extra touch interaction. ## Nested Objects - Choose the fan out depth Previously, when triggering workflows in [nested object hierarchies](/docs/object-subscriptions#adding-nested-object-hierarchies) (where one object subscribes to another), notifications would automatically fan out up to two levels—sending notification to object, its direct subscribers, and child object subscribers. Now, you have full control over how deep the fan-out should go. You can now set the [depth](/docs/object-subscriptions#object-subscription-fan-out-in-trigger) in the recipient payload, defining how far the workflow should propagate to fetch subscriptions. 🔹 Depth 0 → Notify only the object’s channels (e.g., Slack team, shared inbox).
🔹 Depth 1 → Notify the object’s channels + direct subscribers.
🔹 Depth N → Expand deeper into hierarchical subscriptions as needed. ```json theme={"system"} "recipients": [ { "object_type": "teams", "id":"finance", //optional parameter to define subscription fan-out depth in workflows "$object_subscriptions_query": { "depth": 0 } } ] ``` You can use this to build **Escalation Workflows** or **Tiered Customer Support Notifications**, send notification to a shared slack channel or customer support queue first and then escalate to individual users in case of no response in a given time duration. ### Fixes and Improvements: * \[SDK] [Object methods](/docs/java-objects) and [User APIs](/docs/java-create-user-profile) to fetch user and their subscription exposed in Java SDK * Added support to trigger multi-lingual templates in [broadcast](/docs/broadcast)
## New handlebars helpers - jsonParse and jsonPath We’ve added handlebars helpers to seamlessly handle JSON strings in the template editor: * `jsonParse` - Converts a JSON string into an object, making it easier to apply conditions or use JSON strings in merge tags. * `jsonPath` - Fetch data corresponding to a path within a JSON object. Works well with jsonParse to directly access nested data in JSON string without block helpers. ### Fixes and Improvements: * Opened up merge tag input to support handlebars helper in [email merge tags](/docs/email-template#adding-array-elements%3A). * Added support for handlebars helper in [display condition](/docs/email-template#display-email-blocks-based-on-condition). ## List entry/exit events in trigger You can now trigger a workflow when a user enters or leaves a list. Use this in the Wait Until node to stop reminders or dynamically route users in a workflow on list updates. Earlier, you could achieve the same by enabling event tracking on list updates. Now, you can simply add this logic in workflow without making any changes in list. This will help you build workflows on user lists like, send series of activation notifications to users who didn't interact with the product in last 30 days and stop sending when they become active again. List entry/exit events in trigger ### Fixes and Improvements: * \[SDK] We have exposed [object management methods](/docs/objects-node-sdk) in Node SDK ## Inbox 2.0 - better authentication, In-App feed component and seen interaction Happy to announce a major update in our Inbox SDK. Now, you can directly export and embed In-App feed component and seamlessly create Full screen or Side sheet Inbox experience. ### What’s New? ✅ **Enhanced Security**: We've replaced HMAC authentication with stateless JWT authentication for better security. ✅ **Drop-in components**: You can now quickly build an inbox, including full screen and side sheet feeds, by directly importing UI inbox components that are available in our SDK. ✅ **Bring your own toast**: If you plan to use toast notifications, you have full flexibility to choose any toast library you prefer, allowing you to fully customize the notification experience. These updates offer greater flexibility, security, and customization—giving you full control over your in-app notification experience. If you are on the older SDK version, we recommend you to move on the new version as all future developments will be done on the new SDK. ## Interaction Observer: Seen Tracking in Inbox We’re excited to introduce Interaction Observer support in the Inbox, enabling smarter tracking of notification seen state. Now, notifications will be automatically marked as "seen" when they come in user's scroll view. ## Enhanced Broadcast Observability We’ve done a major revamp to our [Broadcast logging](https://app.suprsend.com/en/production/logs/broadcast) and monitoring, designed to give you greater control and transparency over your broadcast executions. Enhanced Broadcast Observability Here’s what’s new: * **Real-time Execution Tracking**: Monitor broadcast operations as they happen, ensuring you stay informed every step of the way. * **Step-by-Step Debugging**: View detailed execution logs for each step of your broadcast, helping you pinpoint errors and resolve issues faster. * **Advanced Filters**: Quickly locate specific broadcasts with filters for tenant, list ID, broadcast slug, idempotency-key, and status. Easily identify and analyze failure logs. * **Detailed Broadcast Summaries**: Access a comprehensive summary of each broadcast run directly from the listing page, similar to workflow execution logs. ## Athena database connector We’ve added Athena to our list of database connectors, enabling you to sync and create dynamic user lists directly from your S3 database. Since Athena can be set up on top of S3, it’s an excellent way to consolidate data from multiple sources and run queries on the unified dataset without the need for complex ETL pipelines. Athena Database Connector ## New workflow node: Invoke Workflow With this update, you can [invoke a workflow](/docs/invoke-workflow) from within another workflow. This is useful when the recipient list or data context changes between steps in a workflow. **A common use case is escalation workflows**—e.g., if a team member doesn't take action within a set time frame, the workflow escalates the issue and notifies their manager. Invoke Workflow This simplifies complex workflows and supports smooth transitions between related processes, enabling more efficient automation management. ## New workflow node: Update User Profile You can now update recipient or actor profiles directly within a workflow. This feature simplifies user profile management by enabling real-time updates as part of the workflow process. If your have event-based system, where user profile changes are coming as events from your product or a third-party system, you don't need to convert it into user update APIs in your codebase. Simply send events to SuprSend, and let workflows handle user profile updates seamlessly. Update User Profile ### Key use cases 1. **Event-based user profile updates**: Simply send events to SuprSend when user updates their profile in your product or when you are setting custom profile attributes as a side-effect of related action, e.g., in a job board, change user's application status when employer shortlists the profile. 2. **Update user profile based on a workflow step**: Common use cases include fetching data during the workflow to update the user profile or updating the profile when a user successfully completes a step. For instance, while the onboarding process, update `%completion` in user profile when they complete a step. ## Update Object subscriptions within workflow You can now dynamically update [object subscriptions](https://docs.suprsend.com/docs/subscriptions) directly within a workflow. This enhancement eliminates the need for separate API calls for object update, allowing you to manage everything seamlessly within workflows. If you have event-based systems where all asset updates are coming in form of event from your product or third-party systems, you don't have to consume those events internally and write custom APIs to update individual assets (user, list, object) in SuprSend. Simply send events and let the workflow handle object subscriptions and user profile updates, making SuprSend truly a single API integration. Update Object subscriptions within workflow ### Example use case When someone subscribes to a topic (like a tournament), add them as a subscriber to the corresponding tournament object. Later, just trigger tournament related events to SuprSend and the object will automatically fan out and send notification to all users subscribed to the topic. ## New workflow node: Add / Remove user in list You can now dynamically update list users as part of workflow execution. This is a step toward creating user segments based on events or workflow progression, removing the need to call the List Update API separately. Add / Remove user in list ### Key use cases 1. **Event-based segmentation**: When an event occurs, trigger notification to the user and simultaneously add them to a list for future updates. e.g., when a user registers for an upcoming event or webinar, you can send them confirmation email and add them to a list to later send further updates related to the event. 2. **Workflow Step-based segmentation**: Another use case is dynamically adding or removing a user from the list when they complete a workflow step. e.g., in a knowledge series designed to onboard new users, remove a user from the POC list once they complete onboarding steps. ## Deletion APIs On customer request, added APIs to dynamically delete entities in SuprSend. Following deletion APIs are added: * [Delete user profile](/reference/delete-user) * [Delete list](/reference/delete-list) * [Delete tenant/brand](/reference/delete-tenant) * [Delete Object](/reference/delete-object) and [Remove object subscription](/reference/delete-object-subscription) These actions are also available on the dashboard for manual management. Deletion APIs Delete function just deletes the asset and their related data, including preferences. It doesn't have any effect on the historical workflows or broadcasts already executed. While calling the delete function, ensure no active workflows are running for the asset, else the execution will fail. ## User Merge API: Merge duplicate users into one Happy to announce [user merge API](/reference/merge-users) to merge duplicate user identities into a single `distinct_id`. This is helpful to consolidate user profiles, especially when users interact across different products or transition from anonymous to identified states. ### Key Use Cases * **Cross-Product Identity Consolidation**: When users interact across multiple products (e.g., different apps or services within your platform), they may have different identifiers for each product which needs to be merged later. * **Anonymous to Identified Transition**: Platforms often track user actions anonymously before sign-up or login. During this period, user actions are typically tracked under an anonymous ID. Upon sign-up, merge the anonymous profile into the newly created identifier to preserve historical data and Associate it with the identified user profile. ## User Management APIs Being developer first, we have made significant updates and enhancements to the User APIs for easier user management in SuprSend. Also, subscriber is renamed to users in all APIs to avoid confusion with object subscription. Here's a list of all the changes: * Introduced new APIs to [fetch user profile](/reference/get-user-profile), [list users](/reference/list-users) and [delete user](/reference/delete-user). * User update API endpoint has been changed from `/event` to `/user/{{distinct_id}}`. * There are 2 separate APIs to create(upsert) and edit user profile. Any addition or changes in existing user properties can be done using [user upsert API](/reference/create-update-users). For deletion of property or channel, [user edit API](/reference/edit-user-profile) can be used. This is done to keep user upsert API structure flat and simple, consistent to how you identify user in workflow trigger. * Subscriber is renamed to user in all APIs, including user preference APIs. ## Objects: Design scalable group notifications We’re excited to introduce a powerful new capability in SuprSend: [Objects](https://docs.suprsend.com/docs/objects). Objects allow you to manage complex user relationship and notify user groups without identifying individual recipients in your trigger. Ideal for building scalable pub/sub and subscription alerting without having to maintain event to subscriber mapping in your database. You can directly map [object-user subscription](https://docs.suprsend.com/docs/subscriptions) mapping in SuprSend and SuprSend can efficiently fan-out notifications to thousands of users simultaneously. Scalable Group Notification ### What You Can Do with Objects: * **Send notifications to non-user entities like group emails, Slack channels, or shared inboxes** (e.g. a Notion feed). Ideal for SaaS applications sending account-level alerts (e.g. anomaly notifications) to shared channels. Objects can have it's own channels and preferences to handle this use case. * **Group users by topic or subscription and send them alerts without having to call individual recipients in the trigger**. A good example could be SaaS applications managing notifications for end-users, where recipient relationships are coming from a different system, and notification triggers or notification calls are coming from a different system which doesn't have information of the users subscribed to that trigger. * **Maintain hierarchical user relationship with nested object subscription**. e.g., sending announcements to all the entire team of customer while sending invoice related alerts to finance team. You can handle this by creating object for finance team and then adding it as subscriber to customer object. Objects can be easily tested from platform with all object related actions available on SuprSend console. You can programmatically manage objects from your codebase using [rest API calls](/reference/create-update-objects). Support for SDKs coming soon... If there's any use case in object that you think is missing and needs to be solved, please reach out to our [support](mailto:support@suprsend.com). ## Datetime comparators in workflow conditions You can now compare datetime fields in [workflow conditions](https://docs.suprsend.com/docs/branch#key-value-pair). This lets you compare two timestamps where values can be: * **Variable**: computed from workflow input data * **Static**: a fixed timestamp (e.g. `2024-01-01T00:00:00Z`) * **Relative to current timestamp**: e.g. "`now`" or "`now+30d`" (current timestamp +/- interval). Current timestamp is calculated at node runtime and is timezone aware. Datetime comparators in workflow conditions ## Send node execution log - UI revamp The UI for multi-channel and smart routing nodes has been revamped to clearly display how the final list of channels is determined. Now, you get clear visibility into how requested channels in the trigger, override channels, and user and tenant preferences are factored together to compute the final channel list. Send node execution log - UI revamp ## Audit Logs To enhance security and transparency, we’ve introduced Audit Trail to help you monitor and track actions happening on your SuprSend console. You can use this to keep track of unwanted or malicious actions in your account. This initial release logs critical account actions along with location and actor details (team member performing the action). You can also filter by team member (actor), specific action or timestamp. Audit Logs Audit logs are available for enterprise users and have customizable retention period. You can find it in account settings.[ ](https://docs.suprsend.com/changelog?page=1) ## Support for customizing header component in Inbox Added support for customizing the header component in inbox SDKs. * **@suprsend/react-inbox** You can now add a custom component to the right side of the header in the inbox popup. This replaces the "Mark all as read" text with any JSX you provide. You can even include custom icons, such as settings or preferences, in your JSX and use them to navigate users to specific pages. For an example, refer [here](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/customization.md#customizing-header). * **@suprsend/web-inbox** In `web-inbox`, you can add an extra icon beside the "Mark all as read" button at the top of the inbox popup using `headerIconUrl`. You can also execute custom logic when this icon is clicked using `headerIconClickHandler`. This feature is useful for cases like displaying settings or preferences icons, which, when clicked, take users to the respective settings or preferences pages. For more information, [refer to the documentation.](/docs/web-components-customisations) ## Sample Workflow Library With the growing number of workflow nodes, we understand that designing the optimal workflow logic can be tricky. That’s why we’ve built out a library of the most-requested, complex workflow samples to make things easier. Now, when you create a new workflow, you can pick from these pre-built samples right within the platform. We’ll continue adding more samples over time—if you have specific use cases, feel free to share them with us at [product@suprsend.com](mailto:product@suprsend.com), and we’ll add them in the library! Sample Workflow Library ## Deprecated Legacy androidpush methods As part of our ongoing efforts to maintain a robust and up-to-date platform, we've made the following deprecations: ### 1. Legacy FCM API Support Due to Google's shutdown of the legacy Firebase Cloud Messaging (FCM) API, we have removed support for this feature. We strongly recommend migrating to the V1 version of the API that we currently support. For more information, please refer to: [Firebase Cloud Messaging Migration Guide](https://firebase.google.com/docs/cloud-messaging/migrate-v1) ### 2. Xiaomi Push Service Following Xiaomi's discontinuation of their push service outside mainland China, we have removed support for this feature. For more information, please visit: [Xiaomi Developer Documentation](https://dev.mi.com/distribute/doc/details?pId=1777) We appreciate your understanding and cooperation as we continue to improve our services. If you have any questions or concerns about these changes, please don't hesitate to contact our support team. ## Subscriber Page Revamp We have revamped subscriber listing page to include relevant information upfront and also, added advanced filtering options on email, phone, active channels, channel count for an entity, and more. All filters are powered by auto-complete search and selectable options, providing you easy access to available filtering options. Subscriber Page Revamp ## Typeahead autocomplete suggestions for subscribers We’re excited to announce a major update to the platform experience with autocomplete in all subscriber search fields. Whether you’re in logs, on the subscriber page, or within testing flows, you can now receive suggestions for existing users without needing to type the full keyword. Autocomplete suggestions are available for `distinct_id`, `email`, and `phone `fields in subscriber profiles. ## Inbox - React SDK v3.4.0 This update introduces improvements to action button functionality, enhancing the flexibility and customization options for developers. ### New Features: * Custom Click Handlers: Action buttons now support custom click handlers, allowing developers to execute custom logic when a button is clicked. This update significantly expands the capabilities of action buttons in the Inbox React SDK, providing developers with more tools to create rich, interactive inbox experiences. ## Slack Text editor We are happy to announce the support of text editor in slack. So, now you won't have to write complicated JSONNET template for simple text messages. The text editor supports emoji and use [handlebars](/docs/handlebars-helpers) as the templating language. ## Web SDK v2.0 We are excited to announce a major update to our `@suprsend/web-sdk`. This new version brings significant improvements in security, performance, and developer experience. ## Major Changes * Enhanced Authentication System * Replaced workspace key-secret method with public API Key and Signed User JWT token * Improved security and access control * Synchronous Method Calls * All methods now return API call status synchronously * Enables better error handling and flow control in applications * Improved Code Consistency and Developer Experience * Renamed library methods and parameters from snake\_case to camelCase * Added proper IDE suggestions and method descriptions for easier development ## Breaking Changes Due to the significant improvements, this version introduces breaking changes. Users upgrading from `v1.x` should review the migration guide carefully. ## Documentation For a comprehensive list of changes and migration instructions, please refer to our [detailed migration guide](/docs/migration-guide-from-v1) For users who need to reference the previous version, v1 documentation is still accessible [here](https://docs.suprsend.com/v1.2.1/docs/integrate-javascript-sdk) ## Feedback We value your feedback and encourage you to try out the new version. If you encounter any issues or have suggestions for improvement, please don't hesitate to reach out to our support team. Thank you for your continued support and trust in SuprSend! ## View and fetch list users We've added a List Users tab to the lists page, giving you direct access to view all users in a list. Being API first, the same functionality is also exposed to API. Refer this [GET list users API](/reference/get-list-users), or checkout: [postman collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521). Fetch List Users **API Details:** The API returns 20 users per response. You can retrieve additional users by using cursor-based pagination (before and after cursors). ## Better delivery tracking in iOS We are excited to announce significant improvements in our latest update, focusing on enhancing delivery tracking for iOS Push notifications. Regardless of the application's state, you will now experience more reliable and precise delivery tracking. We have rolled out updates for all our major SDKs. To take full advantage of these improvements, please ensure that you update your dependencies promptly. * [iOS SDK](https://github.com/suprsend/SuprSend-iOS-SDK/releases/tag/1.0.3) - v1.0.3 * [React Native SDK](https://github.com/suprsend/suprsend-rn-sdk/releases/tag/v2.4.0) - v2.4.0 * [Flutter SDK](https://github.com/suprsend/suprsend-flutter-sdk/releases/tag/v2.2.0) - v2.2.0 ## Web SDK v1.5.1 We have resolved an issue where the SDK would unexpectedly generate an error message whenever the event payload contained specific emojis. This fix ensures that event processing is now stable and reliable, even when such emojis are present. [More details here](https://github.com/suprsend/suprsend-browser-sdk/releases/tag/v1.5.1) ## Improvement in Workflow Listing page * Developer testing workflows are now excluded from the Workflow List Page and search results, ensuring a cleaner and more organized workflow listing. These workflows will still be accessible through logs. * Enhanced observability of Tenant APIs by displaying request logs on the logs page. This improvement provides better visibility and monitoring of API interactions. ## Wait Until - Add Condition on Event Property We’re excited to announce a powerful update to our Wait Until feature! You can now add multiple events and apply conditions on event properties within the Wait Until branch, allowing for more precise event filtering and targeting of the exact event required in your workflow. This is especially useful for scenarios where the same event triggers multiple workflows, and you want to exit or cancel a notification based on user actions. For instance, in a booking reminder workflow, if a user has multiple bookings, you can now match the booking ID of a cancellation event with the original event to ensure correct reminder gets canceled. Wait Until - Add Condition on Event Property ### Key Changes: * Add conditions on event properties using a simple key-operator-value expression (e.g. `booking_id = 123`). Add condition on multiple event properties using `AND`,`OR`. * Apply conditions across multiple events (e.g. avoid sending a notification if a user completes an action or achieves a specific milestone). [Refer documentation](/docs/wait-until#fixed-delay) for details on how to implement wait until node in your workflow. ## Enhanced branching capabilities We are excited to announce significant improvements to our [branching capabilities](/docs/branch). With the addition of more data types, you can now set precise conditions on various inputs within your branches, such as actor, recipient, and tenant properties. This enhancement allows you to tailor your workflows more effectively, ensuring that each journey is as personalized and efficient as possible. If you haven't yet explored our branching feature, now is a great time to do so. It offers a robust way to construct multi-step journeys within a single workflow. Here are some example use cases where you could use branch: * A/B test notification content by splitting cohorts based on user properties like region. * Customize digest schedules (immediate, daily, weekly) using key in your trigger data or recipient’s preference. * For support ticket requests, adjust who gets alerts, when to send them (immediately or batched), and which channels to use based on the issue’s priority. * Define different next steps in an onboarding checklist depending on a user’s completion percentage. Here, you can also [fetch](/docs/fetch) completion% just before sending the next reminder. ## New SMS Integration: Pinnacle On customer demand, we are live with latest vendor Integration with Pinnacle for SMS. [Check out vendor integration documentation](/docs/pinnacle) for setup details. ## List Details Page List Details Page ### Key Improvements: * New List Details Page: Access all essential information (logs, broadcast runs, list users) and actions for a list (run broadcast, update user) in a single view, making list management much simpler. * "Sync Now" button on query page: This will enable you to manually sync list users when required. ### Coming Soon: * List Users Tab and API: We’ll soon be adding a tab to see all list users. The same functionality will also be exposed to hub APIs to fetch list users. ## Revamped workflow list page Revamped Workflow List Page We are excited to announce our latest release, designed to enhance your platform navigation experience. In this update, we have overhauled the [workflow list view](https://app.suprsend.com/en/staging/workflows) to present critical information prominently and introduced robust filtering and sorting capabilities. Here's what's new: * Effortlessly search workflows by , , or  for quick access. * Utilise advanced filters to refine workflows by trigger events, category, template, and incorporated nodes. * Sort workflow lists based on the most recent trigger or modification date. These enhancements will help you search and manage workflows more effectively. ## Bulk Preference APIs We’ve introduced new APIs designed to simplify the migration and management of user preferences within SuprSend. * [Get User Full Preference](/reference/get-user-full-preference): Fetch complete user preferences across all categories and channels in a single API call. * [Bulk Update User Preference](/reference/bulk-update-user-preference): Update preferences for multiple users across all categories and channels in one go. This API is ideal for batch processing and bulk updates, making large-scale migrations easier. * [Reset User Preference](/reference/reset-user-preferences): If you have updated a user's preference by mistake, this API allows you to quickly revert a user's preferences to the default tenant settings. Along with these changes, we have also introduced a flag in GET category preference APIs `show_opt_out_channels`. Set this to `true` to see channel list in opt-out preference categories. ## New Email Integration: Mailjet On customer request, we've added **Mailjet** in our supported email vendor list. To send out emails through Mailjet, all you have to do is add your vendor credentials on SuprSend dashboard and you are good to go. Check out [vendor integration doc](/docs/mailjet) for setup details. ## Introducing Digest Node in Workflow You can now effortlessly batch your notifications into a single, streamlined digest sent at a recurring schedule. Whether, it’s sending a summary of pending activities in a user’s account at the end of the day or personalized recommendations by fetching data from an endpoint, you can design any complicated digest use case with ease Digest Node in Workflow Create a personalized digest experience for your users, * By picking [digest frequency from recipient’s preferences](/docs/digest#dynamic-schedule-send-digest-based-on-user-preference) * Making it timezone-aware and sending digest in recipient’s timezone irrespective of where they are located across the globe. Need help designing your digest use case? [Write back to us](mailto:support@suprsend.com) and our team of experts will be happy to help.
# ACL Sinch Source: https://docs.suprsend.com/docs/acl-sinch Guide to connect your ACL Sinch account with SuprSend to send SMS notifications. This section is a step by step guide to select Sinch (ACL) as your SMS service provider. You can use your existing ACL account to integrate, or connect with their sales team for account setup from [here](https://www.aclmobile.com/request-a-demo/) ## ACL(Sinch) integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click SMS -> Sinch (ACL) from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Account Type | Sinch creates 2 separate accounts, one for OTP messages and one for transactional notification. Add OTP account in "system" notification category | | Enterprise ID / App ID | Unique identifier for your application (App ID) | | Sub Enterprise ID / Sub App ID*(Optional)* | Will be same as your App ID if you don't have any sub accounts attached to your app. Leave it blank if you don't have a separate sub app id | | User ID | User id for ACL account login. SuprSend uses this info to send SMS on your behalf via your registered ACL account. | | Password | Password for ACL account login. SuprSend uses this info to send SMS on your behalf via your registered ACL account. | | URL shortener | Enable it to enable URL shortening in your messages. Not supported in OTP message | | Price per notification | This is the amount you pay per SMS notification to ACL. It helps us to calculate, estimate and optimise your cost spent on notifications. | | DLT Integration -> 'Telecom Operator' | Telecom Operator of your business SMS account | | DLT Integration -> 'Headers' | 6 digit/character sender id registered for your entity ( *You can get the header details from your DLT portal*) *e.g. SPRSND* Also, you can add multiple headers in the list by just typing the header name and clicking on enter | | DLT Integration -> 'User Name' | User Name of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Password' | Password of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Entity ID' | Entity Registration ID linked to your DLT account. You can get the Registration Id from your DLT account homepage. SuprSend uses this info to send messages on your behalf through your registered DLT platform. | ### How to get App ID, Sub App ID for your ACL(Sinch) account As soon as your account is created, you'll receive a mail from sinch team sharing the account credentials with you. You'll get all this information in the mail itself ## Setting Callback URL in ACL(Sinch) account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Send a mail to ACL Sinch team to enable below webhook URL to your account, webhook for OTP and transactional account will be different > **For OTP account** > URL: [`https://hub.suprsend.com/webhook/acl/sms/otp`](https://hub.suprsend.com/webhook/acl/sms/otp) > Request method- POST > **For Transactional account** > URL: [`https://hub.suprsend.com/webhook/acl/sms/`](https://hub.suprsend.com/webhook/acl/sms/) > Request method- POST ## How to register headers through Airtel DLT platform To register header on Airtel DLT platform, you can refer the section: [Sender ID/Mask/Header Registration- DLT Platform](https://enterprise.smsgupshup.com/DLT/senderidRegistration) *** # Add User to list Source: https://docs.suprsend.com/docs/add-user-to-list Dynamically add users to list within a workflow. You can use this node to dynamically add recipient or actor in the list. This is one of the ways to create user segment based on an event or action. For example, when someone registers for an event, you can send them a confirmation email and at the same time, add them to a list to send them reminder messages or announcements related to the event. ### Creating list dynamically within workflow You can either add users to an existing list or create the list on the fly using workflow input data. Dynamic list are defined in handlebars format as `{{...}}`. List will only be created if the `Create list if it doesn't exist` setting is ON. One common use case of creating list dynamically is when you need to create different lists based on user topic subscription. e.g., there are multiple events happening and you want to create a separate list for each event. List ID in such case can be `{{event_id}}_subscribers` and List name `{{event_name}} - subscribers`. Please note that list ID only supports following characters- \`a-z,0-9,-,\_\`. Ensure that list\_id variable resolves to a valid format; otherwise, list creation will fail. 🚧 ### Lists vs. Objects for topic subscriptions * [Lists](/docs/lists) are ideal for one-time broadcasts to large user groups with high throughput (up to 1000 notifications/second). Example: Sending announcements or updates to all users subscribed to a particular event. * [Objects](/docs/objects) are better for reusable topic subscriptions when additional workflows need to be triggered for the same subscribers. Example: Notifying team members or running nested workflows for hierarchical subscribers. *** # Amazon SES Source: https://docs.suprsend.com/docs/amazon-ses Guide to connect your AWS SES account with SuprSend to send email notifications. ## Pre-Requisites Before you begin, ensure you have: * An AWS account with administrative access * Necessary permissions to create IAM roles/users and SES resources * Access to AWS Console If you don't have an AWS account, you can [create one here](https://aws.amazon.com/resources/create-account/). ## Authentication options SuprSend supports two authentication methods for Amazon SES integration: ### IAM Role (Recommended) IAM Role-based authentication is the recommended approach as it: * Provides temporary, scoped access * Eliminates the need for long-term credentials * Follows security best practices If you're using IAM-role authentication at multiple places inside SuprSend, you can consolidate all permission statements within a single policy. * Navigate to IAM console. * Select `Policies` tab. * Click `Create Policy`. * Either use a 'Visual-editor' or a 'JSON editor' to allow actions `["ses:SendEmail", "ses:SendRawEmail"]`. For simplicity, choose "JSON editor" and replace the existing content with below JSON. ```json IAM Role Policy theme={"system"} { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowSesSendActions", "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendBulkEmail", "ses:SendRawEmail" ], "Resource": "*" } ] } ``` * Name your policy (e.g. `suprsend_trust_role_policy`). * Add an optional description. * Click `Create Policy`. This will create a policy with the above permissions. * Go to IAM console → `Roles` tab * Click `Create Role` * Select `Another AWS Account` as trusted entity * Enter SuprSend's AWS account ID: `924219879248` * Select the policy created in step 1 * Name your role (e.g. `suprsend_trust_role`) * Review and create the role * Navigate to your created role * Check Trust Relationships tab * Verify the trust policy matches: ```json theme={"system"} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::924219879248:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "" } } } ] } ``` Important: * Principal must be SuprSend's AWS account ID: "924219879248" * ExternalId should match the unique ID entered during role creation * Save the following information for SuprSend setup: - Role ARN - External ID - Maximum session duration Follow these steps to create an IAM user with programmatic access: 1. Create an [IAM user with Programmatic access](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) 2. Attach `AmazonSESFullAccess` policy 3. Save the `Access-key-ID` and `Secret-Access-Key` securely Make note of your AWS region (e.g. `ap-south-1`) as it will be needed for configuration. ## Integration steps to connect AWS SES with SuprSend * Open AWS SES console * Navigate to `Verified Identities` tab * Add and verify either: * An email address * A domain/subdomain * Follow [AWS documentation](https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html) for detailed steps Configuration-set is a rule set applied to send-email. In configuration-set you can specify to track events (e.g. send, delivery, open, click, bounce, complaint etc.) and send these to an event destination. Refer to the documentation on how to [create configuration sets](https://docs.aws.amazon.com/ses/latest/dg/creating-configuration-sets.html). * Go to SES console → `Configuration sets` * Click `Create set` * Configure: * Name: `ses_suprsend_configset` * IP Pool: Select your pool or use `default` * Click `Create Set` Event destinations are used to send email delivery events to SuprSend for tracking notification status (send, delivery, open, click, bounce, complaint etc.) for logs and analytics. To setup event destination, * Select your configuration set * Go to `Event Destinations` tab * Click `Add destination` * Configure: * Event Types: Select all * Destination Type: Amazon SNS * Enter a destination Name. e.g.: `ses_suprsend_configset_destination` * Make sure`Event publishing`is marked`Enabled`. * Select an SNS topic from the dropdown (or create a new topic) for events destination. * Review and add destination After setting up the configuration set and SNS topic for email events, you'll need to grant SuprSend permission to subscribe to your SNS topic. This is done through AWS's cross-account SNS subscription feature, which allows external AWS accounts (like SuprSend's) to receive notifications from your SNS topics. For detailed information about cross-account SNS subscriptions, see the [AWS documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-send-message-to-sqs-cross-account.html). * Go to SNS Console → Topics * Select the topic created in previous step * Edit Access Policy * On the edit page, expand the`Access Policy`section. The policy looks something like below. You'll notice that the current policy gives`sns:Publish`permission to your SES account. ```json Amazon SNS Console theme={"system"} { "Version": "2008-10-17", "Statement": [ { "Sid": "stmt1651045546197", "Effect": "Allow", "Principal": { "Service": "ses.amazonaws.com" }, "Action": "sns:Publish", "Resource": "arn:aws:sns:topic_region:111122223333:topic_name", "Condition": { "StringEquals": { "AWS:SourceAccount": "111122223333" }, "StringLike": { "AWS:SourceArn": "arn:aws:ses:*" } } } ] } ``` Next you need to add a policy to give`sns:Subscribe`permission to SuprSend (Account: 924219879248). Add below json inside the`Statement`list: (replace the "`Resource`" value with your "Resource" value) ```json Amazon SNS Console theme={"system"} { "Effect":"Allow", "Principal":{ "AWS":"924219879248" }, "Action":"sns:Subscribe", "Resource":"arn:aws:sns:topic_region:111122223333:topic_name" } ``` So in effect, the whole policy would look like this. Verify the policy and click on `Save`. ```json Amazon SNS Console theme={"system"} { "Version": "2008-10-17", "Statement": [ { "Sid": "stmt1651045546197", "Effect": "Allow", "Principal": { "Service": "ses.amazonaws.com" }, "Action": "sns:Publish", "Resource": "arn:aws:sns:topic_region:111122223333:topic_name", "Condition": { "StringEquals": { "AWS:SourceAccount": "111122223333" }, "StringLike": { "AWS:SourceArn": "arn:aws:ses:*" } } }, { "Effect":"Allow", "Principal":{ "AWS":"924219879248" }, "Action":"sns:Subscribe", "Resource":"arn:aws:sns:topic_region:111122223333:topic_name" } ] } ``` On the SuprSend dashboard, go to vendor page from side panel and click Email -> Amazon SES from the list of Vendors. This will open vendor details page as shown below: Here's a description on what each of these form fields describe: | Form Field | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily. E.g. - *AWS SES \[Production]* | | From Email | Default 'From Email ID' that email will go from. You can override this in the individual template.\*e.g. [support@suprsend.com](mailto:support@suprsend.com) | | From Name | Default 'From Name' that email will go from. You can override this in the individual template.*e.g. SuprSend* | | Reply Address | Default 'Reply To Email id' on which replies are received. You can override this in the individual template.\*e.g. [support@suprsend.com](mailto:support@suprsend.com) | | AWS region | aws-region you are going to use for sending emails. | | Access Key ID | Access key ID of the IAM user with full access. [Refer step](/docs/amazon-ses#step-1-create-aws-iam-user) to create a new IAM user and generate access key. | | Secret Access Key | `Secret-Access-Key` of the IAM user with full access. [Refer step](/docs/amazon-ses#step-1-create-aws-iam-user) to create a new IAM user and generate access key. | | Configuration Set | Configuration-set is used to track email events (e.g. send, delivery, open, click, bounce, complaint etc.). [Refer Step](/docs/amazon-ses#step-3-create-configuration-set) to define configuration set. | | SNS Topic ARN | This is the destination where the tracked events will be sent. Configuration set defines what events to be tracked and setting the topic allows SuprSend to receive these events. [Follow step 4 & 5](/docs/amazon-ses#step-4-manange-event-destinations) to setup SNS topic and give SuprSend permission to subscribe to it. | | Price per notification | This is the amount you pay per email notification to AWS. It helps us to calculate, estimate and optimise your cost spent on notifications. | ## Setting Amazon SES event tracking One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. For enabling event tracking at SuprSend (such as delivery, opened, blocked, spam etc.), you'll have to `sns:Subscribe`action to SuprSend. Once you’ve filled in the required fields and saved your changes on the vendor configuration page in the SuprSend platform, you’ll need to manually click the `Subscribe` button to initiate the subscription to your specified SNS Topic ARN. This step is necessary to enable Email Tracking and ensure that notification delivery and failure logs are properly tracked. If any changes are made to the SNS configuration after a successful subscription, you can click the `Resubscribe` button to re-establish the subscription using the latest configuration. This ensures your email tracking setup stays consistent and up to date. Successfully Subscription as: ## How to add Unsubscription link in email It's recommended to allow recipients to unsubscribe from emails. You can use SuprSend’s [hosted preference page](/docs/user-preferences#hosted-preference-page) for giving granular control to your users, allowing them to manage preferences per category while also reducing unnecessary vendor API calls for opt-outs. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don’t give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don’t have them. # Amazon S3 Source: https://docs.suprsend.com/docs/amazon_s3 Guide to export notification data & templates from SuprSend to Amazon S3 bucket. ## Overview SuprSend offers two types of connectors: * **Source**: Connects event and user data from third-party platforms into SuprSend * **Destination**: Syncs data (message templates, notification metrics) from SuprSend to your data warehouse Amazon S3 is a destination used to ingest data from SuprSend into your S3 bucket. You can directly query on this data using Athena or import it to data warehouses like Redshift, Snowflake, ClickHouse etc. for analysis. ## How it works? This integration exports individual [parquet](https://parquet.apache.org/) files for **notification data**, and JSON files for **message templates** to your S3 bucket at a regular interval. You can select what all `data points` you want to sync to your S3 bucket. Data Points Parameter on SuprSend Platform The sync happens every 3-5 minutes, ensuring that you always have the latest data in your S3 bucket. For notifications, there will be a separate parquet file for each day. This integration only publishes parquet files for notifications to your storage bucket. You can either use Athena to query this data or ingest this data into a data warehouse for analysis. ## Pre-Requisites Before you begin, ensure you have: * An AWS account with administrative access * Necessary permissions to create S3 buckets and IAM roles/users * Access to AWS Console ## Set up steps Skip this step if you want to use an existing bucket. Otherwise, follow these steps: 1. Sign in to [AWS S3 Console](https://s3.console.aws.amazon.com/) 2. Click "Create bucket" 3. Configure bucket settings: * Bucket name: e.g. `suprsend-notification` * Region: Choose your preferred region * Object Ownership: **ACLs disabled** (recommended) * Block all public access: **Enabled** * Bucket versioning: **Disabled** * Default encryption: Server-side encryption with Amazon S3 managed keys (SSE-S3) * Bucket Key: **Enabled** 4. Click "Create bucket" We'll need permission to `PutObject` for data ingestion in your bucket. To set permission policy, 1. Go to [IAM Console](https://console.aws.amazon.com/iam/) 2. Select Policies → Create Policy 3. Use JSON editor and paste this policy (replace `suprsend-notification` with your bucket name). Set a relevant name to the policy, something like- **`SingleBucketWriteAccess`**. ```json Amazon AWS IAM Management Console theme={"system"} { "Version":"2012-10-17", "Statement":[ { "Sid":"AllObjectActions", "Effect":"Allow", "Action":[ "s3:PutObject" ], "Resource":[ "arn:aws:s3:::suprsend-notification/*" ] } ] } ``` Choose between IAM Role (recommended) or IAM User authentication. IAM Role-based authentication is the recommended approach as it: * Provides temporary, scoped access * Eliminates the need for long-term credentials * Follows security best practices If you're using IAM-role authentication at multiple places inside SuprSend, you can consolidate all permission statements within a single policy. * Go to IAM console → Policies * Click "Create Policy" * Either use a 'Visual-editor' or a 'JSON editor' to allow actions `["s3:PutObject"]`. For simplity, choose "JSON editor" and replace the existing content with below JSON. Replace the s3-bucket in `Resource` with your own bucket ```json IAM Role Policy theme={"system"} { "Version":"2012-10-17", "Statement":[ { "Sid":"S3ObjectActions", "Effect":"Allow", "Action":[ "s3:PutObject" ], "Resource":[ "arn:aws:s3:::suprsend-notification/*" ] } ] } ``` * Click on `Next`, and enter a Policy Name (e.g. suprsend\_trust\_role\_policy), & other optional Description. * Finally, clicking on the `Create Policy` button, it will create a new policy with the above permissions. * Go to IAM console → Roles. To create an IAM Role, refer to this [documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html). * Click on `Create Role` button, and select `Another AWS Account` as the trusted entity. * Enter SuprSend's AWS account ID: `924219879248` * Next, Select the policy created above * Enter role name (e.g. `suprsend_trust_role`) and create the role. * Select the role created above from IAM console → Roles tab * In Trust Relationships tab, verify this trust policy: ```json IAM Role Policy theme={"system"} { "Version":"2012-10-17", "Statement":[ { "Effect":"Allow", "Principal": { "AWS":"arn:aws:iam::924219879248:root" }, "Action":"sts:AssumeRole", "Condition":{ "StringEquals":{ "sts:ExternalId":"" } } } ] } ``` Important: * Principal must be SuprSend's AWS account ID: "924219879248" * ExternalId should match the unique ID entered during role creation * Save these details for SuprSend setup: * Role ARN * External ID * Maximum session duration Follow these steps to create an IAM user: 1. Create an IAM user with Programmatic access * Add relevant user Name: e.g. `s3-bucket-suprsend` * Attach the policy created in step 2 2. Complete the user creation process 3. Continue to step 4 before closing the browser. 4. Save the access credentials securely This is required to be added in the connector integration form on SuprSend. For IAM User authentication: 1. Go to IAM Users → Select your user 2. Security credentials tab → Create access key 3. Choose "Third party service" 4. Add optional description 5. **IMPORTANT**: Copy and save both access key and secret 6. Click "Done" ## Configure S3 Connector in SuprSend Go to **Settings → Connectors → Amazon S3** and fill in required information. * **In case of IAM Role:** * **In case of IAM User:** | Form Field | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Connector name**\* | This name is identify the connector and is for your reference | | **Authentication Scheme**\* | Select whether to use IAM Role or IAM User for authentication. | | **AWS Region**\* | Choose the AWS Region where your S3 bucket is hosted (for e.g.: `us-east-1`, `ap-south-1`, or `eu-west-2`) | | **Role ARN**\* | The Amazon Resource Name (ARN) of the IAM Role that grants access to the S3 bucket. | | **External ID**\* | The unique External ID you configured in the IAM Role’s trust policy for SuprSend. This provides an extra layer of security when SuprSend assumes the role. | | **Duration Seconds** | The amount of time (in seconds) SuprSend can assume the IAM Role for each session (e.g. 3600 = one hour). Leave it at the default unless you have specific session duration requirements. | | **Access Key ID**\* | This is the access key ID linked to the IAM user. Refer step 4 for steps to create access key | | **Secret Access Key**\* | This is the secret access linked to the IAM user. Refer step 4 for steps to get secret access | | **Export Bucket**\* | Name of the S3 bucket where the parquet files should be exported. Refer step 1 to create an export bucket | | **Data Points to export**\* | Here you can choose what all information should be exported to your S3 bucket: **Notifications Status**- To sync details to the each notification- users, tenants, vendor, channel, DLR status of the notification (delivery, seen, click etc.), and failure reasons for failed notifications **Template**- To sync all templates created in SuprSend in your S3 bucket. Template sync will happen every time you are making change in the template | Your S3 setup is now complete. Click on **`Enable sync`** to start data export. You can pause and resume your sync anytime you want. To Pause sync for certain data points, deselect the ones not needed from "Data Points to export" and save the changes. You can also disable your entire sync by disabling the `Enable sync` button, in which case we’ll stop the export. When you enable your sync again, we send all of your historical data as if you’re starting a new integration. *** # Manage Users Source: https://docs.suprsend.com/docs/android-create-user Android SDK Methods to create user and set their android push token and other communication channels for sending notifications. ## How Suprsend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Identify user and Set Push token You can identify a user using `ssApi.identify()` method. Androidpush token is automatically set in user's profile when this method is called. Call this method as soon as you know the identity of user, that is after login authentication. If you don't call this method, user will be identified using distinct\_id (uuid) that SDK generates internally. We internally create an event called **`$user_login`**. You can see this event on SuprSend workflows event list and you can configure a workflow on it. ```kotlin kotlin theme={"system"} ssApi.identify(uniqueId) //Sample Values ssApi.identify("291XXXXX-62XX-4dXX-b2XX"); ssApi.identify("[support@suprsend.com](mailto:support@suprsend.com)"); ``` | Parameters | Type | Description | | ---------------- | ------------------------- | ----------------------------------------------------------------------------------- | | **distinct\_id** | int, bigint, string, UUID | *mandatory* Unique identifier for a user across devices or between multiple logins. | As soon as the user logs out, call `ssApi.reset()` method to clear data attributed to a user. This will generate a new random `distinct_id` and clear all super properties. This allows you to handle multiple users on a single device. When you call this method, we internally create an event called **\$user\_logout**. You can see this event on SuprSend workflows event list and you can configure a workflow on it. ```kotlin kotlin theme={"system"} ssApi.reset() ``` Don't forget to call reset on user logout. If not called, user id will not reset and multiple tokens and channels will get added to the user\_id who logged in first on the device. ## Set Communication Channels You can send communication channel details of a user to the SuprSend SDK. We will store the channel details in the user profile. This will allow us to send communications to a user on the channels available for that user whenever there is any communication trigger. You can add SMS, Email and Whatsapp channel information by using below methods. You can call this on signup, or whenever a user provides the above channel information. ```kotlin kotlin theme={"system"} ssApi.getUser().setEmail("[support@suprsend.com](mailto:support@suprsend.com)") // To add Email ssApi.getUser().setSms("91XXXXXXXXXX") // To add SMS suprsend.user.setWhatsApp("+91XXXXXXXXXX"); // To add Whatsapp ``` Android Push will automatically be set at the time of user login. All you have to do is to integrate the push notification service in your application [Android Push Integration guide](/docs/firebase-fcm-androidpush) Make sure you are sending the country code when you are calling communication methods for SMS and Whatsapp. You can remove SMS, Email and Whatsapp channel information by using below methods. You can call this when a user updates his channel information. You need not call this when a user unsubscribes from a particular channel notification, as that will be handled in user preferences. ```kotlin kotlin theme={"system"} ssApi.getUser().unSetEmail("[support@suprsend.com](mailto:support@suprsend.com)"); // To remove Email ssApi.getUser().unSetSms("91XXXXXXXXXX") // To remove SMS ssApi.getUser().unSetWhatsApp("91XXXXXXXXXX") // To remove Whatsapp ``` ## Set User Properties You can use SuprSend SDK to set advanced user properties, which will help in creating a user profile. You can use these properties to create user cohorts on SuprSend's platform with future releases. Set is used to set the custom user property or properties. The given name and value will be assigned to the user, overwriting an existing property with the same name if present. It can take key as first param, value as second param for setting single user property or object for setting multiple user properties. ```kotlin kotlin theme={"system"} ssApi.getUser().set(key: String, value: Any) // for single property ssApi.getUser().set(properties: JSONObject) // for multiple properties //Example for setting single property ssApi.getUser().set("prime_member_group","super") ssApi.getUser().set("purchased_product",true) ssApi.getUser().set("purchased_value", 2599.50) //Example for setting multiple properties ssApi.getUser().set(JSONObject().apply { put("prime_member_group", "super") put("purchased_product", true") put("purchased_value", 2599.50") }) ``` | Parameters | Type | Description | | -------------- | ------ | -------------------------------------------------------------------------------------------------- | | **key** | string | *Mandatory* This is property key that will be attached to user. Should not start with `$` or `ss_` | | **value** | any | *Optional* This will be value that will be attached to key property. | | **JSONObject** | object | *Optional* This is used in case of setting multiple user properties. | When you create a key, please ensure that the Key Name does not start with **`$`** or **`ss_`**, as we have reserved these symbols for our internal events and property names. Works just like `ssApi.getUser().set`, except it will not overwrite existing property values. This is useful for properties like *First login date.* ```kotlin kotlin theme={"system"} ssApi.getUser().setOnce(key: String, value: Any) // for single property ssApi.getUser().setOnce(properties: JSONObject) // for multiple properties //Sample for single property ssApi.getUser().setOnce("first_login_at", "01-12-2021") //Sample for multiple properties ssApi.getUser().setOnce(JSONObject().apply { put("first_login_at", "01-12-2021") put("first_ordered_amount", "10000") put("first_ordered_product_name", "Car") }) ``` Add the given amount to an existing property on the user. If the user does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number for the value. ```kotlin kotlin theme={"system"} ssApi.getUser().increment(key: String, value: Number) // for single property ssApi.getUser().increment(properties: JSONObject) // for multiple properties //Sample for single property ssApi.getUser().increment("login_count", 1) ssApi.getUser().increment("amount", 45) ssApi.getUser().increment("total", 1.5) //Sample for multiple properties ssApi.getUser().increment( mapOf( "login_count" to 1, "amount" to 45, "total" to 1.5 ) ) ``` This method will append a value to the list for a given property. ```kotlin kotlin theme={"system"} ssApi.getUser().append(key: String, value: Any) // for single property ssApi.getUser().append(properties: JSONObject) // for multiple properties //Sample for single property ssApi.getUser().append("choices", "ABC") //Sample for multiple properties ssApi.getUser().append(JSONObject().apply { put("choices", "ABC") put("first_ordered_at","01-12-2021") put("first_ordered_amount", 4500.00) }) ``` This method will remove a value from the list for a given property. ```kotlin kotlin theme={"system"} ssApi.getUser().remove(key: String, value: Any) //Sample ssApi.getUser().remove("choices", "ABC") ``` This will remove a property permanently from user properties. ```kotlin kotlin theme={"system"} ssApi.getUser().unSet(key: String) // for single property ssApi.getUser().unSet(keys: List) // for multiple properties //Sample for single property ssApi.getUser().unset("prime_member_group") //Sample for multiple properties ssApi.getUser().unSet(listOf("prime_member_group","purchased_product","purchased_value")) ``` *** # Mobile Push Setup Source: https://docs.suprsend.com/docs/android-firebase-fcm-push-integration Step-by-step guide to integrate FCM Push notifications into your android app using SuprSend. ## Integration Steps To start sending notifications from FCM, you'll have to first create a firebase project. Create firebase project and application in [firebase console](https://firebase.google.com/) with your applications package name which you can find in *`MainApplication.java`* or *`AndroidManifest.xml`* You can get your Service Account JSON by [following these instructions](https://firebase.google.com/docs/cloud-messaging/auth-server#provide_credentials_manually). Download **google-services.json** and add the file inside your android>app folder. Add below dependency inside projects *build.gradle* inside dependencies ```groovy build.gradle theme={"system"} dependencies { ... classpath 'com.google.gms:google-services:4.3.10' // or latest version } ``` Add below plugin inside apps *build.gradle* ```groovy build.gradle theme={"system"} apply plugin: 'com.google.gms.google-services' ``` Add below dependency inside apps *build.gradle* inside dependencies ```groovy build.gradle theme={"system"} implementation("com.google.firebase:firebase-messaging:22.0.0") // or latest version ``` Push feature can be implemented in two ways: You may use this option if all of your android push notifications are to be handled via SuprSend SDK. We recommend you to use this method as it is just a single step process to just register the service in your application manifest and everything else will be ready. ```xml AndroidManifest.xml theme={"system"} ``` Since your service is registered in the app manifest, to render the push notification directed from SuprSend server you will have to add the below code to your service. ```javascript helloWorld.js theme={"system"} console.log("Hello World"); ``` ```kotlin kotlin theme={"system"} class YourApplication : Application() { override fun onCreate() { FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) { Log.w(TAG, "Fetching FCM registration token failed", task.exception) return@OnCompleteListener } val fcmToken = task.result suprsend.user.setAndroidFcmPush(fcmToken); }) //or if you are on older FCM version val fcmToken = FirebaseInstanceId.getInstance().token instance.getUser().setAndroidFcmPush(fcmToken) } ``` ### Asking for permission -Android 13(API-33) ```kotlin YourActivity.kt theme={"system"} import android.Manifest import android.annotation.SuppressLint import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.util.Log import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.viewpager.widget.ViewPager import com.google.firebase.messaging.FirebaseMessaging import org.json.JSONObject class YourActivity : AppCompatActivity() { private val activityResultLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> if (!isGranted) { // You can show a dialog which explains the intent of this permission request of how it is important for certain features of your app to work AlertDialog.Builder(this) .setView(R.layout.notification_permission_desc) .setTitle(getString(R.string.app_name)) .setPositiveButton("Proceed") { _, _ -> val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val uri: Uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivity(intent) } .setNegativeButton("Deny") { _, _ -> }.show() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { activityResultLauncher.launch( Manifest.permission.POST_NOTIFICATIONS ) } } } ``` If the androidx dependency is not present then you will have to add the below dependency in your app dependencies ```Text app/build.gradle.kts theme={"system"} dependencies { implementation("androidx.appcompat:appcompat:1.3.1") } ``` **How to identify if notification is sent by SuprSend?** If notification payload contains key **supr\_send\_n\_pl** then simply consider this as payload sent from suprsend and pass the payload to suprsend SDK by: if (payload?.data?.supr\_send\_n\_pl) \{ suprsend.showNotification(payload.data.supr\_send\_n\_pl); } *** # Android Push Template Source: https://docs.suprsend.com/docs/android-push-template How to design advanced Android Push template with customisation options to send silent, sticky notifications, and more. ## Design Template You can design template with a simple form editor tool. You can add variables with `Handlebars` language. You can check how the message will look in the preview section on the right side. Once designed, you can save the push notification template by clicking on Save Draft. When you are ready, you can Publish Draft by providing a name to the version. This will become the Live version, and will be used whenever the associated workflow is triggered. ## Android Push notification fields description | Field | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Title | Small message text box. Note that this field will be displayed in single line only, and very long content can get curtailed. Use handlebars to add variables. | | Small Icon | Small icon is displayed on the top status bar as well as the notification itself. Developer needs to set this to your logo in your app. Default shown is bell icon. The color used is your brand color set in 'Organisation Settings' | | Large Icon | The large icon will show up to the left of the notification text on Android 4.0.3 - 6.0 devices, and shows on the right for Android 7.0+ devices. SuprSend puts your organisation logo as default in the large icon, which you can set from 'Organisations Tab'. | | Message | Large message text box. Use handlebars to add variables. | | Subtext | *Optional* Subtext appears on top, next to your brand name. | | Banner Image | *Optional* For dynamic images: Banner filetypes: PNG, JPG, JPEG. Recommended aspect ratio: 2:1 Recommended maximum size: 700 KB For static images (that are uploaded): -SuprSend will auto-scale your image so that it doesn't get cropped -SuprSend will optimise the image size so that it is loaded faster on low internet connections. | | Action URL | Provide a URL where a user will go when he clicks on the push notification. You can give your android deeplink URL as well. | | Action Buttons | *Optional* Enter up to 3 Button names and URL. You can use variable names using handlebars in both action name and URL. You can give your android deeplink URL as well. The action button name color is picked up from your organisation settings. You cannot change button color in a template once it is created. | ## Android Push Advanced Configurations (*Optional*) | Field | Type | Description | | --------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Silent | Boolean | With Silent "ON", users won't see this message on their device. Instead, these notifications trigger background activities within the application using the notification payload. For instance, they could be utilized to send breaking news alerts or notify users that the latest episode of their favorite TV show is available for offline viewing. Silent notifications are particularly useful for delivering sporadic but time-sensitive content, where immediate access is crucial and the delay associated with background fetches may not be tolerable. | | Timeout | Numeric | You can use timeout if you want your notifications to auto-dismiss after a certain time period in case your user has not interacted with that notification. You can add time in seconds in this field. | | Sticky Notifications | Boolean | With Sticky Notifications "ON", the user will not be able to dismiss the notification by left swiping on the notification tray. However, notification will be removed from the tray if the user has clicked on the notification | | Notification group | Single line Text | Add a group name to the notification if you want your notifications to be stacked together in the tray. Notifications belonging to the same group will be stacked together. | | App icon (Small icon) | Single line Text | Small icon name without extension. The small icon is displayed on the top status bar and on the left side of notification header. By default SuprSend will show a bell icon, however you can customize this to [show your app icon](/docs/android-push-template#how-to-change-the-small-icon-for-a-notification%3F) instead. | | Sound | Single line Text | Sound file name without extension or with extension. This is used to play custom sound on notification delivery. If left blank, the default notification sound of the device will be played. To set custom sound, you'll have to [add raw sound file](/docs/android-push-template#how-to-add-a-custom-sound-file-in-your-android-project%3F) in your android app folder | | Custom key-value pair | key-value pair | You can use this field to send custom key-value pairs to users' device. This is generally used for storing and retrieving data for caching, app metadata, or user settings. You can combine it with silent notification if the purpose is to update data in user's applications without notifying them. Both key and value is passed as string in the notification payload. e.g., if you are passing this `json: {"foo":1, "bar:1}`. It will be passed as `"json": "{\"foo\":1,\"bar\":1}"` in the payload. You can add variables in both key and value to programmatically pass custom data based on your event or workflow input. **Please note that your app’s backend must be able to process custom key-value pairs for the data payload to function properly.** | **Required- Supported SDK version for app icon and sound:** * App icon functionality is supported in Android native and react native version 0.1.8 and above. * Custom Sound is supported in Android native and react native version 2.2.0 and above. If you are using an older SDK version, please upgrade to the latest SDK version ## Adding dynamic content in Android Push There will always be the case where you would be required to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending push. To send actual values to replace variables at the time of communication trigger, use one of our frontend or backend SDKs. Here is a step by step guide on how to add dynamic content in android push: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing the android push template. We support `handlebars` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable_name}}` If you have declared the variables in the global 'Variables' button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of error like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the 'Variables' button. To manually enter the variable name, follow the [handlerbars guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below are some examples of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the '`Templates`' section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "3,950.00" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "2,549.00" }, { "product_name": "RayBan Sunglasses", "product_price": "7,899.00" } ], "event": { "location": { "city": "Bangalore", "state": "KA" }, "order_id": "11200123", "first_name": "Nikita" }, "product_page": "https://www.suprsend.com" } ``` 1. To enter a nested variable, enter in the format `{{var1.var2.var3}}`. Eg. to refer to city in the example above, you need to enter `{{event.location.city}}` 2. To refer to an array element, enter in format `{{var1.[_index_].var2}}. Eg. to refer to `product\_name`of the first element of the array`array`, enter `\{\{array.\[0].product\_name}}\` 3. If you have any space in the variable name, enclose it in square bracket `{{event.[first name]}}` You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: * Make sure you have entered the variable name and the sample value in the `Variables` button. * Make sure you have entered the correct variable name in the template, as per the `handlebars` guideline. **What happens if there is variable mismatch at the time of sending?** At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. Eg. if there is an error in rendering Android Push template, but email template is successfully rendered, Android Push notification will not be triggered, but email notification will be triggered by SuprSend. ## How to change the small icon for a notification? To show a small app icon in your notification, you'll have to add a small icon in the drawable folder of application modules. You can either use a vector drawable or a png image. We recommend naming this file as `ic_suprsend_app_icon` **Generate icon images with alpha transparency:** Note that Android only uses the alpha channel for the icon. It will display monochrome in the status bar but an accent color can be applied to the left side the notification itself. You can add a vector drawable icon to the default drawable folder of your application module(androidApp/src/main/res/drawable/). Check out a [sample vector drawable link](https://github.com/suprsend/suprsend-android-sdk/blob/main/androidApp/src/main/res/drawable/ic_alarm.xml). You can add a icon in .png format. To render this png icon as a small icon in your push notification, you will have to create icons of below sizes and place them in their respective folders. Refer below table: | Density (dp) | Size (px) | | ------------ | --------- | | MDPI | 24x24 | | HDPI | 36x36 | | XHDPI | 48x48 | | XXHDPI | 72x72 | | XXXHDPI | 96x96 | Follow below steps to add icons in .png format **Option A. Using Android Asset Studio *(Recommended)*** To quickly generate small icons with the correct settings, you can use [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html#source.type=clipart\&source.clipart=ac_unit\&source.space.trim=1\&source.space.pad=0\&name=ic_suprsend_app_icon). **Option B. Manually create icons** If you prefer to create your own icons, you must make your icons for all sizes and make the small ones in white with a transparent background. Make sure the following paths exist in your `App -> main` folder, create any folders you are missing. Place images of each size in their respective folders. | SDK | File path | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Android Native | res/drawable-mdpi/ (24x24) res/drawable-hdpi/ (36x36) res/drawable-xhdpi/ (48x48) res/drawable-xxhdpi/ (72x72) res/drawable-xxxhdpi/ (96x96) | | React Native | android/app/src/main/res/drawable-mdpi/ (24x24) android/app/src/main/res/drawable-hdpi/ (36x36) android/app/src/main/res/drawable-xhdpi/ (48x48) android/app/src/main/res/drawable-xxhdpi/ (72x72) android/app/src/main/res/drawable-xxxhdpi/ (96x96) | Your project should look similar to this depending on your SDK. [Sample png drawable link](https://github.com/suprsend/suprsend-android-sdk/tree/main/androidApp/src/main/res) **Troubleshooting Icons Not Showing:** * If you see the default SuprSend bell icon even after adding app icon in your template, you did not add all icon sizes. Please add all icons sizes and correct paths. * If you see a solid square, you set the image to the correct path, but the image does not have alpha transparency. For more help, try using images from this [Android Asset Studio clipart](http://romannurik.github.io/AndroidAssetStudio/icons-notification.html#source.type=clipart\&source.clipart=access_alarm\&source.space.trim=1\&source.space.pad=0\&name=ic_suprsend_app_icon). ## How to add a custom sound file in your android project? From Android O onwards, notification sound became a property of notification channel and is only set at the time of new [category creation](https://developer.android.com/develop/ui/views/notifications/channels) (Notification channel is identified as [notification category](/docs/notification-category) in SuprSend). This means that for a user when you first time send the notification in a particular category, the sound is set for that category and even if you change the sound in your template, the sound in that category will not change. The updated sound will play for the user either if you send notification in a new category or user uninstalls and re-installs the app. For devices on android version before O, the sound will be set at individual notification level and whenever you change the sound on a template, it will play the new sound for user. Leaving the sound field blank will play the default notification sound on the device. To change the notification sound you will have to add the sound file to the raw folder of your android app. ```Text Location theme={"system"} projectroot/app/res/raw ``` Please keep the file name in lower case and avoid using space in the file name instead you can use underscore(\_) in place of space E.g.- notification.mp3, notificationMusic.mp3,notification\_music.mp3 **Sound is set at channel level for android version >= 8.0:** From Android O onwards (\~95% of the android users), notification sound became a property of notification channel and is only set at the time of new [category creation](https://developer.android.com/develop/ui/views/notifications/channels). So, the new sound set at template will only change for users if * you are sending the notification in a new [notification category](/docs/notification-category) * user is installing the app for the first time or getting notification in that category for the first time * user has uninstalled and reinstalled the app ## Image optimisations SuprSend does some image optimisations in push notifications for images that are static in nature, ie. image is uploaded in 'Banner Image' field on template, so that the push notification delivery rate increases and the time of delivery for push notification reduces. These optimisations are as below: **1. Image optimisation based on Screen Width** If there is a very large image in the "Banner Image" field, SuprSend will reduce the image size to fit as per the mobile screen width of your user. **2. Image optimisation based on network** Your users might be on different network, namely wifi, 4G, 3G, 2G. And therefore, depending on your user's network we optimise the image size. This improves the push notification delivery for users whose network is not very fast. *** # Send and Track Events Source: https://docs.suprsend.com/docs/android-send-event-data Android SDK methods to send events on user action to SuprSend and trigger workflow. ## Pre-Requisites [Create User](/docs/android-create-user) ## Send Event You can setup events on user actions in your app and configure workflows on top of it that triggers when the corresponding event is passed through App. Variables added in the template or workflow should be passed as event `properties` You can send Events from your app to SuprSend platform by using `ssApi.track()` method ```dart Dart theme={"system"} //method suprsend.track(event_name); //for single event suprsend.track(event_name, property_obj); //for event with properties //Sample to track an event suprsend.track("clicked"); //Sample to track an event with one or more properties suprsend.track("clicked", {"page":"Dashboard","city":"Bangalore"}); ``` **Naming Guideline:** Event Name or Property Name does not start with **`$`** or **`ss_`**, as we have reserved these symbols for our internal events and property names. ### System Events tracked by SuprSend There are some system events tracked by SuprSend SDK by default. These are some basic events, as well as events that are necessary for tracking notifications related activity (like delivered, clicked, etc). You are not required to do anything here. | Event Name | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \$app\_installed | \$app\_installed will get tracked when user launch his app for the first time. FYI cases in which it will also get called 1. When user launches his app for first time. 2. When user uninstall the app and installs it again. 3. \[Multiple device login ]When user launch app for first time on different devices. 4. When user clears the app cache and relaunches the app. | | \$app\_launched | Gets tracked when user launches the app each time. | | \$user\_login | Gets tracked when user logs in inside the app | | \$user\_logout | Gets tracked when user logs in to the app | | \$notification\_delivered | Will get tracked when the suprsend notification payload is received at SDK end. | | \$notification\_clicked | Will get tracked when user either clicks the notification body or any action button in the notification. | | \$notification\_dismissed | Will get tracked when user dismisses the notification by left swiping the notification or by clicking on "Clear All" button | ## Advanced Concepts ### 1. Super Properties Super Properties are data that are always sent with events data. These super properties will be sent in each event after calling this method. Super Properties will be stored in local storage, and will persist across invocations of app. There are some super properties that SuprSend SDK will send by default. Developer can set custom super properties as well with `ssApi.getUser().setSuperProperty()` method ```kotlin kotlin theme={"system"} //method //for setting single super property ssApi.getUser().setSuperProperty(key: String, value: Any) //for setting multiple super properties ssApi.getUser().setSuperProperties(jsonObject: JSONObject) //Example //for setting single super property ssApi.getUser().setSuperProperty("Location", "Banglore") //for setting multiple super properties ssApi.getUser().setSuperProperties(JSONObject().apply { put("Location","XYZ") put("Pincode", 1234567) put("Amount", 99.99") }) ``` Default Super Properties tracked by SuprSend SDK: | Super Property | Description | Sample Value | | ---------------------- | -------------------------------------------- | ---------------- | | \$app\_version\_string | Version of your app | 0.0.1 | | \$app\_build\_number | Build number of your app | 2 | | \$os | Operating system of the user | android | | \$manufacturer | Manufacturer of the user's device | OnePlus | | \$brand | Brand of the user's device | OnePlus | | \$model | Model of the user's device | GM1901 | | \$deviceId | Device id | 89eead05a0150146 | | \$ss\_sdk\_version | SuprSend SDK version | 0.1.31 | | \$network | Network on which the user is | wifi | | \$connected | Whether the user is connected to the network | true | There are unset custom super properties with `ssApi.getUser().unSetSuperProperty()` method. This method will stop calling that property with every event trigger. ```kotlin kotlin theme={"system"} //method ssApi.getUser().unSetSuperProperty(key: String) //Example ssApi.getUser().unSetSuperProperty(listOf("Location","Pincode","Amount")) ``` ### 2. Special Events Special events are some best use case events defined by SuprSend. You could call these events with some of their pre-defined properties. You can call purchase made event if user is doing a transaction on your platform. You can pass property values like product id, product name, and amount in the event. ```kotlin kotlin theme={"system"} ssApi.purchaseMade(properties: JSONObject) ssApi.purchaseMade(JSONObject().apply { put("product_id", "P1") put("product_name", "Car") put("amount", "$15000") }) ``` ### 3. Flush Events SuprSend SDK automatically flushes events at an interval of 5 seconds, and on certain activities like app relaunch, etc. If you wish to flush a time sensitive event to SuprSend immediately, you can use the `suprSendApi.flush()` method. All the system tracked events are flushed immediately ```javascript javascript theme={"system"} suprSendApi.flush(); ``` *** # Androidpush Source: https://docs.suprsend.com/docs/androidpush-errors Possible androidpush delivery errors reported by FCM and their resolutions. ## FCM - Legacy API | Error | How to solve? | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): context deadline exceeded | This happens when FCM server took longer than the timeout period to process the request. This can happen due to one of the following reasons: - The application has a defined timeout deadline and the FCM server takes longer than that to respond. - Slow or unstable network connections between the application and the FCM server. - FCM server is experiencing high server loads or temporary issues. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): dial tcp xxxx: connect: network is unreachable | | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): net/http: TLS handshake timeout | | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): read xxxx: read: connection reset by peer | The "Connection reset by peer" logs are likely being caused by the HTTPS Load Balancer in GCP having a timeout of 600 seconds. Please reach out to our [support](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#) in case of this issue. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): unexpected EOF | | | QUOTA\_EXCEEDED: Resource has been exhausted (e.g. check quota). | This indicates that FCM time limit or rate limit quota is exhausted. You can check for allowed quota [here](https://firebase.google.com/docs/functions/quotas). | | Request contains an invalid argument; INVALID\_ARGUMENT: The registration token is not a valid FCM registration token; UNREGISTERED\_DEVICE \| INVALID\_REGISTRATION\_TOKEN: Requested entity was not found.; 404 error: 404 Not Found | This error occurs when the client FCM token becomes invalid. An existing registration token may cease to be valid in a number of scenarios: - The client app unregistered with FCM. - User uninstalls / re-installs the application - The registration token expires (e.g., Google might decide to refresh registration tokens). - The client app is updated but the new version is not configured to receive messages. SuprSend automatically flags these tokens as invalid in user profile, since once an FCM token becomes invalid, it is never going to be valid again. You don't have to explicitly handle these cases as SuprSend SDK handles token refresh as soon as the user installs the App or become active again. | | Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See [https://developers.google.com/identity/sign-in/web/devconsole-project](https://developers.google.com/identity/sign-in/web/devconsole-project). | FCM [vendor](https://app.suprsend.com/en/production/vendors/androidpush/fcm-androidpush?brand_id=default) credentials are not valid or expired. Check [FCM vendor documentation](/docs/firebase-fcm-androidpush?_gl=1*1a8q6ub*_ga*MjAyMzgwNzI3MC4xNzAyMTE3MTk5*_ga_PPDYBESP2L*MTcwMjI0MTc5My42LjEuMTcwMjI0MTk2MS42MC4wLjA.*_gcl_au*MzY5NjIwNjQ3LjE3MDIxMTcxOTkuMTM5NTE5NzE0NS4xNzAyMTIxMDQ0LjE3MDIxMjEwNDM.) for details. | | SENDER\_ID\_MISMATCH: SenderId mismatch | Sender ID in FCM [vendor](https://app.suprsend.com/en/production/vendors/androidpush/fcm-androidpush?brand_id=default) form is not valid. A registration token is tied to a certain group of senders. When a client app registers for FCM, it must specify which senders are allowed to send messages. You should use one of those sender IDs when sending messages to the client app. If you switch to a different sender, the existing registration tokens won't work. Check [FCM vendor documentation](/docs/firebase-fcm-androidpush?_gl=1*1a8q6ub*_ga*MjAyMzgwNzI3MC4xNzAyMTE3MTk5*_ga_PPDYBESP2L*MTcwMjI0MTc5My42LjEuMTcwMjI0MTk2MS42MC4wLjA.*_gcl_au*MzY5NjIwNjQ3LjE3MDIxMTcxOTkuMTM5NTE5NzE0NS4xNzAyMTIxMDQ0LjE3MDIxMjEwNDM.) for details. | | The service is currently unavailable. | Indicates that FCM service is currently unavailable, often due to issues on the Firebase server side. In such instances, SuprSend automatically retries the request. Here are some recommended steps that you can take: - **Check [Firebase status page](https://status.firebase.google.com/)** for ongoing incidents or outages affecting the FCM service. - Check for Firebase updates on [Firebase google channel](https://groups.google.com/g/firebase-talk?pli=1) and [release notes](https://firebase.google.com/support). FCM environment regularly gets software releases. Some of them may not be fully ready for production use and cause these failures. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | Internal error encountered; internal server error; 500 error: 500 Internal Server Error | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | message is too big | Check that the total size of the payload data included in a message does not exceed FCM limits: FCM has a maximum allowed size of 4KB, which enforces a 1000 character limit. The limit drops to 2KB (500 characters) if the message includes an image URL. Also, ensure that the image size URL is of size 2KB. | | code: 500, name: AttributeError, description: module 'collections' has no attribute 'Iterable' | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | 401: Authentication Error; Authentication backend unknown error; Authentication backend unavailable. | The sender account used to send a message couldn't be authenticated. Possible causes are: - Authorization header missing or with invalid syntax in HTTP request. - The Firebase project that the specified server key belongs to is incorrect. - Legacy server keys only—the request originated from a server not whitelisted in the Server key IPs. Check that the token you're sending inside the Authentication header is the correct server key associated with your project. See [Checking the validity of a server key](https://firebase.google.com/docs/cloud-messaging/auth-server#checkAPIkey) for details. If you are using a legacy server key, you're recommended to upgrade to a new key that has no IP restrictions. See [Migrate legacy server keys](https://firebase.google.com/docs/cloud-messaging/auth-server#migrate-legacy-server-keys). | | 502 error: 502 Bad Gateway | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | 500: read tcp xxxx: i/o timeout | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | 500: dial tcp: lookup db-suprsend-production-do-user-9199186-0.b.db.ondigitalocean.com on 10.245.0.10:53: server misbehaving | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | Post "[https://fcm.googleapis.com/fcm/send"](https://fcm.googleapis.com/fcm/send%22): read xxxx: read: connection reset by peer | The "Connection reset by peer" logs are likely being caused by the HTTPS Load Balancer in GCP having a timeout of 600 seconds. Please reach out to our [support](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#) in case of this issue. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | You can find the entire [list of FCM errors here](https://firebase.google.com/docs/cloud-messaging/http-server-ref#error-codes). ## FCM - V1 version | Error | How to solve? | | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | INVALID\_ARGUMENT; (HTTP error code = 400) Request parameters were invalid. | Potential causes include invalid registration, invalid package name, message too big, invalid data key, invalid TTL, or other invalid parameters. - **Invalid registration**: Check the format of the registration token you pass to the server. Make sure it matches the registration token the client app receives from registering with Firebase Notifications. Do not truncate or add additional characters. - **Invalid package name**: Make sure the message was addressed to a registration token whose package name matches the value passed in the request. - **Message too big**: Check that the total size of the payload data included in a message does not exceed FCM limits: 4KB (1000 characters) - **Invalid data key**: Check that the payload data does not contain a key (such as from, or gcm, or any value prefixed by google) that is used internally by FCM. Note that some words (such as collapse\_key) are also used by FCM but are allowed in the payload, in which case the payload value will be overridden by the FCM value. - **Invalid TTL**: Check that the value used in ttl is an integer representing a duration in seconds between 0 and 2,419,200 (4 weeks). - **Invalid parameters**: Check that the provided parameters have the right name and type. | | UNSPECIFIED\_ERROR; No more information is available about this error. | FCM has not provided any details about the error and if you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | SENDER\_ID\_MISMATCH; (HTTP error code = 403) The authenticated sender ID is different from the sender ID for the registration token. | Sender ID in FCM [vendor](https://app.suprsend.com/en/production/vendors/androidpush/fcm-androidpush?brand_id=default) form is not valid. A registration token is tied to a certain group of senders. When a client app registers for FCM, it must specify which senders are allowed to send messages. You should use one of those sender IDs when sending messages to the client app. If you switch to a different sender, the existing registration tokens won't work. Check [FCM vendor documentation](/docs/firebase-fcm-androidpush?_gl=1*1a8q6ub*_ga*MjAyMzgwNzI3MC4xNzAyMTE3MTk5*_ga_PPDYBESP2L*MTcwMjI0MTc5My42LjEuMTcwMjI0MTk2MS42MC4wLjA.*_gcl_au*MzY5NjIwNjQ3LjE3MDIxMTcxOTkuMTM5NTE5NzE0NS4xNzAyMTIxMDQ0LjE3MDIxMjEwNDM.) for details. | | QUOTA\_EXCEEDED: (HTTP error code = 429) Sending limit exceeded for the message target. | This error can be caused by exceeded message rate quota, or exceeded device message rate quota. - **Message rate exceeded**: The sending rate of messages is too high. SuprSend internally optimize for the sending rate. Reach out to our [support](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#) in case of this error. - **Device message rate exceeded**: The rate of messages to a particular device is too high. See [message rate limit to a single device](https://firebase.google.com/docs/cloud-messaging/concept-options#device_throttling). | | UNAVAILABLE; (HTTP error code = 503) The server is overloaded. | Indicates that FCM service is currently unavailable, often due to issues on the Firebase server side. In such instances, SuprSend automatically retries the request. Here are some recommended steps that you can take: - **Check [Firebase status page](https://status.firebase.google.com/)** for ongoing incidents or outages affecting the FCM service. - Check for Firebase updates on [Firebase google channel](https://groups.google.com/g/firebase-talk?pli=1) and [release notes](https://firebase.google.com/support). FCM environment regularly gets software releases. Some of them may not be fully ready for production use and cause these failures. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | | INTERNAL; (HTTP error code = 500) An unknown internal error occurred. | The FCM server encountered an error while trying to process the request. SuprSend has implemented exponential back-off retry mechanism to handle such errors. If you continue to receive this error, you should raise it to [FCM support](https://firebase.google.com/support). You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if androidpush is unreachable. | You can find the entire [list of FCM errors for V1 version here](https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode). *** # Authentication Methods Source: https://docs.suprsend.com/docs/authentication-methods All types of authentication methods available for securely accessing your SuprSend account. **We support invite-only account association:** This would mean that users registering with the same domain as your account domain won't be automatically added to the account. Users can join the account only if they are invited by the admin. ## Magic Auth This is a password less authentication method that allows users to sign in or sign up using a unique six-digit, one-time-use code sent to their email inbox. This code remains valid for 10 minutes. Upon entering the code, users gain access to the SuprSend dashboard. Additionally, users who signed up using Magic Auth can switch to Google SSO during login. ## Social Login (Google, GitHub SSO) Users can login using their existing credentials with OAuth providers such as Google or GitHub. ## SAML 2.0 SSO This method enables users to authenticate using their corporate identity provider, such as Okta, to access their SuprSend account. Once SSO is enabled, all members are redirected through the identity provider's authentication flow for access. **Available in enterprise plan** If you're on the Enterprise plan and would like to enable the single sign-in, reach out to our support team at [support@suprsend.com](mailto:support@suprsend.com). We'll share a step-by-step guide tailored to your specific identity provider (IdP). ## Multi-Factor Auth (MFA) You can enable MFA in your account to introduce an additional layer to security to your account. Admin can enable MFA from your teams page. Once enabled, all team members will have provide an additional time-based one-time password (TOTP) every time they login. *** # AWS SNS Source: https://docs.suprsend.com/docs/aws-sns-sms Guide to integrate AWS SNS with SuprSend for SMS delivery. ## Pre-Requisites You'll need a AWS account to complete this tutorial. You can use your existing AWS account to integrate, or [Create an AWS account](https://portal.aws.amazon.com/billing/signup) ## Amazon SNS integration on SuprSend account Follow below steps to integrate your Amazon SNS account with SuprSend ### Step-1 : Create AWS IAM User To send email through SNS, you need an IAM user (Access-Key-ID and Secret-Access-Key) with necessary permission. To create an IAM user, refer to this [documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) * Create an IAM user (with Programmatic access). * Attach Policy`AmazonSNSFullAccess`to this user. * Copy and save the`Access-key-ID`and`Secret-Access-Key`securely. You'll need to add this information on SuprSend vendor integration page An AWS SNS resource resides in a particular region. Make a note of which aws-region you are going to use for sending SMS. In this guide, we'll assume aws-region :`ap-south-1` for all illustrative purposes. ### Step-2 : Add phone number in your SNS account Before moving into configuration steps, here are a few other things to keep in mind: * **SNS Sandbox Mode:**If you are creating a new SNS account. Your account will be in SNS sandbox. AWS adds all new accounts in the SNS sandbox by default. In sandbox mode, you'll be able to send messages to only verified destination phone numbers. Once you're done testing, you can move out of the SNS sandbox. Refer next section for the steps to configure SNS sandbox and how to move your account out of it. * **Delivery Tracking**. We cannot currently track delivery for SMS sent through AWS SNS. This means that messages sent through SNS will always show on triggered state in logs. Follow below Steps to add phone number and set tracking in your SNS account: Go to AWS SNS console -> Mobile -> Text Messaging (SMS). Select origination numbers either from top right or from left side menu and Click on **"Provision number in Pinpoint"** This will open pinpoint console. Click on **"Request Phone Number"** on this screen and provision a phone number from a country where SMS channel is enabled. **Phone Number Registration required for USA numbers:** If you’re provisioning numbers from USA, you need to complete your phone number registration to send messages successfully across the USA. Read more about phone number registration [here](https://docs.aws.amazon.com/pinpoint/latest/userguide/settings-sms-tfn-register.html) You can also register for Sender ID if you want to send messages with an alphanumeric code. Sender IDs is not supported in all countries. To see if it is supported in your region, see [Supported Regions and Countries](https://docs.aws.amazon.com/sns/latest/dg/sns-supported-regions-countries.html) By default, your account will be in sandbox mode where you’ll only be able to send messages to added destination numbers. Add few phone numbers here to test out the integration. Once the numbers are added, scroll up the screen and click on **"Publish Text Message"** to send one test message from the console itself to test out the integration. If the message is successful, we recommend you to exit sandbox before integrating with SuprSend. * Click on **`Exit SMS Sandbox`** from console. * A prompt will open to raise an AWS support case to exit sandbox and increase spending limits. * Add relevant information and in the new limit add 100 USD (recommended) or higher if needed. The account will be production ready within 24 hours of raising this request Remember that this setup is just to see the delivery reports within AWS dashboard. It doesn't enable delivery tracking in SuprSend logs. Follow below steps to setup tracking: 1. Go to Text Messaging Preferences in SNS console and click edit. 1. Add relevant configuration and expand Delivery status logging. 2. Create a new IAM role on this screen. It will ask for permission to send delivery logs to cloudwatch, allow that and save changes. 1. Once configured, you’ll be able to see delivery logs of your sent messages on this console. You can also access it from cloudwatch console. You’ll also be able to view message analytics on SNS console. ### Step-3 : Add SNS vendor settings on Suprsend dashboard On the SuprSend dashboard, go to vendor page from side panel and click SMS -> AWS SNS from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily. e.g. - *AWS SNS \[Production]* | | AWS region | aws-region you are going to use for sending messages. | | Access Key ID | This is the key ID linked to your IAM user. This is used to send messages on your behalf from your SNS account. [Refer above section](/docs/aws-sns-sms#step-1-create-aws-iam-user) to get access key | | Secret Access Key | This is the secret access key linked to your IAM user. This is used to send messages on your behalf from your SNS account. [Refer above section](/docs/aws-sns-sms#step-1-create-aws-iam-user) to get access key | | Origination Phone Number | Number through which your messages will be sent. Origination number is mandatory if you are sending to the USA; otherwise, you can leave this field empty | | Sender ID | You can add your registered Sender ID here if you want to send messages with an alphanumeric code. [Read more](https://docs.aws.amazon.com/sns/latest/dg/channels-sms-originating-identities-sender-ids.html) about Sender ID | | Price per notification | This is the amount you pay per email notification to SNS. It helps us to calculate, estimate and optimise your cost spent on notifications. | *** # Batch Source: https://docs.suprsend.com/docs/batch Learn about batch node in workflow and how to use it to group similar notifications into a single notification. Batch node aggregates multiple triggers into a single batch output to send one consolidated notification rather than sending notification for every user activity. Batching events are useful when a user needs to be notified about a lot of events happening at once but doesn't need a notification for every single event within the batch. e.g., if you have a product where users can interact with each other's content and post 5 comments in 10 minutes. In this case, rather than sending 5 notifications, you can batch the events for 10 minutes and send one notification about the 5 comments that the user received. ## How Batching works When a workflow reaches the batch node, it opens a batch for given batch window. When the batch window is open, all the workflows initiated for the recipient with the same workflow slug and batch key are aggregated in the batch. Batches are created unique for each recipient and batch key combination for that workflow. After the batch window is closed, it will send one notification for each batch created in the batch window. Also, with retain batch events, you can limit the number of event data that should be retained in the batch for sending the notification. The output variable structure of a batch is different from the data in your event properties. Refer [Using batch variables in templates](/docs/batch#using-batch-variables-in-templates) to know more. ## Batch Window Batch window is the time for which batch should be open for. After receiving the first event, batch window opens and all the events coming in this interval will be accumulated. The next node is executed after the batch window is closed. There is a setting `Flush first item immediately` which bypasses batch window and sends the first trigger immediately and accumulates the rest. There are 3 types of window type: **Fixed** (fixed for all users), **Dynamic** (Passed in trigger payload), Relative (relative to a future timestamp, e.g. 10 minutes before task due time). Fixed batch window is defined in your workflow form as `\\\\\\\*\\\\\\\*d \\\\\\\*\\\\\\\*h \\\\\\\*\\\\\\\*m \\\\\\\*\\\\\\\*s` and it keeps the batch open for a fixed duration for all users. An example of using fixed batch window can be social media updates where you want to send alert to users about new comments or post likes after an hour from the first comment. In case of dynamic batch window, batch duration is computed using the data from your event properties. Dynamic batch window is helpful for cases where batch schedule is defined by the user. You can add duration key as a [JQ-expression](https://jqlang.github.io/jq/manual/). Below are some examples of how to add duration key in JQ format: 1. General format for duration key at parent level is`.duration_key` 2. If the duration key is a nested event property key like shown below, enter it in the format`.preferencs.alert_frequency`. ```json json theme={"system"} properties = { "preferencs": { "alert_frequency": "1h", "channels": ["email","inbox"] } ``` When the duration key specified is missing, or resolves to an invalid value, workflow execution will stop and corresponding error will be logged in the logs❗️ Your duration key variable can be computed to either: * An ISO-8601 timestamp (e.g. 2024-03-02T20:34:07Z) which must be a datetime in the future, or * A relative duration unit, which can be * an integer like`50`, which will be considered as duration in seconds * an interval string defined as`*\\\\\\\*d \\\\\\\*\\\\\\\*h \\\\\\\*\\\\\\\*m \\\\\\\*\\\\\\\*s`, where d = day, h = hour, m = minutes and s = seconds **Batch window is not modified by subsequent workflows once the window is open** It's important to note that an open batch window cannot be extended by a subsequent workflow trigger if a different dynamic batch window is specified. Once a batch has been opened by a workflow trigger, its window interval is set and cannot be altered. Relative batch window is calculated based on a future timestamp. e.g., sending a batched list of all pending tasks 1 hour before workday end time, where workday\_end\_time is a key in the trigger payload. It consists of three key components: 1. **Interval**: The delay from the future timestamp, formatted as xxdxxhxxmxxs (e.g. 30m for 30 minutes). This can be: Fixed (e.g. always 1 hour minutes before). Dynamic, where the value is retrieved from the payload (e.g. in Google Calendar, users can set reminders for 10 or 20 minutes before an event). 2. **Before/After**: Determines whether the interval is subtracted (before) or added (after) to the timestamp. 3. **Timestamp**: An ISO-8601 format datetime (e.g. 2024-03-02T20:34:07Z), which must be in the future. Dynamic Interval & Timestamp must be passed as a JQ-expression. Examples: Timestamp at the parent level: `.timestamp`. If the dynamic interval is set as recipient property: `."$recipient".interval` ## Batch Key This is the property in your `track event` call used for defining unique batches of the events. By default, event will be batched per user. You can use batch key to create multiple batches per user. Batches are created for each unique `distinct_id` and `batch_key` combination. For instance, you can add `post_id` as your batch key if you want to send separate notifications for comments on different LinkedIn posts. ## Retain Batch events It will define the number of event data that will be included in your batch variable. You have the option to display either the first n events or the last n events in your batch output. By default, the first 10 events are included in your batch output variable once the batch window closes. You can customize the number of events to any value between 2 and 100. ## Flush first item immediately When this setting is enabled, the first trigger sends a notification immediately, while subsequent triggers are grouped into a batch. Here, the batch is opened on receiving the first item irrespective of the flush setting. The only difference is, unlike a normal batch, the first item will not be included in batch events and will continue execution past the batch step. The output structure of the first notification matches the batch structure, with `$batched_events_count = 1`. You can use this count in your workflow or templates to customize content based on whether the notification is sent immediately or as part of a batch. **Example Use Case:** Send anomaly alert with first notification sent at the occurrence of first error and next alert sent after 30 minutes if there are further errors. These could be the template content for single vs batched trigger: * First notification (sent immediately):`A new error encountered in your account - {{$batched_events.\\\\\\\[0\\\\\\\].error_message}}` * Batched notification (sent after grouping all errors from second error onwards):`{{$batched_events_count}} errors occurred in your account in the last 30 mins - {{#each $batched_events}}{{error_message}}{{/each}}` ## Using Batch variables in templates Batch output variable has 2 type of variables: 1. `$batched_events`array : All the event properties corresponding to a batched event is appended to this array and can be used in the template in the array format. The number of event properties returned here is limited by retaining batch events. 2. `$batched_event_count`: This count represents the number of events in a batch and is utilized to render the batch count in a template. For instance, you might send a message like,`Joe left 5 comments in the last 1 hour`where 5 corresponds to \$batched\_event\_count. 📘 Please note that **Retain batch events** setting doesn't impact the count, it just limits the number of event properties returned in `$batched_events` array. Let's understand the batch variable structure with an example of task comments with below notification content. ``` 3 comments are added on your task in last 1 hour. - Steve: Hey, added the test cases added for PRD-12 - Olivia: Hey, done with the testing. Check the bugs - Joe: 3 bugs are resolved, 4 are still pending ``` Here is a list of events triggered in the batched window: ```javascript javascript theme={"system"} //Event 1 const event_name = "new_comment" const properties = { "name": "Steve", "card_id": "SS-12", "comment": "Hey, added the test cases added for PRD-12" } const event = new Event(distinct_id, event_name, properties) //Event 2 const event_name = "new_comment" const properties = { "name": "Olivia", "card_id": "SS-12", "comment": "Hey, done with the testing. Check the bugs" } const event = new Event(distinct_id, event_name, properties) //Event 3 const event_name = "new_comment" const properties = { "name": "Joe", "card_id": "SS-12", "comment": "3 bugs are resolved, 4 are still pending" } const event = new Event(distinct_id, event_name, properties) ``` Output variable of the batch will have `$batched_events_count` and `$batched_events` array of all properties passed in the event payload as shown below: ```json json theme={"system"} { "$batched_events": [ { "name": "Steve", "card_id": "SS-12", "comment": "Hey, added the test cases added for PRD-12" }, { "name": "Olivia", "card_id": "SS-12", "comment": "Hey, done with the testing. Check the bugs" }, { "name": "Joe", "card_id": "SS-12", "comment": "3 bugs are resolved, 4 are still pending" } ], "$batched_events_count": 3 } ``` This is how you'll add the variable in your template to render the desired notification content. ```Text Template theme={"system"} {{$batched_events_count}} comments are added on your task in last 1 hour. {{#each $batched_events}} - {{name}}: {{comment}} {{/each}} ``` ```Text Rendered notification theme={"system"} 3 comments are added on your task in last 1 hour. - Steve: Hey, added the test cases added for PRD-12 - Olivia: Hey, done with the testing. Check the bugs - Joe: 3 bugs are resolved, 4 are still pending ``` You can also test this behaviour via `Enable batching` option in [Mock data](/docs/templates#adding-dynamic-content) button on template details page. Once enabled, you'll start getting `$batched_events\\\\\\\*` variable in auto suggestion on typing `{{` in template editor. The variables in mock data will be treated as event properties and `Event Count` will imitate the number of times this event will be triggered in the batch. ## Transforming Batch variable output There can be cases where you need to split the batch output variables into multiple arrays based on keys in your input data. e.g., to send a message like `You have got 5 comments and 3 likes on your post in the past 1 hour` where post and likes are interaction\_type in your input payload. You can use [data transform node](/docs/data-transform) and generate relevant variables using **JSONNET editor** to handle this use case. Let's take below example. There are 3 post interactions, 2 comments and 1 like and this is your workflow trigger. ```node node theme={"system"} //Event 1 const event_name = "new_post_interaction" const properties = { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" } const event = new Event(distinct_id, event_name, properties) //Event 2 const event_name = "new_post_interaction" const properties = { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } const event = new Event(distinct_id, event_name, properties) //Event 3 const event_name = "new_post_interaction" const properties = { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } const event = new Event(distinct_id, event_name, properties) ``` Without transformation, batch output will look like this: ```json json theme={"system"} { "$batched_events":[ { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" }, { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } , { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } ], "$batched_events_count":3 } ``` We'll add 3 variables in data transform node * `comment_count`: to get the count of all interactions where`interaction_type = comment` * `like_count`: to get the count of all interactions where`interaction_type = like` * `all_comments`: to fetch all array objects where`interaction type = comment` ```json JSONNET syntax to generate above variables theme={"system"} //comment_count std.length([x for x in data["$batched_events"] if x.interaction_type == "comment"]) //like_count std.length([x for x in data["$batched_events"] if x.interaction_type == "like"]) //all_comments [x.comment for x in data["$batched_events"] if x.interaction_type == "comment"] ``` After data transform node, output variables will contain 3 additional keys generated above. You can use these variables in your template to send the desired message as `You have got {{comment_count}} comments and {{like_count}} likes on your post in the past 1 hour.`. ```json json theme={"system"} { "comment_count":"2", "like_count":"1", "all_comments":["Well written! looking for more such posts","Every leader should read this"], "$batched_events":[ { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" }, { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } , { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } ], "$batched_events_count":3 } ``` *** ## Frequently Asked Questions: This error indicates that the event triggering the workflow was added to the ongoing batch of an existing workflow. In the context of batched workflows, this occurrence is expected and transient, so it can be safely disregarded. Each batch node aggregates the event triggers that initiate the workflow and doesn't apply to the output of the batch node connected to its input. The primary impact of connected batch nodes is their cumulative effect on the delay of the notification. The delivery node utilizes the output of the last connected batch node to its input. You can use dynamic batch windows and pass per-user batch durations as part of the event property. *** # Best Practices for Key & Token Management Source: https://docs.suprsend.com/docs/best-practices-for-api-keys-management How to securely manage Workspace Keys, Secrets, API Keys, and Service Tokens in your application and backend code. SuprSend provides multiple authentication methods, each with different scopes: * **Workspace Key & Secret** → Backend SDK authentication * **API Keys** → REST API authentication per workspace * **Service Tokens** → Management API authentication across workspaces This guide covers best practices for keeping them secure. *** ## 1. Workspace Key & Secret * Pre-generated for each workspace. * Used only with **backend SDKs**. * Safer by design as the **Workspace Secret is never transmitted over the network**. **Best Practices**: * Never share Workspace Secrets (not even with SuprSend support). * Store them securely in environment variables or a key management system. * Rotate if compromised or leaked. Secret rotation option is not currently exposed to SuprSend dashboard. Please reach out to [support@suprsend.com](mailto:support@suprsend.com) for secret rotation. *** ## 2. API Keys * Used for authenticating **REST API requests** at the workspace level. * Each workspace has its own set of API Keys, isolating staging and production environments. **Best Practices**: * Treat API Keys as sensitive secrets — never expose them in client-side code. * Always store keys securely (as environment variables or in a secure vault). * Rotate periodically (e.g., every 6 months). * Rotate immediately if a key is compromised or accidentally exposed. * Monitor API usage for anomalies on [SuprSend dashboard -> Logs (Requests tab)](https://app.suprsend.com/en/staging/logs/requests?last_n_minutes=1440) (unexpected spikes, unauthorized calls). *** ## 3. Service Tokens * Used to authenticate **Management APIs**. * Scoped at the **account level**, allowing cross-workspace operations (e.g., promoting workflows from staging → production). * Provide higher privilege than API Keys, so require stricter handling. **Best Practices**: * Limit use of Service Tokens to CI/CD pipelines and automation — avoid day-to-day manual use. * Store them as environment variable or encrypted secrets manager. * Rotate on a scheduled basis (every 6–12 months). * Rotate immediately if exposed or if a privileged user leaves the team. * Maintain strict access control — only admins or automation systems should have access. *** ## General Security Guidelines 1. **Never commit keys/tokens** to version control (e.g., GitHub). 2. **Use environment variables** or a **Key Management Service** (e.g., AWS KMS, HCP Vault, GCP Secret Manager). 3. **Follow least privilege principle** — share keys only with systems or people that need them. 4. **Set up monitoring & alerts** for unauthorized or unusual activity. 5. **Rotate keys/tokens** regularly and immediately upon suspected compromise. *** ## Rotation Strategy * **Scheduled Rotation** * API Keys → every 6 months * Service Tokens → every 6–12 months * Workspace Secrets → rotate if organizational policy requires * **Ad-Hoc Rotation** * Immediately upon exposure (logs, repos, screenshots) * On suspicious activity or abuse * When a team member with access leaves # Batching & Digest Source: https://docs.suprsend.com/docs/best-practices-for-batching-digest Guide on designing the right batching logic to group similar notifications and reduce notification fatigue, without compromising on user engagement. Creating an effective notification system is essential for maintaining user engagement without overwhelming them. By leveraging batching and digest techniques, you can enhance the user experience while ensuring that important updates are communicated effectively. Below, we delve into best practices for building notifications using batching and digest. # Group Similar Notifications One of the fundamental principles of effective notification management is grouping similar notifications. The primary goal here is to avoid overwhelming the user with a barrage of alerts. For instance, if a user receives multiple social media notifications, it is more efficient to combine them into a single alert. This approach not only reduces notification fatigue but also makes it easier for users to manage their alerts. By [batching](/docs/batch) similar notifications, you provide a more streamlined and user-friendly experience. In addition to grouping notifications by general use case, employing a [grouping key](/docs/batch#batch-key) can further enhance the efficiency of your notification system. For instance, rather than aggregating all comments across various posts, you can group notifications specifically by comments on a single post. This refined approach ensures that users receive more relevant and organised information, reducing clutter and improving the overall user experience. # Prioritise Important Notifications Not all notifications are created equal. Some are more critical than others and need to be highlighted or sent immediately. For instance, security alerts or urgent messages should not wait for the next batch or digest but should be delivered instantly. Prioritising important notifications ensures that users do not miss out on crucial information. This practice also helps in building trust, as users know that they will be promptly informed about significant events. If similar types of alerts can vary in severity, implement [conditional logic](/docs/branch) to determine whether they should be delivered immediately or included in a batch. For instance, critical alerts that require urgent user attention should bypass batching and be sent instantly, while less critical alerts can be aggregated and sent at a later time. This approach ensures that users are promptly informed of urgent issues while reducing the frequency of less critical notifications, thereby enhancing the overall user experience. # Allow User Customisation Empowering users to customise their notification [preferences](/docs/user-preferences) is a key aspect of a user-centric approach. Allow users to choose the type of notifications they receive and the frequency of digests. This customisation can include options such as receiving notifications for certain types of activities, setting quiet hours, or choosing between immediate alerts and daily digests. By offering this level of customisation, you respect user preferences and enhance their overall experience. # Respect Quiet Hours Implementing respect for user-defined quiet hours is an essential consideration. Allow users to set periods during which they do not wish to receive any notifications. This feature is particularly important for maintaining a healthy work-life balance and preventing notification fatigue. By respecting quiet hours, you show consideration for the user's time and well-being. > 📘 **We are rolling out support for Schedules soon.** # Monitor Engagement Metrics Keeping an eye on how users interact with your notifications and digests is crucial for assessing their effectiveness. Track engagement metrics such as open rates, click-through rates, and user feedback. Low engagement levels might indicate the need for adjustments in your notification strategy. Monitoring these metrics helps you understand user behaviour and preferences, enabling you to refine your approach accordingly. # Keep Digests Concise When creating digest emails or messages, brevity is essential. Users are more likely to engage with concise and to-the-point information. Highlight the most important updates at the beginning of the digest to capture the user's attention quickly. A well-structured and concise digest ensures that users can easily grasp the key points without feeling overwhelmed by too much information. To enhance user experience and ensure that your communications remain concise, [limit the items](/docs/batch#retain-batch-events) in your list to either the last 'n' items or the first 'n' items based on your specific use case. This approach prevents users from having to sift through overly lengthy emails and allows them to focus on the most relevant and recent information. By curating the content in this manner, you can deliver a more streamlined and efficient notification system that respects the user's time and attention. # Provide Easy Unsubscribe Options Make it simple for users to unsubscribe from notifications or digests they no longer find useful. An easy and straightforward unsubscribe process helps maintain a positive user experience. Users should not feel trapped by notifications; instead, they should have the freedom to opt-out whenever they choose. Providing this option demonstrates respect for user preferences and contributes to a more user-friendly system. # Test and Iterate Regularly testing your batching and digest strategy is vital to ensure it meets user needs and preferences. Collect feedback from users and analyse how they interact with notifications. Use this data to make informed adjustments. Iterative testing helps in fine-tuning your approach, ensuring that your notification system remains effective and aligned with user expectations. This continuous improvement process is essential for maintaining a high level of user satisfaction. In conclusion, building an effective notification system using batching and digest techniques requires careful planning and consideration of user preferences. By following these best practices, you can create a notification strategy that enhances user experience, maintains engagement, and ensures the timely delivery of important updates. *** # Notification System Design Source: https://docs.suprsend.com/docs/best-practices-notification-system-design Best Practices on designing your backend architecture for seamless integration with SuprSend. ### Integrating SuprSend in backend code * **Securely store your API Keys**: Store your API Keys, workspace key, and secret as environment variables rather than in source code to prevent unauthorized access. Refer the [best practices of API Key management here](/docs/best-practices-for-api-keys-management). * **Use** [Idempotency Key](/docs/go-trigger-workflow-from-api#idempotent-requests) **to avoid duplicate requests**: Always include an idempotency key in your requests so that request retries doesn't result in duplicate trigger or duplicate notification being sent to users. * **Use** [bulk API triggers](/docs/python-trigger-workflow-from-api#bulk-api-for-triggering-multiple-workflows) wherever applicable to improve performance and reduce request processing time. * **Implement Proper Error Handling** * Design robust error handling to manage API responses, including timeouts, server errors, and invalid responses. * Log errors using [webhook](/docs/outbound-webhook) and monitor them to quickly identify and resolve issues. * **Keep Dependencies Updated**: Regularly update your SDK or API libraries to take advantage of new features and security patches. Monitor for updates in the API or SDK and adapt your implementation as needed. ### Setting up notifications on SuprSend dashboard * **Keep development and production workspaces separate**: Avoid making direct changes in production workspace to safeguard from accidentally sending a test notification to your production users. Test all changes in staging before deploying them to production to ensure reliability. * **Use Test Mode for safe testing in staging workspace**: Enable [Test Mode](/docs/developer/test-mode) in your workspace to safely test notification flows without delivering to real users. In Test Mode, notifications to real users are blocked and delivery is allowed only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. * **Design one workflow per event or trigger**: Configure entire notification journey corresponding to an event or trigger in a single workflow. It simplifies notification management and helps you to edit and track notifications in a single glance. * **Be cautious of the number of alerts you send per user**. * Add [throttle](/docs/throttle) in your workflows to limit the number of alerts you send per user. * Consider [batching](/docs/batch) notifications to summarize frequent alerts to reduce the volume of notifications sent and prevent notification fatigue. * **Smartly route notifications across multiple channels to avoid bombarding**: Avoid sending notifications across multiple channels simultaneously. While multi-channel targeting can boost engagement, excessive notifications on all channels may lead to user unsubscriptions over time. Use [smart channel routing](/docs/smart-delivery) to send notifications sequentially if users do not engage, and consider integrating non-intrusive channels like [Inbox](/docs/inbox-overview) as a secondary channel for not so critical updates. * **Provide** [granular opt-out option](/docs/user-preferences) **to users to reduce channel unsubscription**: It is recommended to give users option to opt-out when sending promotional notifications. Most countries have data privacy and regulation acts, like GDPR (General Data Protection Regulation) in Europe and CCPA (California Consumer Privacy Act) in USA, which mandates taking user’s consent to send them notifications. While users can opt out of channels, offering more granular control to set notification preferences at category level can significantly reduce channel-level unsubscriptions, potentially decreasing them by around 50%. * **Implement Secure Authentication of SuprSend account**: Use secure [authentication methods](/docs/authentication-methods) such as SSO and Multi-Factor Authentication (MFA) for SuprSend account login. Assign appropriate roles to your team members to prevent data leakage or unintended changes in production notification. *** # Bigquery Source: https://docs.suprsend.com/docs/bigquery Guide to set up BigQuery database connection to auto sync subscriber lists. With this integration, you can directly send notifications on your 360 degree data sitting in your data warehouse. This enables your data teams or product managers to automate user syncing, setup recurring user cohort sync and create [subscriber lists](/docs/lists) on SuprSend. You can then trigger notification to this list using our [broadcast](/docs/broadcast) API. ## Getting started To start syncing data from your BigQuery database, you need to add this integration on the connector page. 1. Go to [SuprSend dashboard -> Settings -> Connectors](https://app.suprsend.com/en/staging/connectors) page. Here, you'll see the list of available connectors. If you have already setup any connectors in the past, you'll see a list of existing connectors. Click on **`+New Connector`** button to add the connector. 2. Click on BigQuery and add below information: * **Name**: A name to uniquely qualify the connection as you'll see it in the connector list on your sync task. You can add the name of database here for easy identification. * **Service account key json**: Steps to generate this are mentioned in the next section. ### Step-1: Generating OAuth client key and secret Go to the [Credentials](https://console.cloud.google.com/apis/credentials) page in the Google Cloud Platform console and click on **"+Create Credentials"**. Select OAuth client ID from the options list. ![](https://files.readme.io/cc6d2c3-Screenshot_2022-11-23_at_6.15.28_PM.png) If you have not created OAuth consent screen before, you will need to create one. ![](https://files.readme.io/ca30516-Screenshot_2022-11-23_at_6.17.34_PM.png) Since SuprSend will be used for your internal employees only, Use `Internal` option from the list ![](https://files.readme.io/719d9d2-Screenshot_2022-11-23_at_6.18.48_PM.png) Enter required details like App name, your email, developer contact information, authorized domain etc. ![](https://files.readme.io/fd1f40b-Screenshot_2022-11-23_at_6.28.58_PM.png) ### Step-2: Granting permissions SuprSend requires you to grant certain user permissions on your BigQuery warehouse to successfully access data from it. Perform the below three steps in the exact order to grant these permissions: 1.1. Go to the [Roles](https://console.cloud.google.com/iam-admin/roles) section of Google Cloud platform dashboard and click on **CREATE ROLE**. ![](https://files.readme.io/1593c56-image.png)1.2. Fill in the details as shown ![](https://files.readme.io/f4c596c-Bifrost_role.jpg)1.3. Click on **ADD PERMISSIONS** and add the following permissions > bigquery.datasets.get > bigquery.jobs.create > bigquery.jobs.list > bigquery.tables.get > bigquery.tables.getData > bigquery.tables.list 1.4. Finally, click on **CREATE**. 2.1. Go to [Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) and select the project which has the dataset or the table that you want to use and Click on CREATE SERVICE ACCOUNT ![](https://files.readme.io/5fd5659-image.png)2.2. Fill in the Service Account details as shown below, and click on CREATE AND CONTINUE: ![](https://files.readme.io/f032300-Screenshot_2022-11-29_at_11.11.49_AM.png)2.3. Fill in the Role details as shown below, and click on CONTINUE: ![](https://files.readme.io/14c0317-Screenshot_2022-11-29_at_11.13.18_AM.png)2.4. Click on DONE to move to the list of service accounts. 3.1. Click on the three dots icon under Actions in the service account that you just created and select **Manage keys**, as shown: ![](https://files.readme.io/772b9a7-Screenshot_2022-11-29_at_11.16.39_AM.png)3.2. Click on **ADD KEY**, followed by Create new key, as shown: ![](https://files.readme.io/84ae3e5-Screenshot_2022-11-29_at_11.18.37_AM.png)3.3. Select **JSON** and click on **CREATE** ![](https://files.readme.io/19d6cdb-Screenshot_2022-11-29_at_11.19.04_AM.png)3.4. A JSON file will be downloaded on your system. This file is required in your database integration form. ## Best Practices Before you setup your database sync, you should take some measures to ensure the security of your customers’ data and limit performance impacts to your backend database. The following “best practice” suggestions can help you limit the potential for data exposure and minimize performance impacts: * **Sync your read-only replica instance:** Do not sync data directly from your main instance. Instead use a read-only data replica to minimize the load and avoid data loss on your main database. * **User connected here should have minimal privileges:** You should have a database user with minimal privileges. This person only requires read permissions with access limited to the tables you want to sync from. * **Sync only the data that you’ll need:** Limiting your query can improve performance, and minimize the potential to expose sensitive data. Select only the columns you need to either update user profile in SuprSend and to create list sync. * **"Use `{{last_sync_time}}` to limit query results:** Make sure you use the **`{{last_sync_time}}`** variable in your recurring sync queries. It stores the timestamp of last successful sync in your list. Adding it in your where statement against datetime index can really speed up the query and limit the number of results returned in consecutive syncs. `{{last_sync_time}}` is stored in timestamp format. Use relevant cast expression to format it based on your column type. * **Limit your sync frequency:** Setup a sync frequency based on how frequently you want to send notifications on that list. If the previous sync is still in progress when the next interval occurs, we’ll skip the operation and catch up your data on the next interval. Skipped syncs show`Ignored`status in the logs. Frequently skipped operations may indicate that you’re syncing too often. You should monitor your first few syncs to ensure that you haven’t impacted your system’s performance. *** # Branch Source: https://docs.suprsend.com/docs/branch Use branch to route notifications across different paths by applying condition on input data. Branch is an `if/elseif/else` step used to route notification through different paths in a workflow based on conditions. Some common use cases could be to send multi-step reminder to users until payment is made and route notification on different paths based on user properties. You can add up to 10 branches in a workflow. The First branch satisfying the condition in the order will be executed. ## How branch works? There are 2 types of branches: Condition branch, Default branch 1. **Condition branch**: User proceeds through this branch when a condition is met. The condition can be applied on recipient, actor, tenant properties and node input data. You can add up to 9 condition branches. If 2 branches satisfies the condition, the first branch in the order will be executed. 2. **Default branch**: This is the fallback branch which is executed if no condition branch results in truthy value. ## Constructing a condition You can add conditions on data payload passed in the trigger, user properties and tenant properties. A condition is a combination of 3 parts: Data type, Key, operator and Value. ### Data type You can apply conditions to all data available at the node's input. For instance, if a condition branch follows a batch, fetch, or webhook node, it can utilize data that has been modified or added by those nodes. | Data Type | Description | Referring this property in condition | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | | Input Payload | This includes the data from your trigger payload and any data modified or added by nodes such as data transform, batch/digest, or webhook/fetch nodes before the branch node. | Specify key if you are adding this variable in key or value field. | | Actor | Actor properties. In case of event trigger, `distinct_id` works both as actor and recipient and for inline workflow trigger, it is the `distinct_id` in actor object. | Directly specify key if you are adding this in key or add as `$actor.` if you add it in value field. | | Recipient | Recipient properties. It is the `distinct_id` in your event trigger or the key value defined in [override recipient](/docs/override-recipient-list) field. For inline workflow trigger, it is the `distinct_id` in recipient object. | Directly specify key if you are adding this in key or add as `$recipient.` if you add it in value field. | | Tenant | Tenant properties. It is all the properties of the tenant\_id in your workflow trigger. | Directly specify key if you are adding this in key or add as`$brand.` if you add it in value field. | ### Key-Value pair 1. **Key**: It is the variable key in your data input to apply condition on. 2. **Operator**: you can use any of the below operators to compare key and value: | Operator | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `==` | key **is equal to** value. This is a case sensitive check. | | `!=` | key **is not equal** to value. This is a case sensitive check. | | `> ` | key **is greater than** value. can be applied to integers, float values or epoch timestamps | | `> =` | key **is greater than or equals to** value. can be applied to integers, float values or epoch timestamps | | `<` | key **is less than** value. can be applied to integers and float values | | `< =` | key **is less than or equals to** value. can be applied to integers and float values | | `contains` | key should be a substring or list item of an array. | | `not contains` | key should not be a substring or match any list item of an array. | | `is empty` | evaluates to true if the key is missing, is an empty string or has `null` value. | | `is not empty` | key should be present and should not be an empty string or `null` value | | `datetime is` | datetime comparator. key **is equal to** value. Both values are converted into timestamp for comparison. Use `"now"` in value for current timestamp (evaluated at time of node evaluation) and add interval as `"now+1d"`(y-year, M-month, w-week, d-day, h-hour, m-minute, s-second) | | `datetime is before` | datetime comparator. key **is less than** value. Both values are converted into timestamp for comparison. Use `"now"` in value for current timestamp (evaluated at time of node evaluation) and add interval as `"now+1d"`(y-year, M-month, w-week, d-day, h-hour, m-minute, s-second) | | `datetime is after ` | datetime comparator. key **is greater than** value. Both values are converted into timestamp for comparison. Use `"now"` in value for current timestamp (evaluated at time of node evaluation) and add interval as `"now+1d"`(y-year, M-month, w-week, d-day, h-hour, m-minute, s-second) | | `intersects` | Evaluates to true if **any value** in the left array matches **any value** in the right array. Useful for checking overlaps between arrays. | | `not intersects` | Evaluates to true if **no values** in the left array match any values in the right array. It could be used in case of checking or filtering out any overlaps between arrays. | 3. **Value**: Value can be [**fixed**](/docs/branch#fixed-values) or [**dynamic**](/docs/branch#dynamic-values) (evaluated based on workflow input payload). Dynamic values are helpful when the comparison value is dynamic for each user. e.g., in case of payment reminder, the payment due date could vary for each user and is part of your trigger data. You can add condition in this case as`current_date >= payment_due_date`(where both timestamps are in epoch). #### Fixed values Fixed values can be added as: * **string**: enclose within double inverted commas as`"string"` * **number**:`1`,`1.2` * **boolean**:`true`,`false` * **datetime**: "2024-01-01T00:00:00Z", "now" (current timestamp), "now+1d", "now-1d" (current timestamp +/- interval). You can add only 1 of these interval units in the expression (y-year, M-month, w-week, d-day, h-hour, m-minute, s-second). #### Dynamic values You can add input payload key directly as `key` with no prefix. All other properties should start with their respective `$` prefix. You can refer to all the data types available in workflow [in the above section](/docs/branch#data-type). ## Adding Multiple Conditions in a single branch You can combine multiple conditions in a branch with `AND`, `OR` operator. * `AND`- all conditions separated by`AND`should be true for the evaluation to pass. * `OR`- Evaluation will pass if any of the conditions separated by`OR`is true. If you have a condition (a or b) and c, you can construct it as (a and b) or (b and c). *** # Broadcast Source: https://docs.suprsend.com/docs/broadcast How to trigger broadcast to a list of users. With broadcast, you can send notifications to a large list of users with high throughput and low latency. You can use this to schedule campaigns or send important announcements that is relevant for a large group of users. Best used for cases when you want your messages to be delivered instantly like stock market alerts. ## Pre-requisites [Create a list of users](/docs/lists)- List is a group of users who you want to send broadcast to. ## Triggering Broadcast Once you have the list ready, you can trigger broadcast using one of the following methods: ### 1. Programmatically using SDK / API This is the most flexible way of sending broadcast to a list. Use it when you regularly trigger a specific message to bulk users, like reminders or community updates like news. You can use API or update it using backend SDK ### 2. From UI You can also trigger Broadcast directly from the SuprSend dashboard without any tech involvement. Recommended for cases when you want to send a one-off campaign, something like newsletter or special feature announcement. You can schedule broadcast on a list from the dashboard by clicking on `Run Broadcast` option in the kebab menu on `Subscriber -> Lists` tab This will open the broadcast form. Fill in the relevant information and click on | Field | Obligation | Description | | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | List ID | *Mandatory* | `id` of the list on which broadcast should be sent. | | Template Name | *Mandatory* | Select the template content to be sent. | | Notification category | *Mandatory* | [Notification category](/docs/notification-category) will be used to pick up the vendor credential and also to power [preferences](/docs/user-preferences). If you don't have user preferences setup in your product, you can define the category as `promotional`. Do not use `transactional` or `system` category to run broadcast as it can delay your other transactional or system messages. | | Selected Channels | *Optional* | Add Channels here if you want to send broadcast on particular channel in the template group. | | Variable data | *Optional* | Add variables JSON here if you have variables in your template. | | When | *Optional* | -**Immediately** will trigger the broadcast instantly on the click of the button -Choose **After Delay** or **Scheduled time** if you want to schedule broadcast for later time. **After delay** is relative time difference from the instant of button click and **Scheduled time** is for defining absolute datetime of trigger | Once, you click on button, it will ask for a confirmation. This is just to ensure that a bulk campaigns is not triggered by mistake. Click on to trigger the broadcast. You can check the notification status on Logs page. *** # Broadcast Source: https://docs.suprsend.com/docs/broadcast-go Trigger broadcast notifications to a list of users with Go SDK. ## Pre-Requisites [Create a list of users](/docs/lists-go) ## Payload Schema ```go Request theme={"system"} package main import ( "context" "log" suprsend "github.com/suprsend/suprsend-go" ) func main() { // Initialize SDK opts := []suprsend.ClientOption{ // suprsend.WithDebug(true), } suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_", opts...) if err != nil { log.Println(err) } ctx := context.Background() // ================= broadcast to a list broadcastIns := &suprsend.SubscriberListBroadcast{ Body: map[string]interface{}{ "list_id": "users-with-prepaid-vouchers-1", "template": "template slug", "notification_category": "category", // broadcast channels. // if empty: broadcast will be tried on all available channels // if present: broadcast will be tried on passed channels only "channels": []string{"email"}, "delay": "1m", // check docs for delay format // "trigger_at": "", // check below for trigger_at format "data": map[string]interface{}{ "first_name": "User", "spend_amount": "$10", "nested_key_example": map[string]interface{}{ "nested_key1": "some_value_1", "nested_key2": map[string]interface{}{ "nested_key3": "some_value_3", }, }, }, }, IdempotencyKey: "", TenantId: "", } res, err := suprClient.SubscriberLists.Broadcast(ctx, broadcastIns) if err != nil { log.Fatalln(err) } log.Println(res) } ``` ```go Sample theme={"system"} package main import ( "fmt" "log" suprsend "github.com/suprsend/suprsend-go" ) func main() { // Initialize Suprsend client suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Fatalln(err) } // Create broadcast body broadcastBody := map[string]interface{}{ "list_id": "application_abandoned", "template": "complete_application", "notification_category": "promotional", "data": map[string]interface{}{ "page_no": "2", }, } // Create SubscriberListBroadcast instance inst := suprsend.NewSubscriberListBroadcast(broadcastBody) // Call broadcast API resp, err := suprClient.SubscriberLists.Broadcast(inst) if err != nil { log.Fatalln(err) } fmt.Println(resp) } ``` ```go Response theme={"system"} { 'success': True, 'status': 'success', 'status_code': 202, 'message': 'OK' } ``` Broadcast body field description: | Parameter | Format | Description | | ---------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | list\_id | string | list of users that you want to send broadcast messages. | | template | string | It is the template slug which can be found in Templates tab in SuprSend dashboard. | | notification\_category | system / transactional / promotional | You can understand more about them in the [Notification Category](/docs/notification-category). | | channels (Optional) | string\[] | User channels on which the broadcast messages to be sent. If not provided, it will trigger notifications on all available channels in user profile. Available channels: androidpush / iospush / inbox / email / whatsapp / sms Example:\["sms", "inbox"] | | delay (Optional) | **XX**d**XX**h**XX**m**XX**s or Number (in seconds) | Broadcast will be halted for the time mentioned in delay, and become active once the delay period is over. Example: 1d2h3m4s / 60 | | trigger\_at (Optional) | date string in ISO 8601 | Trigger broadcast on a specific date-time. Example: "2021-08-27T20:14:51.643Z" | | data (Optional) | object | variable data defined in templates | ## Add file attachment (for email) To add one or more attachments to a notification (viz. Email), call `add_attachment()` on broadcast instance for each attachment file. Ensure that attachment url is valid and public, otherwise error will be raised. Since broadcast instance size can't be > 100 KB, local file paths can't be passed in event attachment. ```go Request theme={"system"} // If need to add attachment err = broadcastIns.AddAttachment("https://attachment-url", &suprsend.AttachmentOption{IgnoreIfError: true}) if err != nil { log.Fatalln(err) } ``` A single broadcast instance size (including attachment) must not exceed 100KB (100 x 1024 bytes). *** # Broadcast Source: https://docs.suprsend.com/docs/broadcast-java Trigger broadcast notifications to a list of users with Java SDK. You can use this method to send instant notifications to a list of users. ## Pre-requisites * [Create a list of users](/docs/lists-java) ## Triggering Broadcast You can trigger broadcast using `suprClient.subscriberLists.broadcast()` method. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendAPIException; import suprsend.SubscriberListBroadcast; import suprsend.SuprsendValidationError; public class Lists { public static void main(String[] args) throws Exception { broadcast(); } private static Subscriber broadcast() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); // Create broadcast body String listId = "_list_id_"; String templateSlug = "_template_slug_"; String notifCategory = "_preference_category_"; //Optional Fields //String delay = "30"; //String triggerAt = "2023-03-06T18:56:51.643Z"; //ArrayList channels = new ArrayList<>(Arrays.asList("androidpush","email")); //String idempKey = "__unique_id_of_the_request__"; //String tenantId = "__tenant_id__"; JSONObject body = new JSONObject().put("list_id",listId) .put("template",templateSlug) .put("notification_category",notifCategory) .put("data", new JSONObject() .put("link_suffix", "https://s3.amazonaws.com/unroll-images-production/projects%2F22692%2F1630591176038-322170") .put("first_name", "Joe")) SubscriberListBroadcast broadcastIns = new SubscriberListBroadcast(body); // Broadcast with idempotency key and brand id // SubscriberListBroadcast broadcastIns = new SubscriberListBroadcast(body, idempKey, tenantId); JSONObject res = suprClient.subscriberLists.broadcast(broadcastIns); System.out.println(res); } ``` ```java Sample theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendAPIException; import suprsend.SubscriberListBroadcast; import suprsend.SuprsendValidationError; public class Lists { public static void main(String[] args) throws Exception { broadcast(); } private static Subscriber broadcast() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); String listId = "_product_updates_"; String templateSlug = "Newsletter"; String notifCategory = "promotional"; JSONObject body = new JSONObject().put("list_id",listId) .put("template",templateSlug) .put("notification_category",notifCategory) .put("data", new JSONObject() .put("link_suffix", "https://s3.amazonaws.com/unroll-images-production/projects%2F22692%2F1630591176038-322170") .put("first_name", "Joe")) SubscriberListBroadcast broadcastIns = new SubscriberListBroadcast(body); JSONObject res = suprClient.subscriberLists.broadcast(broadcastIns); System.out.println(res); } ``` ```java Response theme={"system"} { 'success': True, 'status': 'success', 'status_code': 202, 'message': 'OK' } ``` Broadcast body field description: | Parameter | Description | | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | list\_id | list of users that you want to send broadcast messages to. | | template | Add template slug here. You can get this slug by clicking on the clipboard icon next to the Template name on SuprSend templates page. It is the same for all channels. | | notification\_category | Preference Category to apply user preference settings while sending. Root categories - system / transactional / promotional | | data | variable data defined in templates or workflow. | | channels | Specify channels if you don't want to send notification of all live channels in the template. Available channel keys - email, sms, whatsapp, androidpush, iospush, ms\_teams, slack, webpush | | delay | Broadcast will be halted for the time mentioned in delay, and become active once the delay period is over. | | trigger\_at | Trigger broadcast on a specific date-time. Pass in ISO 8601 timestamp (e.g. "2021-08-27T20:14:51.643Z") | | tenant\_id | id of the custom tenant to send broadcast for a specific [tenant](/docs/tenants), used for applying tenant level customizations in notifications. | | idempotency\_key | unique identifier of the request. We'll be returning idempotency\_key in our outbound webhook response. You can use it to map notification statuses and replies in your system. | *** # Authentication Source: https://docs.suprsend.com/docs/client-authentication How to authenticate SuprSend Client SDKs using public API Keys & signed user tokens. 📘 Many of our mobile SDK's are under revamp stage. These SDK's still use workspace key and workspace secret authentication. SuprSend client SDK's use public API Keys to authenticate requests. You can find Public Keys in *SuprSend Dashboard -> Developers -> API Keys -> Public Keys*. You can generate new ones and delete or rotate existing keys. For production workspaces public API Keys alone isn't enough as they are insecure. To solve this enable enhanced secure mode switch which you can find beside Public Key (shown in above image). This mandates signed user token (a JWT token that identifies the user that is performing the request) to be sent along with client requests. ## Enhanced Security Mode with signed User Token When enhanced security mode is on, user level authentication is performed for all requests. This is recommended for Production workspaces. All requests will be rejected by SuprSend if enhanced security mode is on and signed user token is not provided. This signed user token should be generated by your backend application and should be passed to your client. You can generate Signing key from SuprSend Dashboard (below Public Keys section in API Keys page). Once signing key is generated it won't be shown again, so copy and store it securely. It contains 2 formats: (i.) **Base64 format:** This is single line text, suitable for storing as an environment variable. (ii.) **PEM format:** This is multiline text format string. You can use any of the above format. This key will be used as secret to generate JWT token as shown in below step. This should be created on your backend application only. You will need to sign the JWT token with the signing key from above step and expose this JWT token to your Frontend application. * **JWT Algorithm:**ES256 * **JWT Secret:**Signing key in PEM format generated in step1. If you are using Base64 format, it should be converted in to PEM format. * **JWT Payload:** ```json Payload theme={"system"} { "entity_type": 'subscriber', // hardcode this value to subscriber "entity_id": your_distinct_id, // replace this with your actual distinct id "exp": 1725814228, // token expiry timestamp in seconds "iat": 1725814228 // token issued timestamp in seconds. "scope": { "tenant_id": "string" } } ``` SuprSend requests will be scoped to tenant. If tenant passed by you in SDK doesn't match with the JWT payload scope `tenant_id` then requests will throw `403` error. If `tenant_id` is not passed, it is assumed to be `default` tenant. Currently only Inbox requests supports scope, later on we will extend it to preferences and other requests. Create JWT token using above information: ```javascript Node theme={"system"} import jwt from 'jsonwebtoken'; const payload = { entity_type:'subscriber', entity_id:"johndoe", exp:1725814228 }; const secret = 'your PEM format signing key'; // if base64 signing key format is used use below code to convert to PEM format. const secret = Buffer.from('your_base64_signingKey', 'base64').toString('utf-8') const signedUserToken = jwt.sign(payload, secret,{ algorithm: 'ES256' }) ``` After creating user token on backend send it to your Frontend application to be used in SuprSend SDK as user token. ```javascript Javascript theme={"system"} import SuprSend from '@suprsend/web-sdk'; const suprSendClient = new SuprSend(publicApiKey: string); const authResponse = await suprSendClient.identify(user.id, user.userToken); ``` ## Token expiry handling To handle cases of token expiry our client SDK's have **Refresh User Token callback** as parameter in identify method which gets called to get new user token when existing token is expired. ```javascript Javascript theme={"system"} const authResponse = await suprSendClient.identify(user.id, user.userToken, { refreshUserToken: (oldUserToken, tokenPayload) => { //.... write your logic to get new token by making API call to your server... // return new token }}); ``` *** # Overview Source: https://docs.suprsend.com/docs/connectors Learn how to connect third-party platforms and data sources on SuprSend for data sync and workflow automation. With SuprSend Connectors, you can connect data from your third-party platforms with SuprSend. This will help you to create user list, sync users, events and trigger workflow directly from these data platforms. ### Benefits of integrating third-party platforms 1. **Connect your customer data within mins:** If you already track customer data in these platforms, you can directly sync user data from these platforms within mins. 2. **Testing the platform becomes easy:** Once your data is synced, you can quickly create a template on our platform and test it out. 3. **Engineering bandwidth saved:** Once your user and event data is synced, you can directly build and trigger workflows from SuprSend dashboard. It saves engineering bandwidth and also give control to product and growth teams to directly power notification from third-party platforms. There are 3 types of connectors in SuprSend: (i) Third party data source, (ii) Third party data destination and (iii) Database. 1. **Third party data source:** Used to connect event and user data from a third-party platform to power notifications or sync user cohorts (lists) in SuprSend. 2. **Third party data destination:** Used to sync data like message templates and notification metrics that we track in SuprSend back to your data warehouse for reporting and analysis. 3. **Database:** Database connector enables you to setup notifications directly on top of the data from your data warehouse. This way data, product and growth teams can directly setup production notifications and automated marketing campaigns on the 360 degree data without doing any trade-offs on the amount of data available in click stream platforms. Using database connector, you can directly sync user profiles and create lists in SuprSend with a simple SQL query, without any engineering effort. You can then use these lists to send broadcasts. Need support for another connector? [Please Let us know](https://suprsendcommunity.slack.com/ssb/redirect) ### Add Connectors based on workspaces Most of these platforms have different project for staging and for production. Since, each workspace in SuprSend has separate connector settings, you can connect your connector staging project with SuprSend staging workspace. Once you are done testing, connect your production project with SuprSend production workspace. # Data Transform Source: https://docs.suprsend.com/docs/data-transform Guide to help you transform input data and generate new variables in workflow. The Data Transform node is used to dynamically generate or modify variables within your workflow based on specific conditions or the execution of other steps. This allows you to create variables at one place that can be utilized across multiple templates and in workflow settings, without having to write the same transformation in each template or workflow settings. e.g., if you have to batch your workflow for some users and not for others—you can use a single template and adjust the variable values with the Data Transform node. Similarly, if you're offering different Thanksgiving discount to users based on their past activity or billing, you can calculate the appropriate discount slab in the workflow and apply it to your templates. ## How data transform works The Data Transform node generates or modifies variables in your trigger data, which are then merged into the main payload used by the workflow and templates to render dynamic content. You can write these transformations using [Handlebars](/docs/handlebars-helpers) or [JSONNET](https://jsonnet.org/ref/language.html) language. We use a shallow merge strategy where keys with the same name as the input payload will be overridden. Example: Given the following trigger data for two users: ```json json theme={"system"} // User 1 { "time_period": "4 years", "monthly_bill_amount($)":"1500", "discount":"10%" } // User 2 { "time_period": "1 year", "monthly_bill_amount($)":"500", "discount":"10%" } ``` If you want to apply below transformation to give a 15% discount to users whose monthly bill amount exceeds \$1000 The transformation would modify the discount for User 1, resulting in ```json json theme={"system"} // User 1 { "time_period": "4 years", "monthly_bill_amount($)":"1500", "discount":"15%" // Updated by Data Transform node } // User 2 { "time_period": "1 year", "monthly_bill_amount($)":"500", "discount":"10%" } ``` ## Modifying list of variables in data transform You can generate up to 25 variables in this step. Each key-value pair represents a variable and its value. You can choose to write transformation in [Handlebars](/docs/handlebars-helpers) or [JSONNET](https://jsonnet.org/ref/language.html) language. Handlebar is a simpler language suitable for simple string outputs, while JSONNET can handle more complex data structures such as arrays or JSON objects. ## Some common notification use cases * **Use `$batched_variables` or Handlebar helpers in SMS and WhatsApp templates** where approval is required. Since these templates are pre-approved, the type of variable content can't be changed dynamically for these templates, limiting the use of handlebars helpers in these templates. You can use data transform node to write transformation outside template editor and pass the generated variable in template. * Generating variables for use in workflow settings. e.g., dynamically manage user preferences for notifications within a workflow. For instance, if users have different digest preference on channels and want to receive digest notifications on some channels and immediate alerts on others, you can create an array of channels for each type of notification. This array can then be used in the override channel settings of the multi-channel delivery node to handle both digest and immediate alerts appropriately. * **Fetch Response Modification**: Adjust the structure of responses from fetch operations as needed. * Apply batching conditions where some users receive batched notifications and others get immediate alerts. Use the Data Transform node to ensure a consistent data structure in both cases for use in templates. *** # Overview Source: https://docs.suprsend.com/docs/database Learn about database connector and how you can use it to auto sync list users by writing SQL query on your database. Database connector enables you to setup notifications directly on top of your true source of data from your data warehouse. This way data, product and growth teams can directly setup production notifications and automated marketing campaigns on the 360 degree data without doing any trade-offs on the amount of data available in click stream platforms. Using database connector, you can directly sync user profiles and create lists in SuprSend with a simple SQL query, without any engineering effort. You can then use these lists to send broadcasts. ## How it works? The process of syncing data into SuprSend is a ETL process wherein: We currently support [Postgres](/docs/postgres), [MySQL](/docs/mysql) and [BigQuery](/docs/bigquery). We'll be adding more connections soon. The data extracted from your database will always be synced into a list of users. List is a cohort of users whom you would want to send notification. Once your database connection is setup, you can write SQL query to extract the data required for list and user profile sync. This will first create/update user profile and then load it into the list. Make sure that you optimize the query so that it only returns the rows and columns that you need. You can also user [`{{last_sync_time}}`](/docs/database#how-it-works) in case of recurring sync to only return the data that changed after your last successful sync. [Know more about setting up your sync task here](/docs/list-sync-via-database#step-1-create-a-sync-task) Once you have written and tested your SQL query, you can save it and **Commit** setup list sync. It can be a one time list sync for sending one time campaigns like announcements, reminder to users who registered for an event, or a recurring sync for sending crons or regular scheduled notifications like send card abandonment notification to users who left items in their in the last 7 days, onboarding sequences, payment reminders etc. You can also update user profile with list sync. This is helpful for cases where you want to add some variables in your notification template. e.g., in case of event reminder, you can sync event details in user profile. [Learn more about sync settings here](/docs/list-sync-via-database#step-3-save-query-and-commit-to-setup-sync-frequency). *** # Delay Source: https://docs.suprsend.com/docs/delay Learn about delay node in workflow and how to use it to add wait between workflow steps. Delay node halts the workflow for a given time period before moving to the next step. It is best used in case of reminders and re-engagement notifications where you want to bring back user to the product after a period of their last action. There are 3 types of delay types available: **Fixed** (fixed for all users), **Dynamic** (Passed in trigger payload), Relative (relative to a future timestamp, e.g. 10 minutes before task due time). Fixed delay is defined in your workflow form as `*\*d \*\*h \*\*m \*\*s` and it delays the workflow for a fixed duration for all users. Some examples of fixed delay are: * Sending multiple payment or activity reminders at predetermined intervals. For instance, sending three payment reminders spaced 24 hours apart from the last due date. * Implementing conditional sends across multiple channels. e.g., sending an approval notification via Inbox and scheduling an email to be sent one hour later if the approval is not received. [Smart channel routing](/docs/smart-delivery) is a better approach to solve this use case. In case of dynamic delay, delay duration is computed using the data from trigger payload. Dynamic delays are helpful for reminders where the schedule is dictated by the user or when reminders need to be sent before the event or task due date. For instance, you might want to send a reminder one day before an interview date. In such scenarios, the reminder schedule can also be user-defined, resulting in variable delays per user. Consider the example of Google Calendar, where each user sets their own reminder schedule for meetings—some opt for reminders 10 minutes before, while others prefer 30 minutes before. Dynamic delays accommodate these individual preferences seamlessly. You can add duration key as a [JQ-expression](https://jqlang.github.io/jq/manual/). Below are some examples of how to add duration key in JQ format: * General format for duration key at parent level is `.duration_key` * If the duration key is a nested event property key like shown below, enter it in the format `.appointment_details.time` ```json Trigger Payload theme={"system"} properties = { "appointment_details": { "time": "2024-03-02T20:34:07Z", "location": "1775 Stanford Ave, Menlo Park, CA 94025" } ``` Your duration key variable can be computed to either: * An ISO-8601 timestamp (e.g. 2024-03-02T20:34:07Z) which must be a datetime in the future, or * A relative duration unit, which can be * an integer like `50`, considered as duration in seconds. * an interval string defined as `xxdxxhxxmxxs`, where d = day, h = hour, m = minutes and s = seconds When the duration key specified is missing, or resolves to an invalid value, workflow execution will stop and corresponding error will be logged in the logs Relative delay is calculated based on a future timestamp. e.g., sending a reminder 30 minutes before a task's due time, where `task_due_time` is a key in the trigger payload. It consists of three key components: * **Interval**: The delay from the future timestamp, formatted as xxdxxhxxmxxs (e.g. 30m for 30 minutes). This can be: * **Fixed** (e.g. always 30 minutes before). * **Dynamic**, where the value is retrieved from the payload (e.g. in Google Calendar, users can set reminders for 10 or 20 minutes before an event). * **Before/After**: Determines whether the interval is subtracted (before) or added (after) to the timestamp. * **Timestamp**: An ISO-8601 format datetime (e.g. 2024-03-02T20:34:07Z), which must be in the future. Dynamic Interval & Timestamp must be passed as a . Examples: * Timestamp at the parent level: `.timestamp` * If the dynamic interval is set as recipient property: `."$recipient".interval` *** # Delivery- Multi-Channel Source: https://docs.suprsend.com/docs/delivery-multi-channel How to send notification across multiple channels in a single workflow step. You can use multi-channel delivery node to notify users on multiple channels at once. If you want to send multi-channel notifications, we recommend using [smart channel routing](/docs/smart-delivery) to ensure that notifications are delivered sequentially on multiple channels rather than bombarding users on all channels at once. The content of the notification is designed with [templates](/docs/templates). In SuprSend, you can design the content of multiple channels within a single template group. ## How the delivery node is executed? Delivery node is successfully executed if all of the below checks hold true: 1. The channel should be published and live in the template. For WhatsApp and SMS (Indian vendors), templates become live upon approval by the respective provider. 2. Vendor Configuration is available for the channel. For all out-of-app channels, you need to create an account with the respective [channel provider](/docs/vendors) and add the configuration in the vendor form on SuprSend dashboard. Inbox is an internal offering by SuprSend and doesn't need any third party integration. [Refer Integration guide](/docs/inbox-overview) to setup Inbox Channel. 3. Channel information should be available in user profile and channel status should be active. Template channels not available in user profile are skipped for delivery. A set channel becomes inactive in case the channel is`removed `or `unset`using [SDK or API](/docs/users#via-sdk) or it is marked inactive by SuprSend. 4. User preference is`opt-in `for the given channel and notification category (defined in workflow settings). You can check user preference status using [get user preference API](/reference/get-user-category-preferences). ### Inactive channel marking by SuprSend SuprSend marks the user channel identity inactive for email and WhatsApp in case of hard errors from vendor end, such as bounced email addresses, unregistered WhatsApp numbers. This is done to safeguard your email domain authority or WhatsApp rating if you continue to send notifications to users who have reported or marked your email or messages as spam. Additionally, this helps in WhatsApp cost saving, as vendor charges for every processed request. Inactive marking period by SuprSend is 15 days for WhatsApp and 90 days for email. ## Selecting channels for multi-channel delivery By default, notification is sent on all active channels of the template. You can however choose to send notification on selected channels by manually choosing selected channels in the form or [override channels](/docs/delivery-multi-channel#override-channels) dynamically using data in your event property. ### Override Channels You can use this field to pass channel list dynamically using data in your event property. This feature comes in handy when user channels are dynamically defined at the user level for each workflow. For instance, when booking an appointment, your users are dynamically defining their preferred channel to receive booking updates for each appointment. For more consistent channel preferences, like user wanting to receive all communication via email only, or defining preferred communication channel for a notifications category (like booking updates), we recommend updating it using [user preferences](/docs/user-preferences). To override channels, include the channels array in event property and add the corresponding key in the override channels field on SuprSend workflow form. The expected channel values are: `["email", "sms", "whatsapp", "androidpush", "iospush", "webpush", "slack", "inbox", "ms_teams"]` You can add channel array as a [JQ-expression](https://jqlang.github.io/jq/manual/). So, in case your channel values do not match with the one mentioned in the above table, you can transform it using the JQ-expression. Below are some examples of how to add duration key in JQ format: 1. General format for duration key at parent level is`.channels` 2. If channel is a nested event property key like shown below, enter it in the format`.user.channels`. ```json Trigger Payload theme={"system"} properties = { "user": { "name": "Steve", "channels": ["email","inbox] } ``` * If both selected channels and override channels key are set in the form, system will prioritize the override channels, even if that channel is not included in the selected channel list. * When the override channel variable in the event data is missing, or resolves to an invalid value, workflow execution will stop and corresponding error will be logged in the logs ## Success Metric Success Metric can be any event which defines the target user activity you aim to drive with your sent notification. e.g., if the objective of your notification is to prompt users to open it, such as in the case of newsletters, you can set your success metric as `Notification Status - Seen`. If your goal is for users to perform any custom event, like complete payment in case of payment reminder notification, then you can set that event as your success metric. In the context of multi-channel delivery, the success metric is utilized solely to track conversion numbers for display in workflow analytics. However, in the case of [smart channel routing](/docs/smart-delivery), the same success metric serves to halt delivery on further channels once the success metric is achieved. *** # Delivery- Single Channel Source: https://docs.suprsend.com/docs/delivery-single-channel Learn how to use delivery nodes like email, sms, whatsapp, mobile push, web push, slack, ms teams, inbox in workflows. You can use single-channel delivery nodes - Email, SMS, Whatsapp, Inbox, Slack, MS Teams, MobilePush and Webpush to send notification on a particular user channel. The content of the notification is designed with [templates](/docs/templates). ## How the delivery node is executed? Delivery node is successfully executed if all of the below checks hold true: 1. The channel must be published and live within the template. For WhatsApp and SMS (Indian vendors), templates become live upon approval by the respective provider. 2. Vendor Configuration is available for the channel. For all out-of-app channels, you need to create an account with the respective [channel provider](/docs/vendors) and add the configuration in the vendor form on SuprSend dashboard. Inbox is an internal offering by SuprSend and doesn't need any third party integration. [Refer Integration guide](/docs/inbox-overview) to setup Inbox Channel. 3. Channel information should be available in user profile and channel status should be active. A set channel becomes inactive in case the channel is `removed`or `unset`using [SDK or API](/docs/users#via-sdk) or it is marked inactive by SuprSend. 4. User preference is`opt-in `for the given channel and notification category (defined in workflow settings). You can check user preference status using [get user preference API](/reference/get-user-category-preferences). ### Inactive channel marking by SuprSend SuprSend marks the user channel identity inactive for email and WhatsApp in case of hard errors from vendor end, such as bounced email addresses, unregistered WhatsApp numbers. This is done to safeguard your email domain authority or WhatsApp rating if you continue to send notifications to users who have reported or marked your email or messages as spam. Additionally, this helps in WhatsApp cost saving, as vendor charges for every processed request. Inactive marking period by SuprSend is 15 days for WhatsApp and 90 days for email. ## Success Metric Success Metric can be any event which defines the target user activity you aim to drive with your sent notification. e.g., if the objective of your notification is to prompt users to open it, such as in the case of newsletters, you can set your success metric as `Notification Status - Seen`. If your goal is for users to perform any custom event, like complete payment in case of payment reminder notification, then you can set that event as your success metric. In the context of single-channel delivery, the success metric is utilized solely to track conversion numbers for display in workflow analytics. However, in the case of [smart channel routing](/docs/smart-delivery), the same success metric serves to halt delivery on further channels once the success metric is achieved. *** # Design Workflow Source: https://docs.suprsend.com/docs/design-workflow Learn how to design, edit or publish workflow on SuprSend dashboard. ## Pre-Requisites [Understanding the basics of workflows](/docs/workflows) ## Creating a new workflow Click on button on the workflow page to create workflow from scratch or select a workflow from our sample library. * Pass workflow name and category. Choose a relevant name initially as it determines the related workflow slug, which cannot be modified later. * [Notification Category](https://docs.suprsend.com/docs/notification-category) is used to apply user preferences to the workflow. While creating the workflow, you can select any notification category and adjust it later if needed. * After entering the required details, click on `Create` button to create a new workflow in draft state. You'll see the created workflow on top of the listing page, click on it to start editing. * Next, add relevant nodes to your workflow and edit workflow settings. * Once you've finalized your edits, remember to make the workflow live. If you don't want to make your changes live right away, you can `exit edit mode` and come back later to commit the changes. Rest assured, your modifications will remain saved in the draft state until finalized. ![](https://files.readme.io/e79f3e2-ezgif.com-speed.gif) ## Designing workflow Workflows enable you to build complex notifications by defining whom to notify, when, and through which channels. A workflow requires a trigger node to initiate it and a delivery node to send the final notification. Additionally, you have functional nodes that adds logic, branch nodes to split execution based on conditions, and data update nodes to modify or add data and assets in SuprSend. We've explained and listed down the available workflow nodes below. ### 1. Trigger Node It is the first step of your workflow and contains information about what initiates the workflow, and related conditions. There are 3 types of workflow triggers possible: 1. **List entry / exit:** Starts the workflow when a user enters or exists a [list](https://docs.suprsend.com/docs/lists). 2. **Event Stream:** Starts the workflow when one of the linked events is sent to SuprSend. 3. **Workflow API:** Here, you explicitly call a workflow using its workflow-slug, specifying the workflow and recipients directly in the API request. Other than re-engagement notifications, most transactional workflows will either be triggered by passing an event or using workflow API. Compare event vs workflow API here. ### 2. Delivery Nodes These nodes are the final steps in the workflow, responsible for delivering notifications to users. Here's a list of available delivery nodes: Send notification on one of the channels (Email, SMS, Whatsapp, Inbox, Mobile Push, Web Push, Slack or MS Teams). Send notification across multiple channels at once. Send notification across multiple channels sequentially with a delay until user engages with one of the channels. HTTP API request to notify an endpoint such as your CRMs, chat platforms or internal systems. Between multi-channel and smart channel routing, we recommend using the later as it is an optimal way of achieving the engagement uptick of multi-channel reach out without bombarding your users. Notification content is picked from the template, and only published and live templates can be added to the delivery node. Therefore, it's essential to [design the template](/docs/templates) before configuring this node. When the workflow reaches this node, it looks for active channels in the template and user profile, and send the notification to active user channels whose template content is live. [User preferences](/docs/user-preferences) are also taken into account at this stage. If the user has opted out of the given notification category or channel, it will be skipped, and corresponding errors can be seen on the[ logs page](/docs/logging). ### 3. Function Nodes Functions are logical steps in your workflow. We currently support the following functions: Halt workflow for an interval before proceeding to the next node. Aggregate multiple triggers into a single consolidated notification. Batch multiple alerts and send a summary at a recurring schedule. Send notification in a given time schedule or in user's timezone. Transforms existing workflow data and creates / modifies variables. Trigger another workflow using current workflow data. We are adding more functions to support the complex notification use cases. Have a use case in mind? Reach out to us in our [Slack community](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email), and we'll prioritize it. ### 4. Branch Nodes Branches split the workflow execution into parallel flows. We currently support following branches: Halt workflow until a condition is met or a specific time interval is reached. Route notifications through different workflow routes based on conditions. ### 5. Data Update Nodes These Nodes are used to bring in, modify or update data within workflow. Available data update nodes are: HTTP API request to GET data from an external endpoint to use in workflow. Update recipient or actor within the workflow before sending them notification. Dynamically add or remove recipient or actor in/from the list. Dynamically add or remove recipient or actor in/from the object subscript. ### Workflow Settings This is where basic workflow details like name, description and tags go. You can also define workflow-level conditions like [throttle](https://docs.suprsend.com/docs/throttle) here. Below is a list of workflow configurations and their descriptions: | Field | Obligation | Description | | ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | *\*mandatory* | Unique name of the workflow. The workflow name should be easily identifiable for your reference at a later stage. e.g. - `Appointment Reminder`. You can also use event name as your workflow name if there is only one workflow linked to that event. | | **Description** | *optional* | You can use this field to add more details about your workflow like the notification logic. e.g. - `Send 1 hour before the appointment`. | | **Notification Category** | *\*mandatory* | \[Notification category] ("[https://docs.suprsend.com/docs/notification-category](https://docs.suprsend.com/docs/notification-category)") is used to group related workflows together and are also used for users to set their preferences across a group of workflows. There are 3 notification categories by default: `transactional` (user action-based alerts), `promotional` (marketing notifications), and `system` (time-sensitive notifications like OTP and authentication codes). | | **Tags** | *optional* | Tags are used to group related workflows together. e.g., all booking-related workflows can be tagged as `Booking`. | | **Throttle** | *optional* | You can use throttle to limit the number of workflow executions per user in a given time window. \[Know more about throttle here] ("[https://docs.suprsend.com/docs/throttle](https://docs.suprsend.com/docs/throttle)") . | ### Change Node name and description To modify the name and description of a node, click on the `edit metadata` option from the burger menu on your node form. Choose a descriptive name that clarifies the function of the step, and we suggest including node type in the name for easier identification. In the description field, provide a concise explanation of the logic and important elements of the node. For instance, in a send node, you can specify the template being used or provide details about the list of users who will be notified if it differs from the actor. ## Delete Node If you want to remove a node from the workflow, click on the `delete` option from the burger menu on your node form to delete it. ## Cloning a workflow We recommend designing and testing workflows in staging workspace first before pushing it to production. You can use clone functionality to duplicate workflows across workspaces or to avoid creating similar workflows from scratch. To clone a workflow, just click the clone button on workflow details page in view mode. Please note that you won't see this option while editing the workflow. Once cloned, your workflow will appear as a draft in your chosen destination. You can then commit it to make it live. ![](https://files.readme.io/fe58c04-workflow_clone.gif) *** # API Keys and Secrets Source: https://docs.suprsend.com/docs/developer/api-keys Learn the different authentication methods available in SuprSend and how to securely integrate them into your application. SuprSend supports **three authentication methods**: * **Workspace Key & Secret** → Used to authenticate requests from **Backend SDKs**. * **API Keys** → Used to authenticate **REST APIs** as `Bearer `. * **Public Key & Signing Key** → Used to authenticate **Client SDKs** (with enhanced security options). All keys and secrets are unique per workspace. This is done to keep your testing and production workspace separate and safeguards against accidentally sending wrong notification to your production users during testing. *** ## 1. Authenticating Backend SDKs Backend SDKs are authenticated using a **Workspace Key** and **Workspace Secret**. To find these credentials: 1. Go to **SuprSend Dashboard → Developers → API Keys**. 2. The Workspace Key and Secret for the selected workspace are shown at the top. Save this as environment variable in your backend SDK configuration for safekeeping. *** ## 2. Authenticating REST API Requests REST API requests are authenticated using **API Keys**. Pass the API Key in the `Authorization` header with `Bearer` scheme: ```http theme={"system"} Authorization: Bearer Content-Type: application/json ``` To find these credentials: 1. Navigate to **Dashboard → Developers → API Keys**. 2. Click **Generate API Key**. 3. Provide a name and select **Create and Save**. 4. Copy the API Key and store it securely — it will be shown **only once** at generation. API Keys are confidential and are shown only once at generation. We recommend keeping them in your environment variables or secure vault to avoid accidental exposure. ## 3. Authenticating Client-side SDKs Client SDKs (Web/Mobile) use Public Keys for authentication. You can manage these in Dashboard → Developers → API Keys → Public Keys. Generate new keys or rotate/delete existing ones. For Production workspaces, Public Keys alone are insecure. Enable Enhanced Security Mode, which requires a Signed User Token (JWT) from your backend. 📘 Some legacy mobile SDKs may still use Workspace Key/Secret. These are being phased out. ### Enhanced Security Mode with signed User Token When enhanced security mode is on, user level authentication is performed for all requests. This is recommended for Production workspaces. All requests will be rejected by SuprSend if enhanced security mode is on and signed user token is not provided. This signed user token should be generated by your backend application and should be passed to your client. You can generate Signing key from SuprSend Dashboard (below Public Keys section in API Keys page). Once signing key is generated it won't be shown again, so copy and store it securely. It contains 2 formats: * **Base64 format:** This is single line text, suitable for storing as an environment variable. * **PEM format:** This is multiline text format string. You can use any of the above format. This key will be used as secret to generate JWT token as shown in below step. This should be created on your backend application only. You will need to sign the JWT token with the signing key from above step and expose this JWT token to your Frontend application. * **JWT Algorithm:**ES256 * **JWT Secret:**Signing key in PEM format generated in step1. If you are using Base64 format, it should be converted in to PEM format. * **JWT Payload:** ```json Payload theme={"system"} { "entity_type": 'subscriber', // hardcode this value to subscriber "entity_id": your_distinct_id, // replace this with your actual distinct id "exp": 1725814228, // token expiry timestamp in seconds "iat": 1725814228 // token issued timestamp in seconds. "scope": { "tenant_id": "string" } } ``` SuprSend requests will be scoped to tenant. If tenant passed by you in SDK doesn't match with the JWT payload scope `tenant_id` then requests will throw `403` error. If `tenant_id` is not passed, it is assumed to be `default` tenant. Currently only Inbox requests supports scope, later on we will extend it to preferences and other requests. Create JWT token using above information: ```javascript Node theme={"system"} import jwt from 'jsonwebtoken'; const payload = { entity_type:'subscriber', entity_id:"johndoe", exp:1725814228 }; const secret = 'your PEM format signing key'; // if base64 signing key format is used use below code to convert to PEM format. const secret = Buffer.from('your_base64_signingKey', 'base64').toString('utf-8') const signedUserToken = jwt.sign(payload, secret,{ algorithm: 'ES256' }) ``` After creating user token on backend send it to your Frontend application to be used in SuprSend SDK as user token. ```javascript Javascript theme={"system"} import SuprSend from '@suprsend/web-sdk'; const suprSendClient = new SuprSend(publicApiKey: string); const authResponse = await suprSendClient.identify(user.id, user.userToken); ``` ### Token expiry handling To handle cases of token expiry our client SDK's have **Refresh User Token callback** as parameter in identify method which gets called to get new user token when existing token is expired. ```javascript Javascript theme={"system"} const authResponse = await suprSendClient.identify(user.id, user.userToken, { refreshUserToken: (oldUserToken, tokenPayload) => { //.... write your logic to get new token by making API call to your server... // return new token }}); ``` *** # Management API Source: https://docs.suprsend.com/docs/developer/management-api Use the SuprSend Management API to programmatically manage and deploy notification assets across workspaces. ## Overview The **SuprSend Management API** lets you programmatically manage assets such as: Workflows, Templates, Schemas, Translations. This API is designed for **asset management** and **cross-workspace operations** (e.g., promoting workflows from Staging → Production). The Management API only provides access to resources managed in the **SuprSend Dashboard**. For all other operations (e.g., users, triggering workflows, managing tenants or preferences), use the [REST API Reference](/reference/overview). *** ## Authentication Management APIs require a **Service Token** for authentication.\ Generate one from [**Dashboard → Account Settings → Service Tokens**](https://app.suprsend.com/en/account-settings/service-tokens). Include the token in the `Authorization` header as `ServiceToken `: ```bash theme={"system"} curl -H "Authorization: ServiceToken " \ -H "Content-Type: application/json" \ https://management-api.suprsend.com/v1/workflows ``` ### Verify authentication Run a test request with your Service Token: ```bash theme={"system"} curl -H "Authorization: ServiceToken " \ -H "Content-Type: application/json" \ https://management-api.suprsend.com/v1/workflows ``` ## API Reference For a complete list of endpoints and request/response schemas, see the [Management API Reference](/docs/management-api-overview). ## Postman Collection The fastest way to get started is by exploring the APIs in our [Postman Collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521). # Overview Source: https://docs.suprsend.com/docs/developer/overview Learn how to use SuprSend developer tools to build integrations, test notifications, and manage your notification system programmatically. SuprSend provides a comprehensive set of developer resources to help you build, test, and manage your notification system safely andefficiently. These tools are designed to simplify integrating, building and testing your notification system with SuprSend. ## Authentication Learn how to set up and manage API keys for secure authentication with SuprSend APIs. Learn how to set up and manage service tokens for secure authentication with SuprSend Management API. ## Developer tools Pull, Push and sync SuprSend assets right from your terminal. Query, Search, Update assets and communicate with SuprSend from your AI agents through tool calling. ## SDKs and APIs Choose from our server-side and client-side SDKs for seamless integration with your application. Use our REST API to programmatically interact with SuprSend. Programmatically manage workflows, templates, and other SuprSend resources using our Management API. Test and explore SuprSend APIs using our comprehensive Postman collection with pre-configured requests. ## Testing Safely test notification flows in staging environments without delivering to real users. ## Monitoring and Logging Track complete notification lifecycle on SuprSend dashboard: starting from request, workflow execution, to message delivery, and its engagement status (delivered, seen, clicked). Configure webhooks to receive real-time updates about notification status and delivery events. Store notification logs in Amazon S3 for long-term storage and analysis. ## Developer Community Video tutorials, demos, and best practices for building with SuprSend. Technical articles, case studies, and product updates from our team. Open source tools, examples, and SDKs for developers. Connect with other developers, get help, and share your projects. # Postman Collection Source: https://docs.suprsend.com/docs/developer/postman-collection Explore and test SuprSend APIs using the official Postman collection, pre-configured with requests and examples. Our [Postman collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) is the fastest way to get familiar with SuprSend APIs. It includes pre-configured requests and example payloads so you can start testing right away. *** ## Set Up Postman Collection Go to the [SuprSend Postman Workspace](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) and **fork the collection** into your own workspace. In Postman, create a new environment and add the following variables. Retrieve your credentials from: * **SuprSend Dashboard → Developers → API Keys** * **SuprSend Dashboard → Account Settings → Service Tokens** | Variable | Description | Example Value | | ------------------------- | ---------------------------------------------------- | ------------------------------------- | | `base_url` | Base URL for **REST API** requests | `https://hub.suprsend.com` | | `management_api_base_url` | Base URL for **Management API** requests | `https://management-api.suprsend.com` | | `api_key` | API Key for the respective workspace (REST API auth) | `your_api_key_here` | | `service_token` | Service Token for account-level Management API auth | `your_service_token_here` | Select the configured environment and start testing SuprSend APIs using the pre-configured requests in the collection. *** ## Best Practices * Keep your `api_key` and `service_token` **secure** — never share them in a public collection. * Use **separate environments** for Sandbox, Staging, and Production. * Rotate credentials periodically following [key and token management best practices](/docs/best-practices-for-api-keys-management). # REST API Source: https://docs.suprsend.com/docs/developer/rest-api Learn how to use SuprSend REST APIs to sync users and send notifications at scale. The **SuprSend REST API** enables you to sync user data and send notifications at scale. We have [SDKs](/docs/developer/sdk-overview) for most popular languages. If your backend or frontend is built in another language or you prefer direct integration, you can use the REST API to programmatically interact with SuprSend. *** ## Authentication REST APIs require an **API Key** for authentication. API Keys are scoped **per workspace** (e.g., Sandbox, Staging, Production), ensuring isolation across environments. Include your API Key in the `Authorization` header as a `Bearer` token: ```http theme={"system"} Authorization: Bearer Content-Type: application/json ``` ## API Reference For a complete list of endpoints and request/response schemas, see the [API Reference](/reference/overview). ## Postman Collection The fastest way to get started is by exploring the APIs in our [Postman Collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521). # SDK Overview Source: https://docs.suprsend.com/docs/developer/sdk-overview SuprSend SDKs to integrate notifications into your server-side and client-side applications. SuprSend SDKs are lightweight wrappers around the REST APIs, providing out-of-the-box methods that reduce repetitive code and simplify integration. ## Server-side SDKs Server-side SDKs let you integrate SuprSend in your backend applications and handle notification orchestration with minimal setup. Python Node.js · TypeScript Java Golang ## Client-side SDKs Client-side SDKs allow you to integrate SuprSend into **web and mobile applications** and build **in-app notification and Preference centre UI** with ease. ### Web SDKs JavaScript · TypeScript JavaScript · TypeScript ### Mobile SDKs Kotlin Swift JavaScript · TypeScript Flutter · Dart # Service Token Source: https://docs.suprsend.com/docs/developer/service-tokens Learn how to authenticate Management API requests using Service Tokens in SuprSend. SuprSend **Management APIs** (used to manage assets like workflows, templates, schemas, etc.) use **Service Token authentication** instead of the regular API Key authentication. * **Service Tokens** are created at the **account level**, not per workspace. * They are designed for **cross-workspace operations**, such as promoting assets from **Staging → Production**. * Unlike API Keys, Service Tokens are **not tied to notification delivery**, but strictly to **management operations**. *** ## Generating a Service Token 1. Navigate to **[SuprSend Dashboard → Account Settings → Service Tokens](https://app.suprsend.com/en/account-settings/service-tokens)** 2. Click **Generate a new Token**. 3. Provide a **descriptive name** (e.g., `staging-to-prod-promotion`). 4. Click **Create and View**. 5. Copy and store the token securely — it is shown **only once** at generation. Service Tokens give access to all workspaces in your account. Store them in environment variables or a secure vault and never commit them to source control. *** ## Using Service Tokens in Management APIs To authenticate a Management API request, include the Service Token in the `Authorization` header using the `ServiceToken` scheme: ```http theme={"system"} Authorization: ServiceToken Content-Type: application/json ``` ### Example (cURL): ```curl theme={"system"} -X POST "https://management-api.suprsend.com/management/workflows/import" \ -H "Authorization: ServiceToken " \ -H "Content-Type: application/json" \ -d '{"workflow_id": "order-confirmation", "source": "staging", "destination": "production"}' ``` # Test Mode Source: https://docs.suprsend.com/docs/developer/test-mode Safely test notification flows without risk of delivering to real users. The **Test Mode** feature lets you validate your notification flows without worrying about sending messages to real end users. This way you avoid accidental sends to production users while iterating on templates and workflows. With Test Mode enabled: * Notifications are delivered **only to designated internal testers** set as test channels. * The **entire workflow execution** works as usual, just the final delivery is blocked or redirected to catch-all channel. * You can set a **catch-all channel** where notifications sent to non-test channels are diverted. *** ## How It Works * Available in **Staging** and other testing workspaces (not in Sandbox or Production). * Once enabled, the entire workflow execution works as usual. * Test mode applies to final notification delivery and one of the following cases can happen: * We check if the channel value belongs to the test channels. If it does, notification is delivered. * If it doesn't, we check if there is a catch-all channel set for the channel type. If it does, notification is diverted to the catch-all channel. * If there is no catch-all channel set, notification is blocked. *** ## Set Up Test Mode In your workspace, go to [**Developers → Test Mode**](https://app.suprsend.com/en/staging/developers/test-mode) and toggle it ON.\ ⚠️ It may take up to 5 minutes for Test Mode to become active (to avoid disrupting running workflows). Test Mode Enabled Choose which channels to block delivery on. Currently supported: **Email, SMS, WhatsApp** (To enable this on other channel types, Reach us at [support@suprsend.com](mailto:support@suprsend.com)). Add internal testers who should receive notifications during Test Mode. * You can channels by searching available users in SuprSend DB and then selecting their channels. * Or add channel identifiers directly (they don’t need to belong to a registered SuprSend user). Assign one **catch-all channel per type** (e.g., email, SMS).\ All notifications sent to non-test users on that channel will be redirected here (e.g., `qa@company.com`, `dev@company.com`). Test Mode Catch-All *** ## Benefits for Developers * ✅ **Safe testing** → prevent accidental notifications being sent to your production users. * ✅ **Centralized debugging** → catch-all ensures nothing slips through unnoticed and you can see all test notifications in one place. * ✅ **Realistic previews** → internal testers get full notifications as they would appear in production. * ✅ **Faster iteration** → debug templates, workflows, and vendor integrations without cleanup worries. *** ## Troubleshooting * Verify Test Mode is enabled in the correct workspace. * Confirm the correct channels are blocked. * Check that test users are added properly. * Wait up to 5 minutes after enabling Test Mode. * Verify Test Mode is enabled in the correct workspace. * Confirm the recipient is not mistakenly added as a test user. * Wait up to 5 minutes after enabling Test Mode. # SDK Changelog Source: https://docs.suprsend.com/docs/developer/versioning/sdk-changelog Complete release history for all SuprSend SDKs with detailed release notes, features, fixes, and breaking changes. The SuprSend SDK Changelog provides a comprehensive record of all SDK releases, including new features, bug fixes, breaking changes, and security updates across all supported platforms. ## Release History

Repository: suprsend-py-sdk

Latest Release: v0.15.0

v0.15.0

Aug 30, 2025

Changes:

  • v2 APIs and response fixes

v0.14.0

Apr 12, 2025

Changes:

  • Removed cap of 100 from workflow recipients

v0.13.0

Feb 26, 2025

Features:

  • User APIs

v0.12.0

Nov 4, 2024

Changes:

  • Updated README and renamed `$pushvendor` to `$id_provider`
  • Objects implementation methods

v0.11.0

Apr 18, 2024

Features:

  • Added support for user's timezone
  • Added support for workflow trigger API

v0.11.0-pre

Apr 16, 2024

Features:

  • Workflow trigger API

v0.10.0

Jan 29, 2024

Features:

  • Added tenants API

v0.9.0

Oct 31, 2023

Features:

  • Added set/setonce/increment method to update subscriber properties

v0.8.0

Oct 16, 2023

Features:

  • Added support for Microsoft Teams

v0.7.0

Sep 14, 2023

Changes:

  • Increased idempotency key length to 255

v0.6.0

Feb 27, 2023

Features:

  • Support for all outbound Slack messages

v0.6.0-pre

Feb 27, 2023

Features:

  • Support for all outbound Slack messages

v0.5.2

Jan 6, 2023

Features:

  • Support dynamic workflow for transient users

v0.5.1-pre

Dec 26, 2022

Features:

  • Subscribers List and broadcast APIs

v0.5.0

Dec 10, 2022

Features:

  • User operation events v2 schema
  • Faster jsonschema validation for workflows and events

v0.4.2-alpha

Oct 21, 2022

Features:

  • Expose APIs to add/update/retrieve organization brands

v0.4.1

Oct 19, 2022

Features:

  • Support for attachments using URL

v0.4.0

Oct 10, 2022

Features:

  • Added `user.set_preferred_language()` method to enable multi-lingual notifications based on user's preference

v0.3.0

Sep 29, 2022

Features:

  • Support for idempotency-key in workflow and event

v0.2.1

Sep 21, 2022

Changes:

  • Minor fixes

v0.2.0

Sep 21, 2022

Changes:

  • Terminology changed from Batch to Bulk

v0.1.5

Sep 5, 2022

Features:

  • Workflow now supports User channel preference
  • Workflow now supports push tokens with provider

v0.1.4

Sep 3, 2022

Features:

  • Batch support for Events, Workflows and Users

v0.1.3

Jul 24, 2022

Changes:

  • Updated mandatory channels list in Dynamic workflow trigger

v0.1.2

Jul 6, 2022

Features:

  • Added user methods: `add_slack_email`, `add_slack_userid`, `remove_slack_email`, `remove_slack_userid`

v0.1.1

Apr 30, 2022

Initial Release

v0.1.0

Apr 28, 2022

Initial Release

v0.1.0-pre

Apr 13, 2022

Pre-release

v0.0.15

Apr 13, 2022

Initial Release

Repository: suprsend-node-sdk

Latest Release: v1.13.1

v1.13.1

2 weeks ago

Bugfix:

  • Fixed error naming issue in users API

v1.13.0

May 21, 2025

Changes:

  • Added user APIs
  • Removed validation for user channel methods
  • Request payload size changes

v1.12.0

Jan 30, 2025

Features:

  • Object improvement and user methods support

v1.11.1

Jan 18, 2025

Bugfix:

  • Added defaults for optional method parameters in objects

v1.11.0

Nov 4, 2024

Changes:

  • Updated README
  • Added object implementation methods

v1.10.0

May 1, 2024

Features:

  • Added new workflow API
  • Timezone set method in user methods

v1.9.1

Mar 13, 2024

Changes:

  • Added preferred\_language in workflow schema

v1.9.0

Jan 10, 2024

Features:

  • Added tenant APIs
  • Microsoft Teams channel added
  • Syncing list by version methods added
  • User methods: set, set\_once, increment added

v1.8.2

Nov 9, 2023

Fixes:

  • Fixed GitHub dependabot issues

v1.8.1

Jun 13, 2023

Changes:

  • Increased idempotency\_key length

v1.8.0

Apr 22, 2023

Features:

  • Error handling in bulk API

v1.7.1

Mar 31, 2023

Fixes:

  • Fixed error logging related issue in bulk APIs

v1.7.0

Mar 16, 2023

Fixes:

  • Fixed dependabot vulnerabilities in json5 and minimatch packages

v1.6.0

Mar 2, 2023

Features:

  • Added `add_slack`, `remove_slack` methods
  • Deprecated `add_slack_email`, `remove_slack_email`, `add_slack_userid`, `remove_slack_userid`
  • Workflow\.json file minor changes

v1.5.1

Feb 20, 2023

Changes:

  • Made config flag optional while initializing SuprSend instance

v1.5.0

Feb 16, 2023

Features:

  • Added types in this SDK for TypeScript support

v1.4.0

Jan 12, 2023

Features:

  • Broadcast method added
  • List object naming change

v1.3.0

Dec 30, 2022

Features:

  • User operations implementation

v1.2.0

Dec 24, 2022

Features:

  • Added list API methods

v1.1.2

Dec 19, 2022

Features:

  • Added brands API

v1.1.1

Oct 23, 2022

Changes:

  • Updated README
  • Implementation of user language preference method

v1.1.0

Oct 20, 2022

Features:

  • Implementing remote file URL as attachment

v1.0.0

Sep 30, 2022

Features:

  • Bulk APIs implementation for users, events, and workflows
  • Adding an idempotency\_key in the event and workflow to ignore duplicate requests

v0.1.1

Jul 2, 2022

Changes:

  • Updated axios and babel-core packages versions

v0.1.0

May 3, 2022

Features:

  • Send Track events and set user channels through SDK
  • Implemented Success Metrics

v0.0.6

May 3, 2022

Features:

  • Initialize SuprSend SDK
  • Create Dynamic Workflows

Repository: suprsend-java-sdk

Latest Release: v0.12.0

v0.12.0

Aug 30, 2025

Features:

  • v2 APIs response structure

v0.11.0

Jul 5, 2025

Features:

  • HTTP proxy implementation

v0.10.0

May 22, 2025

Changes:

  • Changed payload size limit to 800KB

v0.9.0

Feb 1, 2025

Features:

  • Added all user APIs

v0.8.0

Jan 24, 2025

Features:

  • Added objects implementation methods

v0.7.2

Jan 21, 2025

Changes:

  • Upgraded dependencies

v0.7.1

Nov 8, 2024

Changes:

  • Renamed `$pushvendor` to `$id_provider`
  • Removed regex validation from JSON schema

v0.7.0

May 5, 2024

Features:

  • Added API to trigger workflow via slug

v0.6.0

Jan 29, 2024

Features:

  • Added subscriber properties
  • List versioning
  • Microsoft Teams support

v0.5.0

Mar 6, 2023

Features:

  • Bulk Event, Subscriber & Workflow Support
  • List Support & Broadcast Support

v0.4.0

Sep 29, 2022

Features:

  • Added support for idempotency key
  • Accept idempotencyKey as part of event and workflow

v0.3.0

May 29, 2022

Changes:

  • Refactored codebase
  • Java 8 compatibility

Repository: suprsend-go

Latest Release: v0.8.0

v0.8.0

Sep 1, 2025

Features:

  • Added TikTok and X social links to Brand and Tenant structures
  • Updated response as per event.v2 API & workflow schema

v0.7.0

Jul 23, 2025

Features:

  • Added proxy support
  • Added Preferences API methods

v0.6.0

May 10, 2025

Changes:

  • Updated README and renamed `$pushvendor` to `$id_provider`
  • Bumped golang.org/x/net dependencies
  • Added object and user API methods

v0.5.1

Apr 18, 2024

Fixes:

  • Git tag issue with v0.5.0

v0.5.0

Apr 18, 2024

Features:

  • Added support for user's timezone
  • Added support for workflow trigger API

v0.5.0-pre

Apr 16, 2024

Features:

  • Workflow trigger API

v0.4.0

Jan 28, 2024

Features:

  • Added tenants API
  • List versioning
  • Microsoft Teams support

v0.3.1

Oct 12, 2023

Security:

  • Upgraded go/net package as part of security fix

v0.3.0

Mar 8, 2023

Features:

  • Include all new features

v0.2.0

Jan 6, 2023

Features:

  • Support for transient users in Dynamic workflow

v0.1.0

Nov 16, 2022

Features:

  • Added basic README and examples

Repository: suprsend-web-sdk

Latest Release: v4.0.3

v4.0.3

11 hours ago

Bugfix:

  • Socket connection authentication failed issue after refresh

v4.0.2

2 weeks ago

Changes:

  • Socket.io connection config changes to support offline mode

v4.0.1

Jul 28, 2025

Features:

  • WebPush token update optimization

v4.0.0

Jul 24, 2025

Bugfix:

  • Pagination issue while archiving notification

v3.1.0

May 24, 2025

Changes:

  • Documentation service worker link version change
  • Added preferences tags

v3.0.3

Apr 22, 2025

Changes:

  • Updated documentation

Bugfix:

  • Fixed host undefined issue in service worker file

v3.0.2

Jan 8, 2025

Changes:

  • Added documentation for in-app feed

v3.0.1

Jan 7, 2025

Features:

  • Support for feed

v2.0.1

Sep 10, 2024

Changes:

  • Revamp: v2 version of web SDK

Repository: suprsend-react-sdk

Latest Release: v0.3.2

v0.3.2

11 hours ago

Bugfix:

  • Socket connection authentication failed issue after refresh

v0.3.1

2 weeks ago

Changes:

  • Version updated and Socket.io connection config changes

v0.3.0

Aug 19, 2025

Features:

  • Shadow DOM support and custom infinite scroll component

v0.2.1

Jul 28, 2025

Features:

  • WebPush add token optimization

v0.2.0

Jul 24, 2025

Bugfix:

  • Archive pagination bug fix

v0.1.3

May 24, 2025

Changes:

  • Updated @suprsend/react-core version

v0.1.2

May 15, 2025

Bugfix:

  • Markdown ESM issue fix

v0.1.1

Apr 17, 2025

Changes:

  • Updated core-sdk version

v0.1.0

Apr 17, 2025

Features:

  • Language support

v0.0.7

Mar 18, 2025

Features:

  • Added disable markdown flag

v0.0.6

Feb 18, 2025

Bugfix:

  • Fixed typedef bug related to children

v0.0.5

Feb 3, 2025

Bugfix:

  • Scrolling issue in macOS and always show action menu icon in mobile

v0.0.4

Jan 16, 2025

Bugfix:

  • Action menu overflow issue fixed

v0.0.3

Jan 11, 2025

Bugfix:

  • Improved docs and fixed null case issue of notification card

v0.0.2

Jan 8, 2025

Changes:

  • Added documentation

Repository: suprsend-android-sdk

Latest Release: 0.1.8

0.1.8

Sep 4, 2022

Bugfix:

  • Notification - Small Icon & Action Icon support
  • If icon does not exist in drawable folder then notification was not getting shown

0.1.4

Jun 17, 2022

Changes:

  • Removed cached flag and added check to verify app launch

0.1Beta9

Apr 24, 2022

Changes:

  • Minor fixes

Repository: SuprSend-iOS-SDK

Latest Release: 1.0.7

1.0.7

Mar 11, 2025

Changes:

  • Link SQLite library

1.0.6

Mar 11, 2025

Changes:

  • Remove bitcode from SuprsendCore

1.0.4

Mar 11, 2025

Changes:

  • Bump pod version

1.0.3

Aug 13, 2024

Features:

  • iOS APNS push delivery status improvements

1.0.2

Feb 4, 2023

Changes:

  • SuprSend SDK changes for unsubscribe push notifications on reset

Repository: suprsend-rn-sdk

Latest Release: v2.5.0

v2.5.0

Mar 13, 2025

Changes:

  • Upgraded native iOS SDK to fix bitcode issue

v2.4.0

Sep 3, 2024

Changes:

  • Fixed GitHub dependabot issues
  • Updated iOS native version to fix APNS delivery issue

v2.3.1

Sep 25, 2023

Fixes:

  • Fixed GitHub dependabot issues

v2.3.0

Mar 21, 2023

Fixes:

  • Fixed GitHub Dependabot vulnerabilities in dependencies

v2.2.0

Mar 13, 2023

Changes:

  • Updated Android SDK version to enable sound customization in FCM push

v2.1.0

Feb 9, 2023

Features:

  • Added unsubscribe push flag in reset method

v2.0.2

Jan 6, 2023

Features:

  • Added enableLogging method
  • Deprecated setLogLevel method

v2.0.1

Jan 4, 2023

Bugfix:

  • Reset bugfix in iOS

v2.0.0

Dec 29, 2022

Features:

  • Upgraded Android native SDK version
  • Method to ask notification permission for Android version >= 13
  • Reset method now takes a parameter to remove push tokens on logout

v1.0.0

Sep 28, 2022

Changes:

  • Upgraded native iOS SDK version

v0.4.3

Sep 17, 2022

Fixes:

  • Fixed track method issue in Android
  • iOS deployment version upgraded to 11 in podspec for iOS

v0.4.2

Sep 6, 2022

Changes:

  • Upgraded native Android version

v0.4.1

Aug 31, 2022

Changes:

  • Updated native Android version

v0.4.0

May 20, 2022

Features:

  • iOS implementation in React Native
  • iOS Push notifications implemented

v0.3.14

May 20, 2022

Features:

  • Initial stable release with only Android implementation in this React Native project

Repository: suprsend-flutter-sdk

Latest Release: v2.5.0

v2.5.0

Jun 7, 2025

Changes:

  • Upgraded native SuprSend Android SDK version

v2.4.0

May 10, 2025

Changes:

  • Added namespace changes and removed ask notification permission method

v2.3.1

Mar 11, 2025

Bugfix:

  • Upgraded iOS SDK version to fix bitcode

v2.3.0

Mar 11, 2025

Changes:

  • Upgraded version of native iOS SDK

v2.2.0

Aug 14, 2024

Changes:

  • Fixed iOS delivery issue

v2.1.1

Nov 30, 2023

Changes:

  • Native Android SDK version updated
  • Removed debug logs in Android on identify

v2.1.0

Feb 9, 2023

Features:

  • Added unsubscribePush flag in reset method

v2.0.1

Jan 24, 2023

Changes:

  • Downgraded minimum Dart SDK version to 2.15.0 from 2.16.0

v2.0.0

Jan 6, 2023

Features:

  • Updated native Android version
  • Reset method now accepts unsubscribe\_push flag
  • Added permission method to ask for user permission to show notifications for Android 13

v1.0.0

Nov 4, 2022

Changes:

  • Upgraded native iOS SDK version to 1.0.1
## Migration Guides For detailed migration instructions between major versions, please refer to our [SDK Migration Guide](/docs/developer/versioning/sdk-versioning#migration-guides). ## Support If you encounter any issues during migration or have questions about specific releases, please contact our support team at [support@suprsend.com](mailto:support@suprsend.com) # Versioning and Support Policy Source: https://docs.suprsend.com/docs/developer/versioning/sdk-versioning Learn about SuprSend's versioning and support policy for SDKs and APIs. View all SDK releases and updates Step-by-step upgrade instructions Complete SDK integration guides ## Versioning Policy SuprSend's SDK versioning policy is based on the **[Semantic Versioning (SemVer)](https://semver.org/)** standard. For example, in version 4.3.2, 4 is the **major**, 3 is the **minor**, and 2 is the **patch**. **Major.** Breaking changes that are backward incompatible (e.g., renaming SDK exception classes). **Minor.** New features that are backward compatible (e.g., adding new methods or optional parameters). **Patch.** Backward-compatible bug fixes (e.g., fixing file upload listing issues). Each SDK version is coupled with the API version that was current at the time of release. This ensures stable and predictable API behavior across versions. ## Support Policy All new features, performance improvements, and bug fixes are released on the latest major version of each SDK. Older major versions remain functional, but updates are only rolled out to the most recent major release. SuprSend maintains backward compatibility across all SDKs, ensuring existing integrations continue to work as expected. However, we recommend upgrading to the latest version to take advantage of improved authorization mechanisms (especially in frontend SDKs), better performance, and access to new API capabilities. Our release cycle is synchronized with product development milestones, so SDK and API versions evolve together. This ensures consistent behavior across the platform and prevents breaking changes in existing implementations. Comprehensive migration guides are provided for each major version upgrade, helping you transition with minimal code changes and predictable results. ### Migration Guides We provide migration guides to help you upgrade from older major SDK versions. You can find them in our documentation: * [Migration guide from v1](/docs/migration-guide-from-v1) * [React migration guide](/docs/react-migration-guide) * [Web SDK migration guide](/docs/migration-guide-from-v1) ## Language Support Policy We currently support all SDK languages without any deprecation timeline. In the unlikely event of deprecating a language or version in the future, advance notice will be provided to ensure sufficient migration time. ### Language Support Status | Language | Version | Status | Support Level | | ---------------- | ---------------------- | -------- | ------------- | | **Node.js** | 14+ | ✅ Active | Full Support | | **JavaScript** | All Versions | ✅ Active | Full Support | | **React** | 16.8+ | ✅ Active | Full Support | | **Python** | 3.9+ | ✅ Active | Full Support | | **Java** | 8+ | ✅ Active | Full Support | | **Go** | 1.18+ | ✅ Active | Full Support | | **Swift** | iOS 15+ | ✅ Active | Full Support | | **Kotlin** | Android (all versions) | ✅ Active | Full Support | | **React Native** | All Versions | ✅ Active | Full Support | | **Flutter** | >=2.5.0 | ✅ Active | Full Support | All supported language versions receive full support with no deprecation timeline. This includes regular updates, security patches, and comprehensive documentation. ## Release Policy We follow agile methodologies to ensure rapid and flexible development. **Roadmap Features:** Features are released as soon as development and testing are complete, typically at a rate of \~1-2 per week. There is no fixed release day. We prioritize user feedback and market needs when planning feature releases. **Versions in Backend SDKs:** New versions are released mainly for new API additions. User-facing APIs will always be backward compatible. We maintain comprehensive API versioning to ensure smooth transitions for existing integrations. **Frontend SDKs:** We incrementally add features in frontend SDKs such as Inbox, and release depends on our roadmap. Frontend updates focus on enhancing user experience and adding interactive capabilities. **Bug Fixes & Improvements:** Bug fixes are prioritized and released as soon as they are identified. Critical security fixes are released immediately, while minor improvements are batched for regular releases. **Communication:** Releases are announced on our [Slack community](https://suprsend.com/slack), in-app and via [email newsletters](https://suprsend.com/newsletter). Important features are also communicated directly in shared groups. We provide detailed changelogs and migration guides for major updates. ## Best Practices
• Test in staging environment first
• Review changelog for breaking changes
• Backup your current implementation
• Verify all dependencies are compatible
• Follow our detailed migration guides
• Update incrementally and test thoroughly
• Monitor application after deployment
• Have a rollback plan ready
💡 **Pro tip:** Start with patch upgrades before moving to minor or major versions to minimize risk. # Webhooks Source: https://docs.suprsend.com/docs/developer/webhooks Quick setup guide for configuring webhooks to receive real-time notification updates and delivery status in your application. Set up webhooks to receive real-time updates about notification status changes, delivery events, and user interactions. ## Set Up Webhooks Create an HTTP/HTTPS endpoint in your application that can receive POST requests. Go to Developers → Webhooks → Enable Webhook → Add Endpoint Choose which notification events you want to track for each channel. Send a test notification to verify webhook delivery. ## Webhook reference You can find detailed webhook configuration options, payload structures, and advanced setup in our [complete webhook documentation](/docs/outbound-webhook). # Digest Source: https://docs.suprsend.com/docs/digest Batch multiple alerts & send summary of notifications at a recurring schedule to the user. The Digest node aggregates multiple triggers into a single, summarized notification sent at a recurring schedule. Some common use cases include sending recommendations or top stories like you get for LinkedIn or Quora, or to send a weekly / daily summary of activities in a company workspace or SaaS application. You can configure Digest nodes to send notifications on a [fixed schedule](/docs/digest#fixed-schedule) to all users or a [dynamic schedule based on user preferences](/docs/digest#dynamic-schedule-send-digest-based-on-user-preference). Digest can be configured to be timezone-aware, ensuring that final notifications are sent in user's preferred timezone and all users receive the digest at a reasonable hour. ## How Digest works? When a workflow reaches the Digest node, it opens a batch until the next digest schedule is triggered. Unlike regular batching, the Digest node operates on a fixed schedule rather than being relative to when the first event is received. Consequently, the next steps following the Digest node will be executed at the fixed schedule, regardless of when triggers are received. During the batch period, all events for the same recipient with the same workflow slug are accumulated. A unique batch is created for each recipient. When the digest schedule is reached, a single notification is sent for each batch. You can configure the number of events retained in the batch using the [retain items](/docs/digest#retain-items) setting. The digest output structure is similar to the batch output and templates can be edited in the same format as you do for batched alerts. Refer to [Using Batch Variables in Templates](/docs/digest#using-digest-batch-variables-in-templates) for more details. If no triggers are received within a given schedule or if the number of events is below the [minimum trigger count](/docs/digest#min-trigger-count), the workflow will exit without executing subsequent steps. **Example Use case** A workflow sends a summary of task status changes daily at 7:00 PM for a workflow management tool. In this case, the workflow will batch all triggers for 11 hours (until July 30, 7:00 PM Europe/London) and the email will be sent at the same time as soon as batch is closed. * **Trigger**: When task status is changed (First task status change is received at Jul 30, 7:00 AM UTC) * **Digest Schedule**: Daily at 7:00 PM (in recipient's timezone) * **Recipient’s Timezone**: Europe/London (UTC+1). In this case, the workflow will batch all triggers for 11 hours (until July 30, 7:00 PM Europe/London) and the email will be sent at the same time as soon as batch is closed. ## Configuring Digest schedule It is the recurring schedule when an open digest should be closed. This schedule might differ from the time notifications are actually sent. e.g., if you want to send a daily digest summarizing activities from the previous day at 9:00 AM, you would set the Digest schedule to close daily at midnight and follow it with Time Window or Delay node to send the notification at 9:00 AM. Fixed schedule is same for all users and hard coded in the workflow logic. It has 4 inputs: 1. **Repeat every**: Defines the recurrence of the digest, such as every 5 minutes, hourly, daily, etc. It is a combination interval (1,2,3 etc.) and frequency (daily, weekly, monthly, hourly and minutely). e.g. you can set frequencies as: * every 5 minutes (here, interval is `5 `with `minutely `frequency) * every hour * every 3 days * every weekday * every 2 weeks on selected days of the week (e.g. Mondays and Wednesdays of the week) * every month on selected days of the month (e.g. 1st, 3rd, and 5th Mondays of the month or 1st - 5th day of the month) 2. **Time**: Specifies when the digest should be closed for daily, weekly or monthly frequency. The time is always in reference to the timezone selected. 3. **Timezone**: Set the timezone for the Time specified. You can select **recipient's timezone**, which will be dynamically calculated for each recipient. You can set recipient timezone in user profile with`$timezone`key in HTTP API or`user.set_timezone()`method from your backend or Frontend SDKs. Timezones should be in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format, such as `America/New_York`. 4. **Starting from**: Defines the starting point for recurring schedule. e.g., if starting from is`2024-07-17 17:00`and repeat every is set to daily at 4pm IST, first schedule will be 18th July 4pm IST. You can set a future starting point if you want the first digest to be sent later. Note that in this case, all triggers from when the workflow is activated until the first schedule after the "starting from" time will be included in the first digest. Referred to as`dtstart`in dynamic schedule expression. 📘 If a recipient's timezone is not set, it will default to the account timezone specified in the [SuprSend dashboard -> Account settings](https://app.suprsend.com/en/account-settings/general). If no account timezone is set, UTC (Coordinated Universal Time) will be used as the final fallback. The dynamic schedule is dynamically derived from the workflow trigger or recipient profile, allowing for schedules that adjust based on user preferences. It is advisable to store it in recipient profile. If you are sending notification on behalf of your enterprise customers, digest schedule can also come from enterprise level notification preferences. In such cases, you can store the schedule as custom property in [tenant](/docs/tenants#creating--updating-tenant-on-suprsend-platform) settings. ```json Dynamic Schedule Schema theme={"system"} { "frequency": "minutely"/"hourly"/"daily"/"weekly_mo2fr"/"weekly"/"monthly", "interval": 1, //you can pass any integer value here, default value 1 //mandatory to pass for weekly frequency, weekly_mo2fr assumes weekdays: [ "mo", "tu", "we", "th", "fr"] "weekdays": ["su", "mo", "tu", "we", "th", "fr", "sa"], //mandatory to pass for monthly frequency, "day":"" would represent day of the month, //pass first 2 character of weekday to set frequency like 1st Monday of the month "monthdays": [{"pos": 1, "day": ""/""}], "time": "08:00", // defaults to 00:00 if not passed "dtstart": "2024-08-01T10:40:50", // defaults to current_timestamp if not passed "tz_selection": "recipient", // pass empty for fixed timezone "tz_fixed": "UTC", // pass this if tz_selection is empty, defaults to UTC if not set } ``` | Variable | Type | Description | | -------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `frequency` | string (mandatory) | Choose from one of the below options: **minutely** **hourly** **daily** **weekly** (mandatory to pass `weekdays` in this case) **weekly\_mo2fr** (weekly on Monday to Friday) **monthly** (mandatory to pass `monthdays` in this case) | | `interval` | integer (optional) | Interval at which the frequency will repeat. Interval `2` with frequency `daily` would mean repeat every 2 days. Defaults to `1` if not set. | | `weekdays` | array\[] (mandatory for `weekly` frequency) | Days of the week for weekly frequency. Pass the first 2 characters of days of the week in an array as `["su", "mo", "tu", "we", "th", "fr", "sa"]` | | `monthdays` | array\[map] (mandatory for `monthly` frequency) | Days of the month for monthly frequency (e.g. 1st, 3rd, and 5th Mondays of the month or 1st - 5th day of the month). Pass as `[{"pos": 1, "day": "mo"}]`, where `pos` defines the day index and `day` defines the type of day (can be referred to define the day of the week). e.g., `1st,2nd day of the month` will be set as `[{"pos": 1, "day": ""},{"pos": 2, "day": ""}]`. | | `time` | string (optional) | Time for daily, weekly, or monthly frequency when the digest will close. Defined in hour and minute as `hh:mm`. Defaults to `00:00` if not set. | | `dtstart` | datetime (ISO-8601 format) (optional) | Starting time from which the first schedule will be calculated. Set as `2024-08-01T10:40:50` in ISO-8601 format. Defaults to `current_timestamp` at the time of setting the schedule if not defined. | | `tz_selection` | string (optional) | Timezone selection. `time` and `dtstart` will both be in this timezone. - Leave empty `""` for fixed timezone - Set to `"recipient"` if the timezone needs to be picked dynamically from user property or trigger data. | | `tz_fixed` | string (mandatory if `"tz_selection": ""`) | Timezone to pick in case of fixed `tz_selection`. Add timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format as `America/New_York`. Defaults to `UTC` if not set. | ### Passing dynamic schedule for users You can either pass dynamic schedule in each workflow trigger or a better way to handle is to store it in user profile. You can store any number of schedules in user profile or in tenant properties. It's a good practice to map the digest schedule to either workflow or category identifier in user profile (If you have multiple notification categories in your system). Refer [create user profile section](/docs/users#creating-user-profile-on-suprsend) to understand how user properties are set in user profile. ```json Storing Schedule as user property theme={"system"} // Refer below schedule as ."$recipient".digestSchedule.marketing_alerts in dynamic schedule key user.set( { "digestSchedule":{ "marketing_alerts":{ "frequency": "daily", "time": "08:00", "dtstart": "2024-08-01T10:40:50", "tz_selection": "recipient" } } } ) ``` Refer [workflow trigger methods](/docs/trigger-workflow) to see how dynamic data is passed in different workflow triggers. ```json Passing Schedule in workflow trigger theme={"system"} // Refer below schedule as .digestSchedule in dynamic schedule key body={ "workflow": "_workflow_slug_", "recipients": [..], "data":{ "digestSchedule": { "frequency": "daily", "time": "08:00", "dtstart": "2024-08-01T10:40:50", "tz_selection": "recipient" } } } ``` ## ## Advanced settings Defines the number of trigger data that will be included in your Digest Batch. You have the option to display either the first n triggers or the last n triggers in your digest output. By default, the first 10 triggers are included. You can customize the number of triggers to any value between 2 and 100. Specifies the minimum number of triggers required to proceed with the digest. Workflow will exit and the next steps will not be executed if the number of triggers in the digest is less than this count. This is quite useful for cases where you are sending individual alerts and also a digest of all alerts at the end of the day. Now, In this case, if only two alerts are triggered in a day, sending a digest might be unnecessary. ## Using Digest (Batch) variables in templates Batch output variable has 2 type of variables: 1. `$batched_events`array : All the event properties corresponding to a batched event is appended to this array and can be used in the template in the array format. The number of event properties returned here is limited by retain batch events. 2. `$batched_event_count`: This count represents the number of events in a batch and is utilized to render the batch count in a template. For instance, you might send a message like,`Joe left 5 comments in the last 1 hour`where 5 corresponds to \$batched\_event\_count. 📘 Please note that **Retain items** setting doesn't impact the count, it just limits the number of trigger data returned in `$batched_events` array. Let's understand the batch variable structure with an example of task comments with below notification content. ``` 3 comments are added on your task in last 1 hour. - Steve: Hey, added the test cases added for PRD-12 - Olivia: Hey, done with the testing. Check the bugs - Joe: 3 bugs are resolved, 4 are still pending ``` Here is a list of events triggered in the batched window: ```javascript Event trigger Payload theme={"system"} //Event 1 const event_name = "new_comment" const properties = { "name": "Steve", "card_id": "SS-12", "comment": "Hey, added the test cases added for PRD-12" } const event = new Event(distinct_id, event_name, properties) //Event 2 const event_name = "new_comment" const properties = { "name": "Olivia", "card_id": "SS-12", "comment": "Hey, done with the testing. Check the bugs" } const event = new Event(distinct_id, event_name, properties) //Event 3 const event_name = "new_comment" const properties = { "name": "Joe", "card_id": "SS-12", "comment": "3 bugs are resolved, 4 are still pending" } const event = new Event(distinct_id, event_name, properties) ``` Output variable of the batch will have `$batched_events_count` and `$batched_events` array of all properties passed in the event payload as shown below: ```json json theme={"system"} { "$batched_events": [ { "name": "Steve", "card_id": "SS-12", "comment": "Hey, added the test cases added for PRD-12" }, { "name": "Olivia", "card_id": "SS-12", "comment": "Hey, done with the testing. Check the bugs" }, { "name": "Joe", "card_id": "SS-12", "comment": "3 bugs are resolved, 4 are still pending" } ], "$batched_events_count": 3 } ``` This is how you'll add the variable in your template to render the desired notification content. ```Text Template theme={"system"} {{$batched_events_count}} comments are added on your task in last 1 hour. {{#each $batched_events}} - {{name}}: {{comment}} {{/each}} ``` ```Text Rendered notification theme={"system"} 3 comments are added on your task in last 1 hour. - Steve: Hey, added the test cases added for PRD-12 - Olivia: Hey, done with the testing. Check the bugs - Joe: 3 bugs are resolved, 4 are still pending ``` You can also test this behaviour via `Enable batching` option in [Mock data](/docs/templates#adding-dynamic-content) button on template details page. Once enabled, you'll start getting `$batched_events` variable in auto suggestion on typing `{{` in template editor. The variables in mock data will be treated as event properties and `Event Count` will imitate the number of times this event will be triggered in the batch. ## Transforming Digest variable output There can be cases where you need to split the digest output variables into multiple arrays based on keys in your input data. e.g., to send a message like `You have got 5 comments and 3 likes on your post today` where post and likes are interaction\_type in your input payload. You can use [data transform node](/docs/data-transform) and generate relevant variables using **JSONNET editor** to handle this use case. Let's take below example. There are 3 post interactions, 2 comments and 1 like and this is your workflow trigger. ```node node theme={"system"} //Event 1 const event_name = "new_post_interaction" const properties = { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" } const event = new Event(distinct_id, event_name, properties) //Event 2 const event_name = "new_post_interaction" const properties = { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } const event = new Event(distinct_id, event_name, properties) //Event 3 const event_name = "new_post_interaction" const properties = { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } const event = new Event(distinct_id, event_name, properties) ``` Without transformation, digest output will look like this: ```json json theme={"system"} { "$batched_events":[ { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" }, { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } , { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } ], "$batched_events_count":3 } ``` We'll add 3 variables in data transform node * `comment_count`: to get the count of all interactions where`interaction_type = comment` * `like_count`: to get the count of all interactions where`interaction_type = like` * `all_comments`: to fetch all array objects where`interaction type = comment` ```json JSONNET syntax to generate above variables theme={"system"} //comment_count std.length([x for x in data["$batched_events"] if x.interaction_type == "comment"]) //like_count std.length([x for x in data["$batched_events"] if x.interaction_type == "like"]) //all_comments [x.comment for x in data["$batched_events"] if x.interaction_type == "comment"] ``` After data transform node, output variables will contain 3 additional keys generated above. You can use these variables in your template to send the desired message as `You have got {{comment_count}} comments and {{like_count}} likes on your post today`. ```json json theme={"system"} { "comment_count":"2", "like_count":"1", "all_comments":["Well written! looking for more such posts","Every leader should read this"], "$batched_events":[ { "name": "Steve", "post_id": "PS-12", "interaction_type":"comment", "comment": "Well written! looking for more such posts" }, { "name": "Olivia", "card_id": "PS-12", "interaction_type":"like" } , { "name": "Joe", "post_id": "PS-12", "interaction_type":"comment", "comment": "Every leader should read this" } ], "$batched_events_count":3 } ``` *** ## Some common notification use cases You can store user preference schedule in their profile and use it in the digest node ->[dynamic schedule](/docs/digest#dynamic-schedule-send-digest-based-on-user-preference). To handle immediate alerts in the same flow, add a branch before the digest node to direct users based on their preference to the digest node. If you are using our preference module. You can create separate sub-categories, for immediate, daily or weekly frequency and create different workflows for each of these frequency. This ensures users have a consolidated view of any alerts they might have missed. Use the [minimum trigger count](/docs/digest#min-trigger-count) to prevent sending a digest if the total number of alerts is below a specified threshold, avoiding unnecessary notifications. Club with fetch node to fetch the latest recommendations before sending the digest. Update the list by removing completed tasks before the next reminder. Stop sending once a certain count of reminder is sent or all the activities are completed. You can add wait until node before digest to handle this use case, where on every task completion, an event is triggered and those events are filtered out based on condition rather than going into digest. ## Frequently Asked Questions Once a given batch has been opened by a workflow trigger, its window interval is immutable. The new schedule will be applicable from the next schedule. An error will occur, and the workflow will terminate. If your use case involves sending both immediate and digest notifications based on user preferences within the same workflow, and you are using a dynamic schedule for passing digest frequency, add a branch before the digest node to send an immediate alert if the schedule is empty or if the schedule variable is missing. When the dynamic schedule key is missing, or resolves to an invalid value, a corresponding error will be logged in workflow executions tab and further workflow execution will be skipped. Triggers before the Time Window start and during its open time (between start time and end time) will be accumulated in the batch. For instance, with a Time Window set from 9:00 AM to 5:00 PM UTC and a 2-hour batch window, triggers from 5:00 PM to 9:00 AM (pre-window open) and 9:00 AM to 11:00 AM (during batch window) will be batched together. So, if you have to create a digest of all events coming outside office hours and send it next day at office start time, you can achieve it with time window and batch node in series with batch open for 5 minutes (just to accumulate all events). *** # DLT Guidelines Source: https://docs.suprsend.com/docs/dlt-guidelines Distributed Ledger Technology (DLT) guidelines for approving and sending SMS in India. ## Distributed Ledger Technology (DLT) The Distributed Ledger Technology (DLT) is a Blockchain based technology used by the Telecom Regulatory Authority of India (TRAI) to check for unsolicited SMSs sent to the end users. It was activated as the Telecom Commercial Communications Customer Preference (TCCCP) Regulations in April, 2021. Post these regulations, principal entities (PEs) can only send SMSs which are registered on the Distributed Ledger Technology (DLT) platform. ## Categories of message templates ### A. Transactional Any message which contains One time Password (OTP) and requires to complete a banking transaction initiated by the bank customer will only fall under this category. This is applicable to all banks (national/Scheduled/Private/Government and even MNC banks) > *Example:* > > * OTP message required for completing a Net-banking transaction. > * OTP message required for completing credit/debit card transaction at a Merchant location. ### B. Service Implicit Any message triggered in response to a user action or arising from his relationship with the sender, that is not promotional, will fall in this category. These messages will not be blocked for subscribers who have otherwise blocked service messages also. Informative SMS and other OTPs fall into this category. > *Example:* > > * Confirmation messages payment transactions, purchase confirmation, delivery status etc. > * OTP messages for payments through Payment Wallet over E-Commerce website, OTP messages for App login > * Periodic balance info, bill generation, bill dispatch, due date reminders, recharge confirmation (DTH, cable, prepaid electricity recharge, etc) > * Messages from schools-attendance/transport alerts. > * Messages from hospitals/clinics/pharmacies/radiologists/pathologists about registration, appointment, discharge, reports. > * Confirmatory messages from app-based services. > * Govt/DOT/TRAI mandated messages. > * Service updates from car workshops, repair shops, gadgets service centres. > * Directory services like Justdial, yellow pages. > * Day-end/month-end settlement alerts to securities/Demat account holders. ### C. Service Explicit These are the messages which requires explicit consent from customer, that has been verified directly from the recipient in robust and verifiable manner and recorded by consent registrar. Any service message which doesn’t fall under service-implicit category. There may not be any need for explicit consent to all other subscribers, who have not blocked service messages > *Example:* > > * Messages to the existing customers recommending or promoting other products or services. > * Re-engagement messages sent to existing customer like "It's been 30 days since you last visited our platform. Visit now and explore our new products" ### D. Promotional Any message sent with an intention to promote or sell a product, goods or service will fall in this category. Service content mixed with promotional content will also be treated as promotional. Explicit consent is not needed to send such messages. > *Example:* > > * Offer messages to new users like > > *"Shop for 3999 and get 10% off on our App. Limited time offer. T\&C. Download the App now\..."* > * Pack Upgrade message to existing customers like "Upgrade to our pro plan. Get credit limit of 1k and pay once in 30 days. Click here...." ## General template validation * Organization name or tenant name must appear in the template. * Transaction Content Template is only available for banks, digital wallets duly permitted/approved by RBI. * Transaction/Service Explicit/Service implicit templates can be created under Alpha-headers. * Promotional templates can be created under Numeric headers. * 2 or more spaces are not supposed to be used between 2 words, before word or after word. * Trans/Service category messages should have variable mandatorily. * Promo category can have complete fixed content or with variable part. * Maximum allowed variable length is 30 characters. Spaces, special and regular characters, all qualify as characters. * All special characters are being allowed currently. * Adding non-english alpha numeric and special characters in message qualify as UNICODE SMS which has a lower character limit per message than TEXT SMS | No of messages | Text characters | Unicode characters | | -------------- | --------------- | ------------------ | | 1 SMS | 160 | 70 | | 2 SMS | 306 | 134 | | 3 SMS | 459 | 201 | | 4 SMS | 612 | 268 | | 5 SMS | 765 | 335 | If Principal entity(PE) uses the name of another entity in their templates, the Telecom service provider (TSP) will register the same on the presumption that there exists a business relationship with that entity without having any accountability to validate the same. Valid proofs and justification if sought pursuant to any complaints by TRAI/PE shall have to be furnished by the registering PE. ## Do's for template content * Use promotional category for communications intended to send from numerical sender id only. * Service–explicit category needs to link consent template as well, without which the template gets rejected. * Values like amount, date, a/c no, merchant names, OTP, codes, URL, customer names, card type, etc. should be replaced with variables. ## DON'TS for template content * Not linking consent templates for content template categories `promotional` & `service – explicit`. * Same content template should not be tagged against multiple headers. * Selecting `Transactional` category by non-banking enterprises. * Using double spaces in templates (this can be pre-checked by verifying the template on any text editor before template submission). * The whole template should not be variable, the customer is required to mention the template content in between the variables. Templates should not be less than 6 char long *** # Email Source: https://docs.suprsend.com/docs/email-quick-start Set up guide to send Email notifications via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period and email notifications can be sent only to verified email addresses (to prevent spam). 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
**When to use additional workspaces?**

Workspaces also help isolate different **product lines or applications**, each with their own users and configurations.
For example, a company like *Meta* might create separate workspaces for Facebook, Instagram, and WhatsApp. ### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger)(note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). **Template** in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Whatsapp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. [Learn more about how to design email template here](/docs/email-template). ![](https://files.readme.io/e5b9db7-template_edit.gif) 1. **Functional nodes (Optional)**: These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can check all methods of triggering workflow [here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**: End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id`within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile first](/docs/users#creating-user-profile-on-suprsend) for event based trigger. In Sandbox environment, a sample user with your registered email ID is pre-created for testing. You can always add more users or edit existing user profile from subscriber page on UI. 2. **Data or Event Properties**: This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can check all trigger methods [here.](/docs/trigger-workflow) **Sample payload for API-based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys](https://app.suprsend.com/en/sandbox/developers/api-keys) ```http curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["[support@suprsend.com]"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": []string{"abc@example.com"}, "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$email", Arrays.asList("abc@example.com")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Test with your Email vendor You have to bring your own vendor to setup email notification in staging and production workspaces. However, you can test your workflows in Sandbox where sample vendors are pre-added. You can clone workflows from one workspace to another. All you have to do is fill in the vendor form for your [respective vendor](/docs/email-vendor-integration) and you are good to go. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Email Template Source: https://docs.suprsend.com/docs/email-template How to design email template using either drag and drop editor or code editor. ## Design Template You can start designing beautiful HTML email with the drag & drop tool provided. You can add variables with Handlebarsjs language. The email template has 3 parts - * **Body** (permanent background), * **Blocks** (each block contains different types of content), * **Content** (add Heading, Images, buttons, tables here). The email designed is responsive, you can see how the template looks on desktop and mobile by clicking on preview bar in bottom left corner. 2482 To add other details (subject, from name, from email, reply to and email markup) click on '`Other Email Settings`'. 1386 ## Email fields description | Field | Description | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Subject | Small text box. Use **`handlebarsjs`** to add variables | | From Name | Display name which tells recipient who sent the email | | From Email | Domain name must be registered with the vendor | | Reply To | Domain name must be registered with the vendor | | cc | *Optional* List of email ids. It is not a part of email design. To be sent with Events data or from Workflow Triggered by API | | bcc | *Optional* List of email ids. It is not a part of email design. To be sent with Events data or from Workflow Triggered by API | | Attachment | *Optional* Not a part of email design. To be sent as a parameters when [Workflow is triggered from Backend](/docs/trigger-workflow#triggering-workflow-via-api). | | Email Markup | *Optional* Schema.org markup which allows to add actions in the email itself. Events, tickets, delivery, one click actions, highlights can be sent in email. Use **`handlebarsjs`** to add variables. | #### What is Email Markup? Google has introduced schema.org markup which lets your Gmail recipients take actions on email itself. You can [check here the list of actions and the sample schema.org that Google allows.](https://developers.google.com/gmail/markup/reference) #### Eligibility for sending Email Markups Before you start sending email markups, you must \[[Register with Google](https://docs.google.com/forms/d/e/1FAIpQLSfT5F1VJXtBjGw2mLxY2aX557ctPTsCrJpURiKJjYeVrugHBQ/viewform?pli=1)] Make sure to go through the [Google Registration Guidelines](https://developers.google.com/gmail/markup/registering-with-google) to make sure your application is successful. Google however allows you to test Markups when you send from and receive email on the same account (e.g. from [support@suprsend.com](mailto:support@suprsend.com) to [support@suprsend.com](mailto:support@suprsend.com) #### Using Markup on SuprSend 1. To use Email Markup on SuprSend, you can add your schema in 'Other Email Settings'. 2. You can take sample schema for your use case from [Google reference guide](https://developers.google.com/gmail/markup/reference) 3. You can add variables in the schema with`handlebarsjs`language 4. When you are in testing phase and your markup request is not approved by Google, make sure your Sender Email id is the same as Recipient Email id. Please note that to send an email, you will need to integrate an Email vendor with SuprSend. Please visit the 'Vendor Integration Guideline' section to see vendors list and how to integrate them. ## Adding dynamic content in email There will always be the case where you would be required to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending email. To send actual values to replace variables at the time of communication trigger, use one of our frontend or backend SDKs. Here is a step by step guide on how to add dynamic content in a template: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing template for any channel. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: **\`\{\{variable\_name}}** Below are some examples of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the 'Templates' section: 1. To enter a nested variable, enter in the format `{{var1.var2.var3}}`. e.g. to refer to city in the example above, you need to enter `{{event.location.city}}` 2. If you have any space in the variable name, enclose it in square bracket `{{event.[first name]}}` 3. To add array names, you need to use an email functionality called **Merge Tags** . Scroll below to the Merge Tags section. ### Auto-suggestions in email If you have defined the variables before hand, they will come as auto-suggestions. This will remove the chances of error like variable mismatch at the time of template rendering. You can get auto-suggestions when you type a curly bracket `{`, or you can use a functionality called 'Merge Tags'. ### Merge Tags Merge tags allow end-users to dynamically add content to their email. Merge tags can be inserted into a block of text by clicking on the "Merge Tags" button in the text editor toolbar. The button is not shown if no variables are defined in the global **Variables** button. This is how 'Merge Tags' looks when you click on a Content block. ### Adding array elements In email, there is a difference on how to add array elements in the template, which can be used to send a list of items which could be dynamic in nature. If you have noticed, you do not get array elements in the drop-down suggestion when you type `{` In order to add array element, you will be required to necessarily use a functionality called **Merge Tags**. Follow these steps to add variable for array element in an email template: Declare an array object in the **`Variables`** button. Below is a sample of variables defined. Note that variable `array` has a list of product items, with product\_name, product\_image, and product\_price as array properties. Save it. Select a **Row** in which you want to add dynamic content of an array. You will see an additional button on the bottom left corner. When you click on it, you will see the list of all arrays that you have declared in the global **Variables** button. Select the array whose variables you want to add in the row. Once selected, you will start seeing the array elements variables in the auto-suggestions. (You can type `{` or click on 'Merge Tags' to get the suggestion values). Select the desired variable, and the row will be repeated for all the elements that come at the time of email trigger. ## Display email blocks based on condition There can be a case where you would want to show or hide some content in the template based on a condition. To achieve this, you can add a display condition on a block and only show the content of that block to the users who satisfy the given condition. One such example could be to show branding watermarks in email templates to free users and not show them to paid users. Here's how you can add display condition to an email block: Display condition can only be applied at a content block level. So, please ensure that you create a separate block for the content that you want to add display condition on * Add a relevant name and description to the condition. * You can add condition in this format `(condition "==" )`. You can use any of the [supported conditional operators](/docs/email-template#supported-conditional-operators) in the conditional statement Once the display condition is created, you can use the same display condition across multiple content blocks. ### Supported conditional operators | Operator | Returns truthy when | Sample Statement | | -------- | ----------------------------------------------- | -------------------- | | `==` | LHS equals RHS | 1 `==` "1" | | `===` | LHS value as well as data type matches with RHS | 1 `===` 1 | | `>` | LHS is greater than RHS | 2 `>` 1 | | `\<` | LHS is less than RHS | 1 `<` 2 | | `>=` | LHS is greater than equals to RHS | 2 `>=` 2 or 3 `>=` 2 | | `\<=` | LHS is less than equals to RHS | 2 `<=` 2 or 1 `<=` 2 | | `!=` | LHS is not equals RHS | 3 `!=` 1 | | `!==` | LHS value or data type does not equal RHS | 1 `!==` "1" | **`OR` condition** For cases where your condition looks like `users belonging to New York or Chicago`, you can add `or `operator in your display condition. The block will be displayed if one of the conditions is true. Conditional statement for above example will look like this: **(or (condition city "==" "New York") (condition city "==" "Chicago"))** You can add as many conditions in the `or` statement as you want. **`AND` condition** For cases where your condition looks like `users with monthly billing >= 100$ and yearly billing >= 1000$`, you can add `and` operator in your display condition. The block will be displayed if all the conditions in `and` statement are true. Conditional statement for above example will look like this: **(and (condition monthly\_bill ">=" 100) (condition annual\_bill ">=" 1000))** You can add as many conditions in the `and` statement as you want. ## Email Preview You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: 1. Make sure you have entered the variable name and the sample value in the**Variables**button. 2. Make sure you have entered the correct variable name in the template, as per the`handlebarsjs`guideline. At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. e.g. If there is an error in rendering Android Push template, but email template is successfully rendered, Android Push notification will not be triggered, but email notification will be triggered by SuprSend. ## Frequently Asked Questions Email clients strip some HTML tags related to styling to script while rendering the email as it may pose security risks. You can use alternative tags to achieve similar output. You can check the supported tags in each email client [here](https://www.campaignmonitor.com/css/). *** # Email Integrations Source: https://docs.suprsend.com/docs/email-vendor-integration SuprSend sends email to your users via reputed email service providers (called Vendors on SuprSend platform). You need to make an account with at least **one** of these providers, and provide API Key of these accounts on SuprSend dashboard. Once this is done, you will be able to send email to your users via SuprSend. All integrations related to sending email, collecting responses, and maintenance will be handled by SuprSend. Whenever you would like to add more email providers, or switch providers, simply add a new email provider on SuprSend dashboard and make it 'Default'; your emails will start going from that provider. SuprSend has done integration with following email providers. These email providers are chosen carefully on the basis of multiple factors, like IP reputation, shared vs dedicated IP, delivery rate, strength of APIs, real-time monitoring, etc. We will be adding more email providers shortly. 1. [SendGrid:](https://sendgrid.com/) Check SuprSend documentation on the integration guidelines [here](/docs/sendgrid) 2. [Amazon SES:](https://aws.amazon.com/) Check SuprSend documentation on the integration guidelines [here](/docs/amazon-ses) 3. [Mailgun:](https://www.mailgun.com/) Check SuprSend documentation on the integration guidelines [here](/docs/mailgun) 4. [Resend:](https://resend.com/) Check SuprSend documentation on the integration guidelines [here](/docs/resend) 5. [Postmark:](https://postmarkapp.com/) Check SuprSend documentation on the integration guidelines [here](/docs/postmark) ## Add email vendors based on workspaces You may have different email vendor accounts for staging and production. Hence, it is optional to add email vendors based on your workspaces. Make sure to add vendor details for each workspace, to avoid failure in communication delivery. ## Add email vendors based on communication category We have given an option to add email vendors based on communication category. At SuprSend, we have defined 3 types of communication categories: System, Transactional and Promotional. You can read more about these categories [here](/docs/notification-category). You can add different vendors for these categories, or have different accounts from the same vendor, depending on your need. *** # Embedded Preference Centre Source: https://docs.suprsend.com/docs/embedded-preference-centre How to integrate a Notification Preference Center into your website and add its link to your notification templates. The Notification Preference Centre is a embedded page inside your application where users can specify the types of notifications they wish to receive and on which channels. With SuprSend's preference management center, you can effortlessly configure preferences. Simply [add notification categories on SuprSend dashboard](/docs/user-preferences#create-notification-category) and get a fully functional preference page that can be seamlessly integrated into your application. This preference page offers a user-friendly interface, enabling users to conveniently manage their notification settings within your application. Rest assured, it remains synchronized with any changes made to notification categories on SuprSend. This means that once integrated, you no longer need to worry about modifying your code to accommodate future updates. Also, user preferences are automatically validated at each workflow run, guaranteeing notifications align with user's latest preference settings. With notification preferences, you can increase user delight by giving them greater autonomy over their notification experience thereby reducing the risk of users turning of all notifications from your platform. ## Integrating preference page into your application With SuprSend, user can set preferences at 3 levels- communication channel, notification category and selected channels inside a category. We provide a headless solution with hooks to read and update data at all 3 levels. We'll also give an example code to add our pre-defined UI. You can do any level of UI customization to match with your brand design. SDKs are available in below languages. We'll be adding support in other languages soon: (Web) ## Adding preference page link in notifications Add Embeddable Preference Page link in tenant settings on SuprSend dashboard. This will automatically create a variable with key `$embedded_preference_url`. You can add this variable in your templates to add preference page link in your notifications. Add this variable `{{$embedded_preference_url}}` in your templates to redirect users to the preference page in your application when users clicks on that link. ![](https://mintlify.s3-us-west-1.amazonaws.com/suprsend/images/docs/0dc752d-image.png) *** # Execution Errors Source: https://docs.suprsend.com/docs/error-guides List of possible errors occurred during workflow or broadcast execution and their resolutions. ## Pre-Requisite [Understanding the data structure of logs](/docs/logging#data-structure-of-logs) ## Error Stages Errors can essentially occur at 4 stages when triggering a notification: * [API Errors](/docs/error-guides#api-errors): These errors occur at API level * [Workflow is not processed](/docs/error-guides#workflow-not-processed): SuprSend could not process the workflow due to issues in the workflow or event payload such as the user's`distinct_id`is not found in the SuprSend database or missing template information. * **Workflow processed, but notification for one or more channels is not triggered by SuprSend**: This type of error occurs from template rendering failures or incorrect/missing channel information in the user profile. Refer[section below](/docs/error-guides#notification-not-triggered-by-suprsend)to debug these errors. * **Vendor didn't accept the sent request or returned delivery failure error**: In such cases, you'll see`Trigger failed`or`Failure by Vendor`status in the third step of the logs. These errors are passed by the vendor when SuprSend initiates a send call to the vendor. Hover over the 'i' icon to view the error message. For detailed troubleshooting, refer to the individual vendor guides. ## API Errors This can happen for two major reasons: | Failure reason | How to solve? | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **2 API requests have the same idempotency key.** SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives more than one request with the same idempotency\_key in a 24 hour window, it will only process the first request and skip all other requests. Know more about idempotency key [here](/docs/python-trigger-workflow-from-api#idempotent-requests). | Try passing another idempotency key in your workflow or event payload. | | **If workflow is triggered via event and there is no active workflow associated with that event.** | Check if there is an active workflow created on SuprSend dashboard with the given event name. If not, [Refer documentation](/docs/workflows#create-workflow) to create workflow on SuprSend dashboard. | If your error isn't covered above, reach out to us on [Slack community](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw) or via email: [support@suprsend.com](mailto:support@suprsend.com) ### Workflow not processed **Workflow errors** Visible inside the workflow tab on logs page. | Failure reason | How to solve? | | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **no user found with distinct\_id: \** | - If you are triggering notification via event, user needs to be created before hand. [Create user profile via API or one of our SDKs](/docs/users#creating-user-profile-on-suprsend). - If you are triggering workflow directly via API, pass user channel information in the user instance of your workflow call. Refer [workflow documentation](/docs/trigger-workflow#triggering-workflow-via-api) of your respective backend SDK. | | **no active/preferred channels found for user with distinct\_id:\** | This error would essentially mean that user doesn't have any applicable active channels in their profile. Applicable channel is the subset of `template group channel`, `channels specified in workflow $channels key`. This could happen for 2 major reasons: (1) User doesn't have that channel set in their profile. If the workflow is triggered via event, user's channel is derived from the existing user profile. [Create user profile via API or one of our SDKs](/docs/users#creating-user-profile-on-suprsend). (2) All identities for that channel are `inactive` in user profile. Channel can be marked inactive using `unset` or `remove` function via [SDK or API](/docs/users#via-sdk). SuprSend also marks the channel identity inactive for email and WhatsApp in case of hard errors from vendor end, such as bounced email addresses, unregistered WhatsApp numbers. This is done to safeguard your email domain authority or WhatsApp rating if you continue to send notifications to users who have reported or marked your email or messages as spam. Additionally, this helps in WhatsApp cost saving, as vendor charges for every processed request. Inactive marking period by SuprSend is 15 days for WhatsApp and 90 days for email. | | **template: live channels: \[channel\_array], but no respective user (subscribed-)channels present** | User profile doesn't have any active channel from the channels available in template group. This could happen for 2 major reasons: - User doesn't have that channel set in their profile. If the workflow is triggered via event, user's channel is derived from the existing user profile. [Create user profile via API or one of our SDKs](/docs/users#creating-user-profile-on-suprsend). - All identities for that channel are `inactive` in user profile. Channel can be marked inactive using `unset` or `remove` function via [SDK or API](/docs/users#via-sdk). SuprSend also marks the channel identity inactive for email and WhatsApp in case of hard errors from vendor end, such as bounced email addresses, unregistered WhatsApp numbers. This is done to safeguard your email domain authority or WhatsApp rating if you continue to send notifications to users who have reported or marked your email or messages as spam. Additionally, this helps in WhatsApp cost saving, as vendor charges for every processed request. Inactive marking period by SuprSend is 15 days for WhatsApp and 90 days for email. | | **TemplateGroup \ not found** | This error occurs when, (1) the template with given template\_slug in not present in your workspace. You can search with `template_slug` using global search `⌘ + K` on dashboard to verify this. (2) there is no published template version available for the channel (3) you have mistakenly passed template name instead of template slug in your workflow payload. You can get template\_slug by clicking on copy icon next to template name on template details page | | **NotificationCategory \ not found** | The notification category passed in workflow payload is not present on SuprSend dashboard or you have mistakenly added category name rather than it's slug. Click on Notification category name to copy the slug. > Please note only published categories can be used in workflows. If you have your category available in draft version, publish the changes before triggering the workflow. Navigate to [Settings -> Notification Categories](https://app.suprsend.com/en/staging/settings/notification-categories) to see all available categories. Click on Category name to copy slug. | | **user not subscribed to category: \** | User has opted\_out from this category. This could happen either if the default user preference for this category is `opt_out` or if the default preference is `opt_in`, then user has explicitly opted\_out of this category. You can get user preference setting for that particular category using [HTTP API](/reference/user-category-preference). | | **user has unsubscribed from all-channels for category: \*\*** | User has opted\_out from this category. This could happen either if the default user preference for this category is `opt_out` or if the default preference is `opt_in`, then user has explicitly opted\_out of this category. You can get user preference setting for that particular category using [HTTP API](/reference/user-category-preference). | | **no (subscribed-)channels found for user. category: \** | User has opted\_out from this category. This could happen either if the default user preference for this category is `opt_out` or if the default preference is `opt_in`, then user has explicitly opted\_out of this category. You can get user preference setting for that particular category using [HTTP API](/reference/user-category-preference). | | **brand doesn't exist with id: \** | The `brand_id` doesn't exist in the given SuprSend Workspace. Check for available brands \[now called tenants] on [SuprSend dashboard -> tenants page](https://app.suprsend.com/en/production/tenants/). You can create this brand/tenant from [SuprSend dashboard or via your backend SDK / API](/docs/tenants#creating--updating-tenant-on-suprsend-platform). | | **NotificationCategory \ not applicable to tenant/brand \** | This means that brand's default preference for the `category_slug` is turned off for `brand_id`. To check default preference setting of each brand \[now called tenant], navigate to [default preferences tab](https://develop.suprsend-app.pages.dev/en/staging/brands/default/brand_preference) on brand details page. | | **recipient: error evaluating jq-expression \, could not be resolved from event properties** | This error occurs when you have overridden the recipient configuration in your workflow, and the jq-expression added in the recipient field is throwing an error or returning null value. To troubleshoot: 1. Navigate to the workflow details page by clicking on the workflow name in the logs. 2. Check the jq-expression added in the recipient field. 3. The recipient jq-expression utilizes event properties JSON for parsing. Validate the output of your jq-expression by adding property JSON [here](https://www.devtoolsdaily.com/jq_playground/). | | **500: xxxx** | SuprSend Internal server error. Reach out to us on [Slack community](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email) for assistance with this issue. | | **all channel identities provided for user are invalid** | This error can arise when user channels are passed as part of your workflow payload and the format of the channel identity is not correct. Supported format for each channel identity: - SMS, Whatsapp - +15555555555 (country code is mandatory) - Email - [support@suprsend.com](mailto:support@suprsend.com)[Refer here](/docs/python-create-user-profile#1-add-user-channels) for other channel formats. | | **user's distinct\_id must be present if it's not transient** | This error occurs if user `distinct_id` key is null or blank in [workflow](/docs/python-trigger-workflow-from-api#trigger-workflow-from-api) or [event](/docs/python-send-event-data#send-event) payload. The `distinct_id` is the unique identifier of the recipient and should always be included in the workflow if you are not sending notifications to anonymous users. If you wish to send notification to an anonymous user without providing the `distinct_id`, include user channels and set `is_transient: True` in the users key of your workflow payload. [Refer doc](/docs/python-trigger-workflow-from-api#sending-notification-to-anonymous-user) for more information on sending notification to anonymous users. | | **users - no valid value passed** | This error occurs when `distinct_id` key is not passed in your [workflow](/docs/python-trigger-workflow-from-api#trigger-workflow-from-api) or [event](/docs/python-send-event-data#send-event) payload. | | **invalid duration-format \[TTL duration]. valid format \[xx]d\[xx]h\[xx]m\[xx]s** | This error occurs when TTL format in the [smart delivery](/docs/smart-delivery) block is incorrect. The correct TTL format is \[xx]d\[xx]h\[xx]m\[xx]s where d = day, h = hour, m = minute, s = second. e.g. - 1d2h30m would indicate a Time to live (TTL) is 1 day 2 hours 30 minutes. | | **invalid duration-format . valid format \[xx]d\[xx]h\[xx]m\[xx]s** | This error occurs when time-duration format in the delay block is incorrect. The correct format is \[xx]d\[xx]h\[xx]m\[xx]s where d = day, h = hour, m = minute, s = second. e.g. - 1d2h30m would indicate a delay of 1 day 2 hours 30 minutes. | | **channel details missing for transient user** | It is essential to pass channel identities in users key if you are sending notification to anonymous user with `is_transient: True` set. You can add channel identity as `"$email":["abc@example.com"]` in your users object. Read more about sending notification to anonymous users [here](/docs/python-trigger-workflow-from-api#sending-notification-to-anonymous-user). | **Broadcast errors** Broadcast logs are visible inside broadcast tab on logs page. | Failure reason | How to solve? | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`SubscriberList Broadcast API: subscribers list is empty for list_id:`** | List doesn't have any users added. You can either sync users programmatically [via SDK / API](/docs/lists#1-programmatically-using-sdk--api) or [directly via csv upload](/docs/lists#3-from-ui-using-csv-upload) from [dashboard -> Lists page](https://app.suprsend.com/en/staging/subscribers/subscriber-list/). | | **`SubscriberList Broadcast API: TemplateGroup are not live** | This error occurs when none of the channels mentioned in broadcast `channels` key is published in the template group. | | **SubscriberList Broadcast API: list\_id: \ not found** | \ doesn't exist in SuprSend. You can create list programmatically [via SDK / API](/docs/lists#1-programmatically-using-sdk--api) or directly by clicking on **"New List"** button on [SuprSend dashboard -> Lists page](https://app.suprsend.com/en/staging/subscribers/subscriber-list/). | | **SubscriberList Broadcast API: trigger\_at must be in ISO 8601 format....** | This error occurs when time-duration format in the `trigger_at` key is incorrect. The correct format ISO 8601 timestamp. e.g. - `2024-01-01T05:30:00Z` indicates that the notification will be triggered at 1 Jan, 2024 5:30 AM UTC. | | **SubscriberList Broadcast API: no vendor configured for channel: \ and category: \ and id-provider: \** | This happens when vendor configuration for the given channel and notification category is missing. Navigate to vendor page on SuprSend dashboard to setup vendor. You can find integration guidelines for all vendors [here](/docs/vendors). | ### Notification not triggered by SuprSend **Workflow errors** Workflow logs are visible inside workflow tab on logs page. | Failure reason | How to solve? | | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`: template rendering failed error: "" not defined in [object Object]`** | Certain variables with `variable_key` exist in the template but are not added in workflow data or event properties. | | **\: template rendering error error: Cannot read properties of undefined (reading '0')** | This error generally occurs when you are using handlebars helpers in your template on a nested object and the variable referenced inside the helper has parent key missing. For instance, if you have `{{default location.city "your place"}}` helper in your template, and the workflow data has `location` key missing, this error will occur. | | **user has unsubscribed from channels \ for category: \** | User has opted\_out from the given channel list for `category_slug`. You can get user preference setting for that particular category using [HTTP API](/reference/user-category-preference). | | **no vendor configured for channel: and category: \ and id-provider:** | This happens when vendor configuration for the given channel and notification category is missing. Navigate to vendor page on SuprSend dashboard to setup vendor. You can find integration guidelines for all vendors [here](/docs/vendors). | **Broadcast errors** Broadcast logs are visible inside broadcast tab on logs page. | Failure reason | How to solve? | | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`SubscriberList Broadcast API: : template rendering failed error: "" not defined in [object Object]`** | Certain variables with `variable_key` exist in the template but not part of broadcast data or user properties if you are using `$user` variable in the template. | | **SubscriberList Broadcast API: template: broadcast applicable live channels: \[channel\_list], but no respective user channels present** | User profile doesn't have any active channel from the channels available in template group. This could happen for 2 reasons: - User doesn't have that channel set in their profile. If the workflow is triggered via event, user's channel is derived from the existing user profile. [Create user profile via API or one of our SDKs](/docs/users#creating-user-profile-on-suprsend). - All identities for that channel are `inactive` in user profile. Channel can be marked inactive using `unset` or `remove` function via [SDK or API](/docs/users#via-sdk). SuprSend also marks the channel identity inactive for email and WhatsApp in case of hard errors from vendor end, such as bounced email addresses, unregistered WhatsApp numbers. This is done to safeguard your email domain authority or WhatsApp rating if you continue to send notifications to users who have reported or marked your email or messages as spam. Additionally, this helps in WhatsApp cost saving, as vendor charges for every processed request. Inactive marking period by SuprSend is 15 days for WhatsApp and 90 days for email. | ## Frequently Asked Questions We currently do not track individual event trigger currently unless it's associated with a workflow. If you don't see an event trigger in logs, check if there are active workflows linked to that event on SuprSend dashboard. Another reason could be if you are using idempotency key and 2 requests have the same idempotency key. SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives another request with the same idempotency\_key in a 24 hour span, it will skip processing those requests. Know more about idempotency key [here](/docs/python-trigger-workflow-from-api#idempotent-requests). Check the format of value passed in user channels. Channel will not be updated in profile if the format is incorrect. Supported format for each channel: * SMS, Whatsapp - `+15555555555` (country code is mandatory) * Email - `abc@example.com` * [Refer here](/docs/python-create-user-profile#1-add-user-channels) for other channel formats. If you are facing an issue which is not covered above, reach out to us on [Slack community](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email) or via email: [support@suprsend.com](mailto:support@suprsend.com) *** # Workflow Trigger: Event vs Workflow API Source: https://docs.suprsend.com/docs/event-vs-api-trigger Compare the methods to trigger workflow within SuprSend. ## Event-Based triggers [Event-streaming](https://docs.suprsend.com/docs/trigger-workflow#event-based-trigger) is a loosely coupled SuprSend Integration where you can publish events to your event bus in a pub-sub model and build associated workflows within SuprSend. Whenever a notification needs to be sent, your system simply pushes an event containing the necessary data, while SuprSend handles all processing—such as determining which user to notify, fetching user profile and their preferences and computing target channels. This approach is ideal for notifications triggered in response to user-initiated actions. **Pros:** * **Decoupled integration**: Just connect your event bus or microservice to SuprSend, and the entire notification logic stays inside SuprSend. * **Single event can trigger multiple workflows.** * **Flexible workflow logic**: You only need to pass the event name, actor, and relevant data. SuprSend determines the appropriate workflow, recipients, and notification channels. * Easily add new notifications without requiring engineering involvement. **Cons:** * **Users must be pre-created**; inline user creation within a workflow trigger isn't supported. Cannot send anonymous notifications. * No immediate response confirming whether the workflow was triggered. * Requires a persistent connection to continuously send and listen for events. * Workflow control is managed via UI, meaning changes can be made without engineering oversight. For critical triggers where failure isn't an option, API-based triggers are recommended for immediate failure response. Event-Based triggers are best suited for event streaming architectures, microservices orchestration, and service buses. ## Workflow API With [API-based triggers](https://docs.suprsend.com/docs/trigger-workflow#triggering-workflow-via-api), you explicitly call a workflow using its workflow-slug, specifying the workflow and recipients directly in the API request. This approach provides tighter integration with SuprSend and is ideal for system-generated workflows, such as sending a daily digest to all users in a company or sending anonymous notifications to unidentified users. It’s also useful for cases where you don't want to setup a separate user sync and just want to pass user information within the trigger. **Pros:** * Recipients and preferences can be [**identified inline**](https://docs.suprsend.com/docs/trigger-workflow#identifying-recipients-inline). * Supports sending to [**anonymous users**](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#sending-notification-to-anonymous-user). * Recommended approach to \*\*send notification to non-user entities like \*\*[**object**](https://docs.suprsend.com/docs/objects) * Ideal for critical notifications, as you receive an immediate response when the workflow is triggered. **Cons:** * Tightly coupled integration: Any changes to notifications or recipient payloads require code-level updates. Workflow API is best for system-generated notifications where you need better control of triggers in your backend, or for sending to anonymous users or non-user entities like object. # Events Source: https://docs.suprsend.com/docs/events How to send events to SuprSend to trigger workflows. The event (or sometimes referred to as event\_name) is a string describing an event. Events are also used to trigger [workflows designed on SuprSend Platform](/docs/trigger-workflow). All you have to do is send the event to SuprSend platform and the associated workflows will be triggered Below is an example of workflow with **'Event Name'** (`GROCERY_PURCHASED`) defined as trigger 577 ## Send Event to SuprSend You can either use [HTTP API](docs/update-user-profile) or our backend SDKs to send events via your backend systems or use Client side SDK to directly sync events from your mobile or web applications. ## Backend SDK: ## Client side SDK: 1. (for web applications) 1. (for mobile applications) 1. (for mobile applications) 1. (for mobile applications) #### Via third-party connectors You can use connectors to directly sync events from your third-party data tracking platforms. *** # Exotel Cloud API Source: https://docs.suprsend.com/docs/exotel Guide to connect your Exotel account with SuprSend to send Whatsapp notifications. ## Pre-Requisites You'll need an Exotel account to complete this tutorial. You can use your existing Exotel account to integrate, or [Create an Exotel account](https://my.exotel.com/auth/register) ## Exotel integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Whatsapp -> Exotel from the list of Vendors. This will open vendor details page as shown below: | Form Field | Obligation | Description | | -------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Nickname** | *mandatory* | You can give any name which may help you to identify this account easily | | **Mobile Number** | *mandatory* | Mobile number of your WhatsApp business account. SuprSend uses this information to send Whatsapp on your behalf via your registered WhatsApp number | | **Login Username** | *optional* | Username of your Exotel WhatsApp dashboard. SuprSend uses this information to upload WhatsApp template in your Exotel dashboard. | | **Login Password** | *optional* | Password of your Exotel WhatsApp dashboard. SuprSend uses this information to upload WhatsApp template in your Exotel dashboard. | | **Account SID** | *mandatory* | Unique identifier of your Exotel account. You'll get it from Exotel dashboard. SuprSend uses this information to send WhatsApp messages via your Exotel account | | **Sub Domain** | *mandatory* | Sub domain of your Exotel account. You'll get it from Exotel dashboard. SuprSend uses this information to send WhatsApp messages via your Exotel account | | **API Key** | *mandatory* | API Key for authenticating the WhatsApp API request. You'll get it from Exotel dashboard. SuprSend uses this information to send WhatsApp messages via your Exotel account | | **API Token** | *mandatory* | API Token for authenticating the WhatsApp API request. You'll get it from Exotel dashboard. SuprSend uses this information to send WhatsApp messages via your Exotel account | | **Price per notification** | *optional* | This is the amount you pay per Whatsapp notification to Exotel. It helps us to calculate, estimate and optimise your cost spent on notifications. | ### How to get API credentials from your Exotel account Login to [Exotel account](https://my.exotel.com/) . On the top navigation panel, you'll see "API Credentials" link. Click on it and you'll find all the relevant information required to add in the vendor configuration form. ## Setting callback URL for tracking Exotel DLR response One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. In case of Exotel, you don't need to set anything for tracking the DLR reports. Exotel allows the option to pass webhook URL in the message payload itself and so DLR tracking will always be enabled on SuprSend dashboard. However, for tracking inbound response, you'll have to add inbound webhook in your Exotel dashboard. You can get the webhook URL at the end of the vendor configuration once you have saved your changes. *** # Fetch Source: https://docs.suprsend.com/docs/fetch Use Fetch node to dynamically fetch data from an API endpoint in workflow. Fetch is an HTTP API request call to an endpoint which you can use to fetch data from an endpoint and use it as template or workflow variables. One of the use case of using fetch node could be to send digest notifications where you can add fetch node before a batch or delay function to fetch digest data and then use it inside template to trigger the notification. Any data returned in the API request response is appended to the response\_key in your fetch function and then merged with the input payload. ## Configuring fetch node In a Fetch node, you have to define the endpoint, query params and headers. You can add both static and dynamic values in all request fields except response key. All Static values are added within `"static value"` and dynamic data is referred as `data.key`. | Field | Description | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Method\* | Only `GET` method is supported in Fetch. | | Endpoint\* | A valid URL endpoint for the `GET` method. Add static URL as `"https://static_url"` and dynamic URL as `{{data.url_endpoint}}`. Data for dynamic URL will be picked from trigger payload. You can also combine static and dynamic part as `"https://domain" + {{data.path}}`. | | Params | Query Params to pass in the request, e.g. (limit 10) to fetch top 10 stories to show in digest notification. Similar to endpoint, you can pass both static and dynamic value in query params. | | Header | Any header to be passed in the request can be added as key-value pair with key being the header type and value as header value. | | Response key | Response of your `GET` request is appended against response key and merged in the workflow payload. Data is merged at parent level if response key is not set. | ### Adding variables in Fetch node Fetch node supports JSONNET rendering language. You can add workflow trigger payload variables as `data.` and internal suprsend data as `data["$brand"].`. Here's a list of variables supported in workflow engine: | Variable | Description | JSONNET format | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | Input Payload | data passed in your event or workflow trigger. | `data.key` | | Actor | properties of actor in your workflow trigger. In case of event, distinct\_id works both as actor and recipient. | `data["$actor"].key` | | Recipient | properties of recipient in your workflow trigger. In case of event, distinct\_id works both as actor and recipient, unless override recipient is set in workflow trigger settings. In case of override recipient, properties of overridden recipient is picked. | `data["$recipient"].key` | | Tenant | properties of the tenant passed in workflow trigger. | `data["$brand"].key` | ### Request execution When executing the request for a fetch function, SuprSend expects the following from your service: * The response to the request is one of: 200 OK, 201 Created, or 204 No Content. * If the request response contains data, it's encoded as JSON and can be decoded into a map/dictionary/hash. * The response to the request takes no longer than 5 seconds for SuprSend to receive. ### Merging response to workflow input If you have added a response key in your setting. The response of the `GET` request is first appended to the response key and then merged to the input workflow data. The response is merged at parent level if response key is empty. *The merged data result from a fetch function step then becomes the global trigger data for all subsequent steps in the workflow run.* Here's how Fetch function data output would look like with response key and without response key ```json json theme={"system"} // node input data { "name":"old_name", "location":{ "city":"old_city" } } //Fetch function response with response key = fetch_response { "org":"new_org", "location":{ "state":"new_state" } } //Merged data with response key = fetch_response { "name":"old_name", "location":{ "city":"old_city" }, "fetch_response":{ "org":"new_org", "location":{ "state":"new_state" } } } //Merged data with empty response key { "name":"old_name", "org":"new_org", "location":{ "state":"new_state" } } ``` *** # Firebase FCM Source: https://docs.suprsend.com/docs/firebase-fcm-androidpush Guide to integrate Firebase Cloud Messaging (FCM) to send Android Push notifications via SuprSend. ## Pre-Requisites Integrate FCM in your application based on your framework: ## Add FCM config on vendor page On the SuprSend dashboard, go to vendor page from side panel and click `Android Push -> FCM` and add service account key file. ### Get service account key file Add Service Account Key json file. You'll get this file from your Firebase Console Project settings -> "Service accounts" tab. *** # Android Integration Source: https://docs.suprsend.com/docs/flutter-android-integration This document will cover integration steps for Android side of your Flutter application. ## Installation Add following line of code inside dependencies in pubspec.yaml file ```yaml pubsec.yaml theme={"system"} dependencies: flutter: sdk: flutter suprsend_flutter_sdk: "^2.4.0" ``` ```shell shell theme={"system"} flutter pub get ``` **Troubleshooting notes:** In case you face compilation errors or warnings, please perform the following troubleshooting steps: * Ensure mavenCentral is present under repositories in project's build.gradle * Perform gradle sync ## Initialization To integrate SuprSend in your Android app, you will need to initialize the suprsend flutter SDK in your MainApplication class. **Note**: **SSApi.init** should only be called in Application class, not inside Activity class(**MainActivity.kt**). If your project does not have an Application class, create it manually and register it in the AndroidManifest. Example: If you create a new Application class named [MainApplication.kt](https://github.com/suprsend/suprsend-flutter-sdk/blob/main/example/android/app/src/main/kotlin/com/suprsend/suprsend_flutter_sdk_example/SuprsendFlutterPluginTestApplication.kt) in your source package, go to your AndroidManifest file and enter the path of the class in the tag like this: ```xml AndroidManifest.xml theme={"system"} ``` ```kotlin MainApplication.kt theme={"system"} package import android.app.Application import app.suprsend.SSApi; // import sdk class MainApplication : Application(){ override fun onCreate() { SSApi.init(this, WORKSPACE KEY, WORKSPACE SECRET) // Important! without this, SDK will not work SSApi.initXiaomi(this, xiaomi_app_id, xiaomi_api_key) // Optional. Add this if you want to support Xiaomi notifications framework super.onCreate() } } ``` Replace **`WORKSPACE KEY`** and **`WORKSPACE SECRET`** with values linked to your account. You'll find it on SuprSend dashboard (Developers -> API Keys) page. Import suprsend SDK in your dart file. Go back to the flutter folder and follow below steps: ```javascript Main.dart theme={"system"} import 'package:suprsend_flutter_sdk/suprsend.dart'; ``` ## Logging By default the logs of SuprSend SDK are disabled. We recommend you to enable the SDK logs by setting its value to VERBOSE. You can enable the logs just in debug mode while in development by below condition. ```javascript Dart theme={"system"} suprsend.setLogLevel(level); suprsend.setLogLevel(LogLevels.VERBOSE); suprsend.setLogLevel(LogLevels.DEBUG); suprsend.setLogLevel(LogLevels.INFO); suprsend.setLogLevel(LogLevels.ERROR); suprsend.setLogLevel(LogLevels.OFF); ``` *** # Android Push Setup (FCM) Source: https://docs.suprsend.com/docs/flutter-androidpush-integration Step-by-step guide to setup FCM Push notifications in flutter android app. Example integration project can be found [here](https://github.com/suprsend/suprsend-flutter-sdk/tree/main/example). ## Integration Steps To start sending notifications from FCM, you'll have to first create a firebase project. Create a firebase project and application in [firebase console](https://firebase.google.com/) with your applications package name which you can find in *`AndroidManifest.xml`* You can get your Service Account JSON from Firebase Console Project Settings. Download *google-services.json* and add the file inside your *android > app* folder. 3.1. Add the below dependency inside projects *`build.gradle`* inside dependencies ```groovy Groovy (build.gradle) theme={"system"} dependencies { ... classpath 'com.google.gms:google-services:4.3.10' // or latest version } ``` ```kotlin Kotlin DSL (build.gradle.kts) theme={"system"} plugins { id("com.google.gms.google-services") version "4.3.10" // or latest version } ``` 3.2. Add the below plugin inside the app *`build.gradle`* ```groovy Groovy (build.gradle) theme={"system"} apply plugin: 'com.google.gms.google-services' ``` ```kotlin Kotlin DSL (build.gradle.kts) theme={"system"} plugins { id("com.google.gms.google-services") } ``` 3.3 Add the below dependency inside apps *`build.gradle`* inside dependencies ```groovy Groovy (build.gradle) theme={"system"} implementation("com.google.firebase:firebase-messaging:22.0.0") // or latest version ``` ```kotlin Kotlin DSL (build.gradle.kts) theme={"system"} dependencies { implementation("com.google.firebase:firebase-messaging:22.0.0") // or latest version } ``` Push feature can be implemented in two ways: You may use this option if all of your android push notifications are to be handled via SuprSend SDK. We recommend you use this method as it is just a single-step process to just register the service in your application manifest and everything else will be ready. ```xml AndroidManifest.xml theme={"system"} ``` Once you get a token from Firebase you can pass the token by using the below code ```javascript main.dart theme={"system"} suprsend.setAndroidFcmPush(fcm_token); ``` When you get a push notification you will get a payload and it can be passed to the method provided by Suprsend Flutter SDK and the notification displaying part will be handled by SDK. ```javascript main.dart theme={"system"} suprsend.showNotification(notification_payload); ``` ## How to identify if notification is sent by SuprSend? If notification payload contains key **supr\_send\_n\_pl** then simply consider this as payload sent from suprsend and pass the payload to suprsend sdk. ### Targeting Android 13 (API-33) In Android13 (API 33) or higher [notification permission](https://developer.android.com/develop/ui/views/notifications/notification-permission) will be disabled by default so permission needs to be asked to enable notifications if you are targeting android 13 users. You can follow [this doc](https://developer.android.com/about/versions/13/setup-sdk) to update to support Andriod 13(API 33), if not already supported. Please test the application as well as upgrading to API 33 may causes breaking changes. ```xml AndroidManifest.xml theme={"system"} ... ``` You can use [permission\_handler](https://pub.dev/packages/permission_handler) or any other package to ask notification permission to user. From v2.4.0, we have removed internal method to ask notification permission (`suprsend.askNotificationPermission`). You can use external package to ask notification permission. Once notification permission is granted, users can be able to see push notifications. *** # Manage Users Source: https://docs.suprsend.com/docs/flutter-create-user Create, update, & manage user profiles and communication channels using Flutter SDK methods. ## How SuprSend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Identify user and set Push token You can identify a user via `suprsend.identify()` method. Call this method as soon as you know the identity of user, that is after login authentication. If you don't call this method, user will be identified using distinct\_id (uuid) that SDK generates internally. When you call this method, we internally create an event called **`$user_login`**. You can use this event to trigger workflow on user login. ```dart Dart theme={"system"} suprsend.identify(_distinct_id_); //Sample suprsend.identify("291eaa13-62f5-4d52-b2dd"); suprsend.identify(10005); ``` | Parameter | Type | Description | | ---------------- | ------------------------- | ----------------------------------------------------------------------------------- | | **distinct\_id** | int, bigint, string, UUID | *mandatory* Unique identifier for a user across devices or between multiple logins. | As soon as the user logs out, call `suprsend.reset()` method to clear data attributed to a user. This allows you to handle multiple user logins on a single device and keep their data isolated from each other. When you call this method, we internally create an event called **\$user\_logout**. You can see this event on SuprSend workflows event list and you can configure a workflow on it. ```javascript Dart theme={"system"} suprsend.reset(); ``` **Mandatory to call reset on logout:** Don't forget to call reset on user logout. If not called, users' distinct\_id will not reset and multiple tokens and channels will get added to the distinct\_id who logged in first on the device. ## Set communication channels Mobile Push tokens automatically gets updated in user profile on `suprsend.identify()` call. All you have to do is to integrate [push notification ](/docs/flutter-androidpush-integration)service in your application to start sending mobile push notification. To set other communication channels, use below methods. Add user channels on signup, or whenever a user updates their communication channels in the application flow. ```dart Dart theme={"system"} suprsend.user.setEmail("abc@example.com"); // To add Email suprsend.user.setSms("+91XXXXXXXXXX"); // To add SMS, mandatory to pass country code suprsend.user.setWhatsApp("+91XXXXXXXXXX"); // To add Whatsapp, mandatory to pass country code ``` Android Push token will automatically be set at the time of user login. All you have to do is to integrate the push notification service in your application. Here's the [Android Push Integration guide](https://docs.suprsend.com/docs/flutter-androidpush-integration). **Country Code Mandatory:** > Make sure you are sending the country code when you are calling communication methods for SMS and Whatsapp. ```javascript javascript theme={"system"} suprsend.user.unSetEmail("abc@example.com"); // To remove Email suprsend.user.unSetSms("+91XXXXXXXXXX"); // To remove SMS, mandatory to pass country code suprsend.user.unSetWhatsApp("+91XXXXXXXXXX"); // To remove Whatsapp, mandatory to pass country code ``` ## Set user properties You can sync other user properties in SuprSend and use it to pass dynamic content in template or add in workflow conditions. Set is used to add property. Set is upsert function and will override existing values corresponding to a key. ```javascript Dart theme={"system"} suprsend.user.set(property_obj); //Example for setting single property suprsend.user.set({"prime_member_group" : "super"}); //Example for setting multiple properties suprsend.user.set({"prime_member_group" : "super"},{"name" : "john doe"}); ``` Property key can't start with **`$`** or **`ss_`**, as this is reserved for our internal events and property names. | Parameters | Type | Description | | -------------- | ------ | --------------------------------------------------------------------------------------------------------------- | | key | string | *Mandatory* This is property key that will be attached to user. Should not start with `$` or `ss_` | | value | any | *Optional* This will be value that will be attached to key property. | | **JSONObject** | object | *Optional* This is used in case of setting multiple user properties. | This will delete property key ```javascript Dart theme={"system"} suprsend.user.unSet(property_list); suprsend.user.unSet(["wishlist"]); suprsend.user.unSet(["wishlist", "cart"]); ``` This method will append a value to an array ```dart theme={"system"} Suprsend.user.append(key, value); Suprsend.user.append(propertyObj); // for multiple properties // Sample values for appending a single property: Suprsend.user.append("wishlist", "iphone12"); // Sample values for appending multiple properties: Suprsend.user.append({ "wishlist": ["iphone12", "Apple airpods"] }); ``` Removes property value but doesn't unset property key. If you want to remove property key use unset method. ```javascript javascript theme={"system"} // remove single property suprsend.user.remove(key, value); suprsend.user.remove("wishlist", "iphone12"); // remove multiple properties suprsend.user.remove(property_obj); suprsend.user.remove({"wishlist" : "iphone12", "wishlist": "Apple airpods"}); ``` Works just like `user.set`, except it will not overwrite existing property values. This is useful for properties like *First login date* ```dart theme={"system"} import 'package:suprsend_flutter/suprsend_flutter.dart'; void main() { // Initialize SuprSend instance final Suprsend suprsend = Suprsend(); // Set a single property once suprsend.user.setOnce("first_login", "2021-11-02"); // Set multiple properties once suprsend.user.setOnce({ "first_login": "2021-11-02", "DOB": "1991-10-02", }); } ``` Increase or decrease integer values on consecutive action, like login count. To reduce a property, provide a negative number for the value. ```dart theme={"system"} // For single property Suprsend.user.increment(key, value); Suprsend.user.increment("login_count", 1); // For multiple properties Suprsend.user.increment(propertyObj); Suprsend.user.increment({ "login_count": 1, "order_count": 1 }); ``` *** # iOS Integration Source: https://docs.suprsend.com/docs/flutter-ios-integration This document will cover integration steps for iOS side of your Flutter application. ## Installation Add following line of code inside dependencies in pubspec.yaml file ```yaml pubspec.yaml theme={"system"} dependencies: flutter: sdk: flutter suprsend_flutter_sdk: "^2.4.0" ``` ```shell shell theme={"system"} flutter pub get ``` SuprSend SDK can be installed in iOS platform version >= 13. Check version in PodFile and upgrade it by running **pod install** inside iOS folder. ```ruby PodFile theme={"system"} platform :ios, '13.0' // this version has to be >=13 ``` SuprSend SDK needs an iOS deployment target >= 11. Open your project in Xcode(*project > ios > project.xcworkspace*) and update the target. ## Initialization Import Suprsend iOS SDK into your application. In `AppDelegate`, add the below code inside `didFinishLaunchingWithOptions` method, just before returning. ```objectivec AppDelegate.swift theme={"system"} import UIKit import Flutter import SuprSendSdk // Add this ... override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) // Add below 2 lines let suprSendConfiguration = SuprSendSDKConfiguration(withKey: "your workspace key", secret:"your workspace secret", baseUrl:nil) SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } ``` Replace **`WORKSPACE KEY`** and **`WORKSPACE SECRET`** with values linked to your account. You'll find this in SuprSend dashboard (Developers -> API Keys) page. ## Logging By default the logs of SuprSend SDK are disabled. We recommend you to enable the SDK logs by setting its value to VERBOSE. You can enable the logs just in debug mode while in development by below condition. ```javascript javascript theme={"system"} suprsend.setLogLevel(level) // level is optional for iOS suprsend.setLogLevel("VERBOSE") suprsend.setLogLevel("DEBUG") suprsend.setLogLevel("INFO") suprsend.setLogLevel("ERROR") suprsend.setLogLevel("OFF") ``` *** # iOS Push Setup Source: https://docs.suprsend.com/docs/flutter-ios-push-integration Step-by-step guide to setup APNS iOSpush notifications in your flutter app. Starting from iOS version v1.0.0, we have introduced explicit push notification permission and option to add images in your notification. Also, introduced background mode for improved tracking of notification delivery. If you are using an iOS version older than v1.0.0 and upgrading to the new version. Please ensure to use the latest integration steps, especially for below methods: 1. [Adding Background mode capability (point 3 of capabilities)](/docs/flutter-ios-push-integration#step-1-add-capabilities-in-ios-application) 2. [Calling registerPush method](/docs/flutter-ios-push-integration#step-2-register-for-push-notification-in-appdelegateswift-file) 3. [Tracking delivery methods](/docs/flutter-ios-push-integration#step-4-enable-sending-and-tracking-of-push-notifications) Example flutter repository with this implementation can be found [here](https://github.com/suprsend/suprsend-flutter-sdk/tree/main/example) 1. Inside Targets select **signing and capabilities** 2. Click on **+capabilities** and select **Push Notifications** and **Background Modes** In Background Modes, select **Remote Notifications** option. We use background notifications to receive delivery reports when your app is in quit and background state. Refer [doc](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app) to know more about background notification Call `registerForPushNotifications` method below the SuprSend SDK initialized code which will register the iOS device for push service ```objectivec AppDelegate.swift theme={"system"} SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: launchOptions) // init code which is already added at time of initialisation var options: UNAuthorizationOptions = [.badge, .alert, .sound] // Add this SuprSend.shared.registerForPushNotifications(options: options) // Add this ``` There are 2 ways in which your app can prompt users to allow push notifications on their devices: Explicit authorization allows you to display alerts, add a badge to the app icon, or play sounds whenever a notification is delivered. In this type of authorization, the request is made the first time user launches your app. If the user denies the request, you can't send subsequent prompts to send the notification. Explicit authorization is default authorization method as it automatically sets alert, sound and badge as soon as the user allows this request. Provisional Authorization (Supported in iOS 12.0 and above) are sent quietly to the users —they don’t interrupt the user with a sound or banner. Also, they will not be shown when your app is in foreground. First time this type of notifications are sent, user is asked to "Keep" or "Turn off" the notifications. Further notifications continue to be sent if they click on "Keep". Add below code in `AppDelegate.swift` file for provisional authorization. ```swift AppDelegate.swift theme={"system"} SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: launchOptions) // init code which is already added at time of initialisation var options: UNAuthorizationOptions = [.badge, .alert, .sound] // If you want to use provisional authorization if #available(iOS 12.0, *) { options = [.badge, .alert, .sound, .provisional] } SuprSend.shared.registerForPushNotifications(options: options) ``` Receiving iOS APNS token sending to backend and listening for push notification and tracking user notification clicks can be done using the following snippet of code. Directly copy and paste it at end of the AppDelegate.swift file inside *AppDelegate* class. ```swift AppDelegate.swift theme={"system"} override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() SuprSend.shared.setPushNotificationToken(token: token) // Send APNS Token to SuprSend } @available(iOS 10.0, *) override func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { if response.isSuprSendNotification() { SuprSend.shared.userNotificationCenter(center, didReceive: response) } completionHandler() } override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]){ SuprSend.shared.application(application, didReceiveRemoteNotification: userInfo) } @available(iOS 10.0, *) override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { completionHandler([.alert, .badge, .sound]) } } ``` iOS Push notifications only work on real devices so while developing/testing use real device to test it instead of simulators. From Xcode-15 push support can be tested in simulators as well. For better notification status (delivered, seen) tracking this step is needed. Then in Next popup give it name to your notification service, select your team, select swift language and click finish. After clicking on `Finish`, a folder will be created with your given product name. Inside that there will be **`NotificationService.swift`** file like below. In your project **`podFile`** add following snippet. Replace `` with name you given to notification service while creating it. After that Run `pod install`. ```yaml PodFile theme={"system"} target '' do use_frameworks! pod 'SuprsendCore' pod 'SuprSendSdk' end ``` Replace the content in **`NotificationService.swift`** file with below code. In this snippet on line 11, 12 replace values with your workspace key and workspace secret. ```swift NotificationService.swift theme={"system"} import UserNotifications import UIKit import SuprSendSdk class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var modifiedNotificationContent: UNMutableNotificationContent? private func track(request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { let suprSendConfiguration = SuprSendSDKConfiguration( withKey: "your workspace key", secret: "your workspace secret" ) SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: [:]) SuprSend.shared.didReceive(request, withContentHandler: contentHandler) } override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler modifiedNotificationContent = (request.content.mutableCopy() as? UNMutableNotificationContent) track(request: request, withContentHandler: contentHandler) if let modifiedNotificationContent = modifiedNotificationContent { // Modify the notification content here... // 1 guard let imageURLString = modifiedNotificationContent.userInfo["image_url"] as? String else { contentHandler(modifiedNotificationContent) return } getMediaAttachment(for: imageURLString) { [weak self] image in guard let self = self, let image = image, let fileURL = self.saveImageAttachment( image: image, forIdentifier: "attachment.png") else { contentHandler(modifiedNotificationContent) return } let imageAttachment = try? UNNotificationAttachment( identifier: "image", url: fileURL, options: nil) if let imageAttachment = imageAttachment { modifiedNotificationContent.attachments = [imageAttachment] } contentHandler(modifiedNotificationContent) } } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = modifiedNotificationContent { contentHandler(bestAttemptContent) } } } extension NotificationService { private func saveImageAttachment(image: UIImage, forIdentifier identifier: String ) -> URL? { let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()) let directoryPath = tempDirectory.appendingPathComponent( ProcessInfo.processInfo.globallyUniqueString, isDirectory: true) do { try FileManager.default.createDirectory( at: directoryPath, withIntermediateDirectories: true, attributes: nil) let fileURL = directoryPath.appendingPathComponent(identifier) guard let imageData = image.pngData() else { return nil } try imageData.write(to: fileURL) return fileURL } catch { return nil } } private func getMediaAttachment(for urlString: String, completion: @escaping (UIImage?) -> Void ) { // 1 guard let url = URL(string: urlString) else { completion(nil) return } let task = URLSession.shared.dataTask(with: url) { data, response, error in if error != nil { completion(nil) return } guard let data = data else { completion(nil) return } guard let image = UIImage(data: data) else { completion(nil) return } completion(image) } task.resume() } } ``` Runner Target inside Build Phases, drag **`Embedded Foundation Extensions`** section and drop it below the**`Copy Bundle Resources`** section like in image. You are now all set to send push notifications. All you have to do is add iOS vendor configuration on SuprSend dashboard and your push notifications will be configured. Please refer [vendor integration guide](/docs/ios-push-vendor-integration) to integrate your apns push service. *** # Sync Events Source: https://docs.suprsend.com/docs/flutter-send-event-data Methods for sending events from your flutter app to the SuprSend platform to trigger workflows. ## Pre-Requisites [Create User](/docs/flutter-create-user) -Mandatory to pass in event trigger ## Sending events to SuprSend You can setup events on user actions in your app and configure workflows on top of it that triggers when the corresponding event is passed through App. Variables added in the template or workflow should be passed as event `properties` You can send Events from your app to SuprSend platform by using `suprsend.track()` method. ```javascript Dart theme={"system"} suprsend.track(event_name, property_obj); suprsend.track("clicked", {"page":"Dashboard","city":"Bangalore"}); ``` Event Name or Property Name should not start with **`$`** or **`ss_`**. These keywords are reserved for internal events and property names. ### System events tracked by SuprSend There are some system events tracked by SuprSend SDK by default. These are some basic events, as well as events that are necessary for tracking notifications related activity (like delivered, clicked, etc). You are not required to do anything here. | Event Name | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \$app\_installed | \$app\_installed will get tracked when user launch his app for the first time. FYI cases in which it will also get called 1. When user launches his app for first time. 2. When user uninstall the app and installs it again. 3. \[Multiple device login ]When user launch app for first time on different devices. 4. When user clears the app cache and relaunches the app. | | \$app\_launched | Gets tracked when user launches the app each time. | | \$user\_login | Gets tracked when user logs in inside the app | | \$user\_logout | Gets tracked when user logs in to the app | | \$notification\_delivered | Will get tracked when the suprsend notification payload is received at SDK end. | | \$notification\_clicked | Will get tracked when user either clicks the notification body or any action button in the notification. | | \$notification\_dismissed | Will get tracked when user dismisses the notification by left swiping the notification or by clicking on "Clear All" button | ## Advanced Concepts ### 1. Super Properties Super Properties are data that are always sent with events data. These super properties will be sent in each event after calling this method. Super Properties will be stored in local storage, and will persist across invocations of app. There are some super properties that SuprSend SDK will send by default. Developer can set custom super properties as well with `suprsend.setSuperProperties()` method ```javascript Dart theme={"system"} suprsend.setSuperProperties(property_obj); //setting single super property suprsend.setSuperProperties({"Location": "San Francisco, CA"}); // setting multiple super properties suprsend.setSuperProperties({"Location": "San Francisco, CA","Zipcode": 940167}); ``` Default Super Properties tracked by SuprSend SDK: | Super Property | Description | Sample Value | | ---------------------- | -------------------------------------------- | ---------------- | | \$app\_version\_string | Version of your app | 0.0.1 | | \$app\_build\_number | Build number of your app | 2 | | \$os | Operating system of the user | android | | \$manufacturer | Manufacturer of the user's device | OnePlus | | \$brand | Brand of the user's device | OnePlus | | \$model | Model of the user's device | GM1901 | | \$deviceId | Device id | 89eead05a0150146 | | \$ss\_sdk\_version | SuprSend SDK version | 0.1.31 | | \$network | Network on which the user is | wifi | | \$connected | Whether the user is connected to the network | true | There are unset custom super properties with `suprsend.unSetSuperProperty()` method. This method will stop calling that property with every event trigger. ```javascript Dart theme={"system"} suprsend.unSetSuperProperty(key); //unsetting single super property suprsend.unSetSuperProperty({"Location"}); // unsetting multiple super properties suprsend.unSetSuperProperty({"Location","Pincode"}); ``` ### 2. Flush events SuprSend SDK automatically flushes events at an interval of 5 seconds, and on certain activities like app relaunch, etc. If you wish to flush a time sensitive event to SuprSend immediately, you can use the `suprsend.flush()` method. All the system tracked events are flushed immediately ```javascript Dart theme={"system"} suprsend.flush(); ``` *** # Manage User Source: https://docs.suprsend.com/docs/go-create-user-profile Manage user profiles and communication channels programmatically with the Go SDK. ## How Suprsend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Create User To create a new user or to update an existing user, you'll have to fetch user instance. Call `supr_client.user.get_instance` to instantiate user object. ```go theme={"system"} user := suprClient.Users.GetInstance("_distinct_id_") // Unique identifier of user in your application // Save user _, err = user.Save() if err != nil { log.Fatalln(err) } ``` ## Edit User To Edit user, you need to first fetch user instance, call all the update methods and save changes using `users.Save()` method. ```go Request theme={"system"} func main() { // Fetch user instance user := suprClient.Users.GetEditInstance("_distinct_id_") // Call user update methods user.SetTimezone("America/Los_Angeles") user.Set("name", "John Doe") // Save Changes res, err := user.Save() if err != nil { log.Fatalln(err) } fmt.Println(res) } ``` ```go Response theme={"system"} { "success": boolean, "status": "success"/"fail", "status_code": http_status_code, "message": "response_message"/"error_message", } ``` Here's a list of all edit methods: Add communication channels on which you want to notify user. Push sand Inbox tokens are automatically tracked on user identification when the corresponding frontend SDK is integrated. Other channels (Email, SMS, Slack, MS teams, Whatsapp) need to be explicitly set in user profile. Use `user.Add*` method(s) to add user channels. ```go Request theme={"system"} // Add email channel user.AddEmail("user@example.com") // add SMS channel user.AddSms("+1444455555") // Add WhatsApp channel user.AddWhatsapp("+1444455555") // Add androidpush token, token providers: fcm/xiaomi user.AddAndroidpush("__fcm_push_token__", "fcm") // Add iospush token, token providers: apns user.AddIospush("__ios_push_token__", "apns") // Add webpush token (vapid) user.AddWebpush(map[string]interface{}{ "keys": map[string]interface{}{ "auth": "", "p256dh": "", }, "endpoint": "", }, "vapid") ``` Use `user.Remove*` method(s) to remove channels. ```go Request theme={"system"} // remove email channel user.RemoveEmail("user@example.com") // remove SMS channel user.RemoveSms("+1444455555") // remove WhatsApp channel user.RemoveWhatsapp("+1444455555") // remove androidpush token user.RemoveAndroidpush("__fcm_push_token__", "fcm") // remove iospush token user.RemoveIospush("__ios_push_token__", "apns") // remove webpush token user.RemoveWebpush(map[string]interface{}{ "keys": map[string]interface{}{ "auth": "", "p256dh": "", }, "endpoint": "", }, "vapid") ``` This method will delete/unset all values in specified channel for user (ex: remove all emails attached to user). ```go theme={"system"} // If you need to remove all emails for this user, call user.Unset(["$email"]) user.Unset([]string{"$email"}) // Supported channel keys are: // $email, $whatsapp, $sms, $androidpush, $iospush, $webpush, $slack, $ms_teams ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```go theme={"system"} // Set user preferred language. languageCode must be in [ISO 639-1 2-letter] format user.SetPreferredLanguage("en") ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```go theme={"system"} user.SetTimezone("America/Los_Angeles") ``` Set is used to add custom user properties. It is an upsert function, meaning any existing property value with the same key will be overwritten on subsequent updates. ```go theme={"system"} user.Set("key", "value") user.Set("name","John Doe") user.Set(map[string]interface{}{"prop1": "val1", "prop2": "val2"}) user.Set(map[string]interface{}{"name": "John Doe","city": "San Francisco"}) ``` Unset is used to remove a property key. ```go theme={"system"} user.Unset([]string{"$email"}) ``` This method will append a value to the array list. ```go theme={"system"} user.Append("key", "value") user.Append("played_games", "game_1") user.Append(map[string]interface{}{"key1": "val1", "key2": "val2"}) user.Append(map[string]interface{}{"played_games": "game_1", "liked_games": "game_2"}) ``` This method will remove a value from the array list. ```go theme={"system"} user.Remove("key", "value") user.Remove("played_games", "game_1") user.Remove(map[string]interface{}{"key1": "val1", "key2": "val2"}) user.Remove(map[string]interface{}{"played_games": "game_1", "liked_games": "game_2"}) ``` Works just like user.Set, except it will not override already existing property values. This is useful for properties like first\_login\_date. ```go theme={"system"} user.SetOnce(key, value) user.SetOnce("first_login","2021-11-02") user.SetOnce(map[string]interface{}{ key1: value1, key2: value2 }) user.SetOnce(map[string]interface{}{"first_login": "2021-11-02","signup_date": "2021-11-02"}) ``` Increase or decrease integer values on consecutive action, like login count. To reduce a property, provide a negative number for the value. ```go theme={"system"} user.Increment("login_count", 1) user.Increment(map[string]interface{}{"login_count" : 1, "order_count" : 1}) ``` After calling `Add/Remove/Unset` methods, don't forget to call `user.Save()` since user edit is async update and the changes will be sent to SuprSend only after calling this method. ## Bulk Update Users There isn't any limit on number-of-records that can be added to `bulk_users` instance. Use `.Append()` on bulk\_users instance to add however-many-records to call in bulk. ```go Request theme={"system"} // create bulkUsers instance bulkIns := suprClient.BulkUsers.NewInstance() // Prepare user1 user1 := suprClient.Users.GetInstance("__distinct_id1__") user1.AddEmail("user1@example.com") user1.AddWhatsapp("+1909090900") // prepare user 2 user2 := suprClient.Users.GetInstance("__distinct_id2__") user2.AddEmail("user2@example.com") user2.AddWhatsapp("+2909090900") // Append all users to bulk instance bulkIns.Append(user1, user2) // Call save bulkResponse, err := bulkIns.Save() if err != nil { log.Fatalln(err) } log.Println(bulkResponse) ``` ```go Response theme={"system"} { Status : "success", Total : 10, // number of records sent in bulk Success : 10, // number of records succeeded Failure : 0, // number of records failed FailedRecords : [], } { Status : "fail", Total : 10, // number of records sent in bulk Success : 0, // number of records succeeded Failure : 10, // number of records failed FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } ``` ## Get user details ```go Request theme={"system"} func main() { // Fetch user instance res, err := suprClient.Users.Get("_distinct_id_") if err != nil { log.Fatalln(err) } fmt.Println(res) } ``` ```go Response theme={"system"} { "distinct_id": "_distinct_id_", "properties": { "name": "John Doe", "email": ["johndoe@example.com"], "created_at": "2024-01-01T12:00:00Z" }, "status": "active" } ``` ## Delete user ```go Request theme={"system"} func main() { // Delete user instance res, err := suprClient.Users.Delete("_distinct_id_") if err != nil { log.Fatalln(err) } fmt.Println(res) } ``` ```go Response theme={"system"} { "success": True, "status_code": 204 } ``` ## Get list of objects subscribed by user You can pass optional query parameters -`limit`, `before`, `after` ```go Request theme={"system"} func main() { // Get objects the user is subscribed to params := map[string]interface{}{ "after": "01JJW6HXXXXPB59ARDW85G0KN", "limit": 1, } res, err := suprClient.Users.GetObjectsSubscribedTo("_distinct_id_", params) if err != nil { log.Fatalln(err) } fmt.Println(res) } ``` ```go Response theme={"system"} { "objects": [ { "object_id": "Frontend", "type": "Developers", "subscribed_at": "2024-02-20T10:15:30Z" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": false } } ``` ## Get lists subscribed by user You can pass optional query parameters -`limit`, `before`, `after` ```go Request theme={"system"} func main() { // Get lists the user is subscribed to params := map[string]interface{}{ "after": "01JJW6HXXXXPB59ARDW85G0KN", "limit": 1, } res, err := suprClient.Users.GetListsSubscribedTo("_distinct_id_", params) if err != nil { log.Fatalln(err) } fmt.Println(res) } ``` ```go Response theme={"system"} { "lists": [ { "list_id": "product_updates", "subscribed_at": "2024-02-20T12:00:00Z", "status": "subscribed" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": true } } ``` # Go-live checklist Source: https://docs.suprsend.com/docs/go-live-checklist Checklist to ensure a smooth transition of your notifications from staging to production, covering pre-launch preparations, testing, and post-launch monitoring In SuprSend, workspaces are logically isolated and share no data by default. This means, all your assets (workflows, templates, users, preferences) and config files (vendor, API Keys) are independently set in workspaces and won't be shared across workspaces. Also check our [best practices](/docs/best-practices-notification-system-design) to design an effective notification system for your use case. ## Pre-Go-Live preparation * Add [vendor settings](/docs/vendors) in your production workspace for the channels you plan to use for notifications. * Set the API Keys, workspace key, and secret for your production workspace in both client-side and server-side applications. * For Inbox integration, update the Inbox secret in your server-side code to generate user [HMAC](/docs/hmac-authentication) for the production environment. You can obtain this secret from the [Inbox vendor configuration page](https://app.suprsend.com/en/staging/vendors/inbox/suprsend-inbox?brand_id=default) on the SuprSend dashboard. Add SuprSend webhook url in respective vendor portals for SMS, Email and Whatsapp to track delivery metrics (delivered, click, seen) in logs and analytics. Only required if you are using event based workflow triggers You can [setup user sync](/docs/users#creating-user-profile-on-suprsend) from your frontend application or via backend systems. The best way to keep user data updated at all times is to setup a sync when user profile is created or updated in your database. Be cautious of large user syncs just before triggering notifications, as users are updated in async manner and it may result in some users not being synced in time and eventually getting missed from final notifications. Test your workflow changes using test runner with actual trigger data to make sure that template content and workflow logic is working as expected. Some common issues to check: * URLs and text with special characters should be enclosed in triple curly braces`{{{...}}}`to avoid random text added in final template due to HTML character escaping. * Adding batch node in workflow also changes the output variables structure and the template should also be modified to include batched variables output. See how to add [batched variables in templates](/docs/batch#using-batch-variables-in-templates). * Remember to commit all template and workflow changes, as uncommitted edits will not be used when triggered from backend. * Use [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. You'll need to push template, workflow, preference changes from staging to production workspace. You can do this by cloning your assets between workspaces. You'll get clone option next to each asset that you created. You can also push your changes in an automated manner as feature release using CLI (coming soon...) ## Post-Go-Live checks Check Analytics to track engagement metrics (open, seen, click) and use it to optimize workflow and templates over time. You can see analytics for each workflow inside workflow details page and overall analytics on the [home page](https://app.suprsend.com/en/sandbox/). You can refer to all the [statuses tracked per channel](/docs/logging#notification-statuses-tracked-per-channel) here. Regularly check the [Logs page](https://app.suprsend.com/en/staging/logs/api?last_n_minutes=1440) for the first 3-4 days after going live. Look for any anomalies or critical errors and address them promptly. Logs are divided in 3 sections: * **Requests**: All the requests sent to SuprSend from your backend or Frontend systems are logged here. You can see the input payload and request response here. * **Executions**: Workflow executions are logged in this tab. You can click on each log in this tab to see step-by-step debugger of the workflows. * **Messages**: This is where all delivery nodes and their sent message status (delivery, seen, clicked) can be tracked. You'll also be able to see message preview of your delivered messages here (coming soon). For any assistance in fixing errors, our [Slack Support Channel](https://join.slack.com/t/suprsendcommunity/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw) is available 24/7. *** # Send and Track Events Source: https://docs.suprsend.com/docs/go-send-event-data Learn how to send events to trigger workflows, with code snippets and examples. ## Pre-Requisites [Create User Profile](/docs/go-create-user-profile) ## Send Event You can send event to Suprsend platform by using the `supr_client.track_event` method. When you call `supr_client.track_event`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. The actual processing/execution of event happens asynchronously. ```go Request theme={"system"} ev := &suprsend.Event{ EventName: "__event_name__", DistinctId: "_distinct_id_", Properties: map[string]interface{}{ "k1": "v1", }, IdempotencyKey: "_unique_request_id_" } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` ```go Sample Code theme={"system"} ev := &suprsend.Event{ EventName: "product_purchased", // Mandatory, name of the event you're tracking DistinctId: "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", // Mandatory, Unique id of user in your application Properties: map[string]interface{}{ "first_name": "User", "spend_amount": "$10", "nested_key_example": map[string]interface{}{ "nested_key1": "some_value_1", "nested_key2": map[string]interface{}{ "nested_key3": "some_value_3", }, }, IdempotencyKey: "abcd-1234-abc6" } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` ```go Response theme={"system"} // Response structure { "success": True, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } { "success": False, // error will be present in message "status": "fail", "status_code": 500, // http status code "message": "error message", } ``` | Parameter | Description | Type | Obligation | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | | `DistinctId` | `distinct_id` of user performing the event | string | *mandatory* | | `EventName` | string identifier for the event like `product_purchased` | string | *mandatory* | | `Properties` | a dictionary representing event attributes like `first_name` Event properties can be used to pass template variables in case of event based trigger | Dictionary | *optional* | | `IdempotencyKey` | `idempotency_key` is the unique identifier for an event, and expires in 24 hours. | string | optional | **Event naming guidelines:** Event Name or Property Name should not start with or , as we have reserved these symbols for our internal events and property names. ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. You can use this key to track webhooks related to workflow notifications. Idempotency key should be unique that you generate for each request. You may use any string up to 255 characters in length. Any space at start and end of the key will be trimmed. Here are some common approaches for assigning idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ## Add file attachment in event (for email) To add one or more Attachments to a Notification (viz. Email), you can just append the filepath of attachment to the event instance. * Call `event.AddAttachment()` for each file with an accessible URL. * Ensure that file\_path is a publicly accessible URL. Since event API size can't be > 100 KB, local file paths can't be passed in event attachment. ```go Request theme={"system"} ev := &suprsend.Event{ EventName: "invoice_generated", DistinctId: "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", Properties: map[string]interface{}{...} } // Add attachment (If needed) by calling .AddAttachment err = ev.AddAttachment("https://www.adobe.com/sample_file.pdf") if err != nil { log.Println(err) } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` ```go Response theme={"system"} # Response structure { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } { "success": false, // error will be present in message "status": "fail", "status_code": 500, // http status code "message": "error message", } ``` Please add public accessible URL only as attachment file otherwise it will throw an error `404 not found` and workflow will not be triggered ## Bulk API for multiple event requests You can use Bulk API to send multiple events. Use .Append() on bulk\_events instance to add however-many-records to call in bulk. ```go Request theme={"system"} ev1 := &suprsend.Event{ EventName: "__event_name1__", DistinctId: "__distinct_id1__", Properties: map[string]interface{}{ "k1": "v1", } } ev2 := &suprsend.Event{ EventName: "__event_name2__", DistinctId: "__distinct_id2__" } // Create bulkEvents instance bulkIns := suprClient.BulkEvents.NewInstance() // Add all events to bulk Instance bulkIns.Append(ev1, ev2) // call trigger to send all these events to SuprSend bulkResponse, err := bulkIns.Trigger() if err != nil { log.Fatalln(err) } log.Println(bulkResponse) ``` ```go With Email attachment theme={"system"} ev1 := &suprsend.Event{ EventName: "__event_name1__", DistinctId: "__distinct_id1__", Properties: map[string]interface{}{ "k1": "v1", } } //Add Email Attachment to event 1 err = ev.AddAttachment("https://file_path_1.pdf") if err != nil { log.Println(err) } ev2 := &suprsend.Event{ EventName: "__event_name2__", DistinctId: "__distinct_id2__" } //Add Email Attachment to event 2 err = ev.AddAttachment("https://file_path_2.pdf") if err != nil { log.Println(err) } // Create bulkEvents instance bulkIns := suprClient.BulkEvents.NewInstance() // Add all events to bulk Instance bulkIns.Append(ev1, ev2) // call trigger to send all these events to SuprSend bulkResponse, err := bulkIns.Trigger() if err != nil { log.Fatalln(err) } log.Println(bulkResponse) ``` ```go Response theme={"system"} suprsend.BulkResponse{ Status : "success", Total : 10, // number of records sent in bulk Success : 10, // number of records succeeded Failure : 0, // number of records failed FailedRecords : [], } suprsend.BulkResponse{ Status : "fail", Total : 10, // number of records sent in bulk Success : 0, // number of records succeeded Failure : 10, // number of records failed FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } suprsend.BulkResponse{ Status : "partial", Total : 10, // number of records sent in bulk Success : 6, // number of records succeeded Failure : 4, // number of records failed FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } ``` *** # Trigger Workflow from API Source: https://docs.suprsend.com/docs/go-trigger-workflow-from-api Learn how to trigger workflows using direct workflow API, with code snippets and examples. It is a unified API to trigger workflow and doesn't require user creation before hand to trigger notification. Recommended for platforms transitioning their existing notifications to SuprSend. If you are using our frontend SDKs to configure notifications and passing events and user properties from third-party data platforms like Segment, then [event-based trigger](/docs/go-send-event-data) would be a better choice. Available in SDK version >= v0.5.1 ## Payload Schema ```go Sample Payload theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err: = suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient * suprsend.Client) { // Create WorkflowRequest body wfReqBody: = map[string] interface {} { "workflow": "_workflow_slug_", "actor": map[string] interface {} { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true, }, "recipients": [] map[string] interface {} { // notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": [] string { "abc@example.com" }, "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true, }, // notify object { "object_type":   "teams", "id": "finance", "$skip_create": true, }, }, // data can be any json/serializable map structure "data": map[string] interface {} { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234", "spend_amount": "$10", }, } w1: = & suprsend.WorkflowTriggerRequest { Body: wfReqBody, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err: = suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```go Response theme={"system"} { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } ``` To prevent automatic creation of an actor, or recipient (user/object) in SuprSend (the case where they already exist in your system), you can use the `"$skip_create": true` flag. This can be applied inside the actor, individual user recipient objects, or object recipient objects. | Property | Type | Description | | ----------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | workflow | string | Slug of designed workflow on SuprSend dashboard. You'll get the slug from workflow settings. | | actor (*optional*) | string / object | Includes distinct\_id and properties of the user who performed the action. You can use it for [cross-user notifications](/docs/go-trigger-workflow-from-api#sending-cross-user-notifications) where you need to include actor properties in notification template. Actor properties can be added as `$actor.`. | | recipients | array of string / array of objects | List of users or objects who need to be notified. You can add up to 100 recipients in a workflow trigger. You can either pass recipients as an array of `distinct_ID` (if user is pre-synced in SuprSend database) or [define recipient information inline](/docs/go-trigger-workflow-from-api#identifying-recipients-inline). | | data | object | variable data required to render dynamic template content or workflow properties such as dynamic delay or channel override in send node. | | tenant\_id | string | unique identifier of the [brand / tenant](/docs/tenants) | | idempotency\_key | string | unique identifier of the request. We'll be returning idempotency\_key in our [outbound webhook response](/docs/outbound-webhook). You can use it to map notification statuses and replies in your system. | | recipients\[].\$timezone | string | to set recipient's timezone. Used to send notification in user's local timezone. You can pass timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | recipients\[].\$preferred\_language | string | to set recipient's preferred language. This is to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) . | ### Sending notification to multiple recipients Recipients in workflow call is an array of `distinct_ids` or recipient objects. You can pass up to 100 recipients in a single workflow trigger. SuprSend will internally convert it into multiple workflow triggers, one for each recipient in the array. ```go Request theme={"system"} "recipients": []map[string]interface{}{ { "distinct_id": "id1", "$email":["abc@example.com"], "name":"recipient_1" }, { "distinct_id": "id1", "$email":["abc@example.com"], "name":"recipient_2" } } ---- OR ------ "recipients": []string{"id1","id2"} ``` **Use lists to broadcast to a large list of users:** We recommend you to use [lists](/docs/lists) and [broadcasts](/docs/broadcast) to send notifications to a user list larger than 1000 users. This approach allows for bulk processing within SuprSend, resulting in significantly faster delivery compared to individual workflow calls. Sending individual workflows to a large set of users may introduce delays in your notification queue and is not an optimized way of handling bulk trigger. ### Identifying recipients inline One of the benefits of using direct workflow trigger is that you can identify recipients inline. You can include recipient channel information, their channel preferences, and their user properties along with the workflow trigger. Upon triggering the workflow, the recipient will be automatically created in the SuprSend database in the background. This facilitates dynamic synchronization of your user data within SuprSend and eliminates the need for any migration efforts on your end to start sending notifications from SuprSend. You can also use recipient properties in your template as `$recipient.`. This is how the complete recipient object with look like ```json Request theme={"system"} "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "$channels":["email","inbox"], "user_prop1":"value_1", "$preferred_language":"en", "$timezone": "America/New_York" } } ``` | Property | Type | Description | | --------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | distinct\_id | string | Unique identifier of the user to be notified. | | `$` like `$email`, `$sms` etc. | array of string / objects | You can pass user channel information using `$` key. The channel info will be updated to user profile in the background. For this workflow, only channel values specified in this key will be used for sending notification (instead of all channel values present in User profile). Refer how different communication channels can be passed [here](/docs/go-trigger-workflow-from-api#add-user-communication-channel) | | \$channels | array of string | Use it to pass user's channel preference in the payload. You can always use our [in-build preference APIs](/docs/user-preferences) to maintain user notification preferences. Preferences defined within SuprSend will automatically apply with workflow trigger. By default, notifications will be sent to all channels defined in the workflow delivery node. However, if you have a scenario where user has specific channel preference for a notification (e.g. they only want to receive payment reminders via email), you can include that preference in the workflow payload. This will ensure that notifications are sent only to the channels specified in the \$channels key. The supported channel values are `email, sms, whatsapp, androidpush, iospush, slack, webpush, ms_teams`. | | \$preferred\_language | string | to set recipient's preferred language. This is to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) . | | \$timezone | string | to set recipient's timezone. Used to send notification in user's local timezone. You can pass timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | \* | key-value pair | You can pass other user properties to render dynamic template content in key-value pair as `"user_prop1":"value1"` . Extra properties will be set in subscriber profile (as subscriber properties) which can then be used in the template as `$recipient.`. | #### Add user communication channel You can add channels in recipient map as shown below: ```go Request theme={"system"} "$email":["abc@example.com"], "$whatsapp":["+15555555555"], "$sms":["+15555555555"], "$androidpush": map[string]interface{}{"token": "__android_push_token__", "provider": "fcm"}, "$iospush":map[string]interface{}{{"token": "__ios_push_token__", "provider": "apns"}, "$slack": map[string]interface{}{ "email": "abc@example.com", "access_token": "xoxb-XXXXXXXX" } // slack using email "$slack": map[string]interface{}{ "user_id": "U/WXXXXXXXX", "access_token": "xoxb-XXXXXX" } // slack using member_id "$slack": map[string]interface{}{ "channel": "CXXXXXXXX", "access_token": "xoxb-XXXXXX" } // slack channel "$slack": map[string]interface{}{ "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } } // slack incoming webhook "$ms_teams": map[string]interface{}{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" } // MS teams user or channel using conversation_id "$ms_teams": map[string]interface{}{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" } // MS teams user using user_id "$ms_teams": map[string]interface{}{ "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } } // MS teams incoming webhook ``` ### Sending cross-user notifications In scenarios where you need to notify a group of users based on another user's action, such as sending a notification to the document owner when someone comments on it, you can specify the actor in your workflow call. This allows you to use actor's name or other properties in your notification template. Actor properties can be included in the template as `$actor.`. Sample template with actor and recipient properties: ```text text theme={"system"} //handlebar template Hi {{$recipient.name}}, {{$actor.name}} added {{length comments}} new comments on the {{doc_name}}. //Rendered content Hi recipient_1, actor_1 added 2 new comments on the annual IT report. ``` ```go Sample workflow body theme={"system"} wfReqBody := map[string]interface{}{ "workflow": "new_comment", "actor": map[string]interface{}{ "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", }, "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": []string{"abc@example.com"}, "name":"recipient_1" }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "doc_name": "annual IT report", "date": "2024-01-01", "comments":["change the date","rest looks good"], }, } ``` ### Sending notification to anonymous user You can send notifications to anonymous users by passing ` "is_transient": true` in your recipient object. This approach is recommended for scenarios where you need to send notifications to unregistered users without creating them in the SuprSend platform. The same way, you can pass ` "is_transient": true` in your actor object to use actor properties in template without creating user profile. ```go Request theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_key_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "actor": map[string]interface{}{ "is_transient": true, "name": "actor_1", }, "recipients": []map[string]interface{}{ { "is_transient": true, "$email": []string{"abc@example.com"}, "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ### Multi-tenant notifications For use cases where you want to send notifications to your enterprise customers' end users, pass the `tenant_id` in your workflow instance. You can use this to dynamically manage [tenant level notification customizations](/docs/tenants). This includes the ability to customize template design or content and route notifications via tenant vendors. ```go Request theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_key_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{...} w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```go Response theme={"system"} { success: true, status: "success", status_code: 202, message: { status: "success" } } ``` ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. Idempotency key should be uniquely generated for each request (max 255 characters allowed). Spaces in start and end of the key will be trimmed. Here are some common approaches for generating idempotency keys: * **Generate a random UUID**for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like`user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. Once your request is accepted, you can check the status of your request in the '[ SuprSend Logs](https://app.suprsend.com/en/staging/logs)' section. ## Bulk API for triggering multiple workflows Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.Append()` on `Workflows.bulkTriggerInstance()` instance to add however-many-records to call in bulk. Response is an instance of `suprsend.BulkResponse` type. ```go Request theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_key_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody1 := map[string]interface{}{...} wfReqBody2 := map[string]interface{}{...} // Workflow: 1 w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody1, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // Workflow: 2 w2 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody2, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // ...... Add as many Workflow records as required. // Create bulk workflows instance bulkIns := suprClient.Workflows.bulkTriggerInstance() // add all your workflows to bulkInstance bulkIns.Append(w1, w2) // Trigger bulkResponse, err := bulkIns.Trigger() if err != nil { log.Println(err) } log.Println(bulkResponse) } ``` ```go Response theme={"system"} // Response structure suprsend.BulkResponse{ Status : "success", Total : 10, // number of records sent in bulk Success : 10, // number of records succeeded Failure : 0, // number of records failed Warnings: [], FailedRecords : [], } suprsend.BulkResponse{ Status : "fail", Total : 10, // number of records sent in bulk Success : 0, // number of records succeeded Failure : 10, // number of records failed Warnings: [] FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } suprsend.BulkResponse{ Status : "partial", Total : 10, // number of records sent in bulk Success : 6, // number of records succeeded Failure : 4, // number of records failed Warnings: [] FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } ``` ## Add file attachment in email To add one or more Attachments to a Notification (viz. Email), call `wfInstance.AddAttachment()` for each file with local-path or an accessible URL. ```go Request theme={"system"} workflowBody := {...} wfInstance := Workflow{Body: workflowBody} // this snippet can be used to add attachment to workflow err = wfInstance.AddAttachment("https://file_path.pdf") if err != nil { log.Println(err) } ``` A single workflow body size (including local file attachment) must not exceed 800KB (800 x 1024 bytes). If the file is a public accessible URL, attachment size doesn't add to overall workflow size. *** ## Dynamic workflow trigger ```go Request theme={"system"} // Create workflow body wfBody := map[string]interface{}{ "name": "Workflow Name", "template": "template slug", "notification_category": "category", // "delay": "15m", // Chek duration format in documentation "users": []map[string]interface{}{ { "distinct_id": "_distinct_id_", // if $channels is present, communication will be tried on mentioned channels only. // "$channels": []string{"email"}, "$email": []string{"abc@example.com"}, "$androidpush": []map[string]interface{}{ {"token": "__android_push_token__", "provider": "fcm", "device_id": ""}, }, }, }, // delivery instruction. how should notifications be sent, and whats the success metric "delivery": map[string]interface{}{ "smart": false, "success": "seen", }, // data can be any json string "data": map[string]interface{}{ "key1": "value1" }, } wf := &suprsend.Workflow{ Body: wfBody, IdempotencyKey: "", BrandId: "", } // Call TriggerWorkflow to send request to Suprsend _, err = suprClient.TriggerWorkflow(wf) if err != nil { log.Fatalln(err) } ``` **+CountryCode Required for SMS and Whatsapp:** For setting `$sms` and `$whatsapp`, `+` is mandatory to send along with phone number. Eg: +1 for US For configuring a workflow from backend, you can pass following properties in your method | Parameter | Description | Format | Obligation | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `name` | It is the unique name of the workflow. You can see workflow-related analytics on the workflow page (how many notifications were sent, delivered, clicked or interacted). The workflow name should be easily identifiable for your reference at a later stage | text | *Mandatory* | | `template` | It is the unique slug of the template created on SuprSend platform. You can get this slug by clicking on the clipboard icon next to the Template name on SuprSend templates page. It is the same for all channels | | *Mandatory* | | `notification_category` | You can understand more about them in the [Notification Category](/docs/notification-category) documentation | system / transactional / promotional | *Mandatory* | | `delay` | Workflow will be halted for the time mentioned in delay, and become active once the delay period is over. | **XX**d**XX**h**XX**m**XX**s or if its number (n) then delay is in seconds (n) | *Optional* | | `users` | Array object of target users. At least 1 user mandatory. `distinct_id` for each user mandatory Channel information is non-mandatory. If you pass channel information here, then these channels will be used for sending notification otherwise channels will be picked from user profile. | `"users": { "distinct_id": "value", "$channels": [], "channel_information_dict": {} },` | *Mandatory* | | `delivery` | Delivery instructions for the workflow. You can enable smart delivery by setting `"smart":True` By default, delivery instruction will be `"delivery": { "smart": False, "success": "seen" }` Further details are given in the [below section](/docs/go-trigger-workflow-from-api#smart-delivery) | `delivery = { "smart": True/False, "success": "delivered/seen/interaction/", "time_to_live": "", "mandatory_channels": [] }` # list of mandatory channels e.g gation", | *Optional* | | `data` | JSON. To replace the variables in the template, templates use [handlebars](https://handlebarsjs.com/guide/) language | `"data": { "key": { "key": "value", "key": "value" } },` | *Optional* | \*\*Response structure: \*\*When you call `suprClient.TriggerWorkflow`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll get an error message if something fails Note: The actual processing/execution of workflow happens asynchronously. Once your request is accepted, you can check the status of your request in the '[ SuprSend Logs](https://app.suprsend.com/en/staging/logs)' section. In the background, a workflow is created with the given workflow `name`. ### Bulk trigger multiple dynamic workflows Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.Append()` on `BulkWorkflows` instance to add however-many-records to call in bulk. Response is an instance of `suprsend.BulkResponse` type ```go Request theme={"system"} // Workflow: 1 wf1 := &suprsend.Workflow{ Body: map[string]interface{}{ "name": "__workflow_name__", "template": "__template_slug__", "notification_category": "__category__", // system/transactional/promotional "users": []map[string]interface{}{ { "distinct_id": "_distinct_id_", "$email": []string{"__email__"}, }, }, }, IdempotencyKey: "", BrandId: "", } // Workflow: 2 wf2 := &suprsend.Workflow{ Body: map[string]interface{}{ "name": "__workflow_name__", "template": "__template_slug__", "notification_category": "__category__", // system/transactional/promotional "users": []map[string]interface{}{ { "distinct_id": "_distinct_id_", "$email": []string{"__email__"}, }, }, }, IdempotencyKey: "123456", BrandId: "default", } // ...... Add as many Workflow records as required. // Create bulk workflows instance bulkIns := suprClient.BulkWorkflows.NewInstance() // add all your workflows to bulkInstance bulkIns.Append(wf1, wf2) // Trigger bulkResponse, err := bulkIns.Trigger() if err != nil { log.Println(err) } log.Println(bulkResponse) ``` ```go Response theme={"system"} // Response structure suprsend.BulkResponse{ Status : "success", Total : 10, // number of records sent in bulk Success : 10, // number of records succeeded Failure : 0, // number of records failed FailedRecords : [], } suprsend.BulkResponse{ Status : "fail", Total : 10, // number of records sent in bulk Success : 0, // number of records succeeded Failure : 10, // number of records failed FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } suprsend.BulkResponse{ Status : "partial", Total : 10, // number of records sent in bulk Success : 6, // number of records succeeded Failure : 4, // number of records failed FailedRecords : [{"record": {...}, "error": "error_str", "code": 500}] } ``` *** # Gupshup Source: https://docs.suprsend.com/docs/gupshup-sms Guide to connect your Twilio account with Gupshup to send SMS notifications. This section is a step-by-step guide to select Gupshup as your SMS service provider. ## Pre-Requisites 1. You'll need a Gupshup account to complete this tutorial. You can use your existing Gupshup account to integrate, or [Create a Gupshup account](https://www.gupshup.io/auth/signup) 2. [Create an Enterprise DLT account](https://dltconnect.airtel.in/signup/) (The link added here is for Airtel DLT portal. Though you can choose any vendor that best suits you) ## Gupshup integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click SMS -> Gupshup from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | User ID | User ID of your Gupshup account. SuprSend uses this information to send SMS on your behalf via your registered Gupshup account. | | Password | Password of your Gupshup account. SuprSend uses this information to send SMS on your behalf via your registered Gupshup account. | | Price per notification | This is the amount you pay per SMS notification to Karix. It helps us to calculate, estimate and optimise your cost spent on notifications. | | DLT Integration -> 'Mobile Operator' | Mobile Operator of your enterprise DLT account | | DLT Integration -> 'Headers' | 6 digit/character sender id registered for your entity ( *You can get the header details from your DLT portal*)*e.g. SPRSND* Also, you can add multiple headers in the list by just typing the header name and clicking on enter | | DLT Integration -> 'User Name' | User Name of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Password' | Password of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Entity ID' | Entity Registration ID linked to your DLT account. You can get the Registration Id from your DLT account homepage. SuprSend uses this info to send messages on your behalf through your registered DLT platform. | ## Setting callback URL in Gupshup account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Send a mail to Gupshup team to enable below webhook URL to your account: URL: [`https://hub.suprsend.com/webhook/gupshup/sms/`](https://hub.suprsend.com/webhook/gupshup/sms/) Request method- POST ## Enabling URL shortener service in Gupshup account URL shortening is essential if you are sending URL links in your SMS content. It reduces the number of characters in your SMS and is required for tracking the click rate of your messages. To activate URL shortening, write a mail to gupshup enterprise support ## How to register headers through Airtel DLT platform To register header on Airtel DLT platform, you can refer the section: [Sender ID/Mask/Header Registration- DLT Platform](https://enterprise.smsgupshup.com/DLT/senderidRegistration) *** # Gupshup (SMS) Source: https://docs.suprsend.com/docs/gupshup-sms-errors Possible SMS delivery errors reported by Gupshup and their resolutions. In case of vendor error, you'll see `Trigger failed` or `Failure by Vendor` status in the third step of the logs. However, you can hover over the failed chip to access the specific error message. ## Error Codes | Error | How to solve | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 001 - ABSENT\_SUBSCRIBER | The message could not be delivered because the recipient is temporarily unavailable. This error can occur due to network issues, roaming, recipient's mobile network provider service is temporary down or the SIM card is inactive. You can recheck the number and retry sending. | | 002 - CALL\_BARRED | Subscribers have some kind of call barring activity on the number due to which messages from unknown sources are blocked. | | 003 - UNKNOWN\_SUBSCRIBER | Unknown/invalid number. | | 004 - SERVICE\_DOWN | Operator service is temporarily down. | | 005 - SYSTEM\_FAILURE | Failure due to a problem in the Operator’s systems (Originating or Destination Operator). | | 006 - DND\_FAIL | The number is either in DND or Blocked due to being in DND or a complaint. | | 007 - BLOCKED | From number is blocked by the end user and you can't reach out to this user. | | 008 - DND\_TIMEOUT | Before sending promotional messages, the DLT system checks if the recipient's number is registered under DND (Do Not Disturb). This error indicates that the system failed to complete this check within the required timeframe. Retry sending after a day. | | 009 - OUTSIDE\_WORKING\_HOURS | DLT has restrictions on the hours during which promotional or bulk SMS messages can be sent. In India, promotional messages are allowed between 9 AM and 9 PM. You'll get this error if the message is sent outside these hours. | | error: 175: The "INTERNATIONAL\_PHONE" service is disabled for you. Kindly get the service enabled before using this action | You need to enable international sending in your gupshup account to send messages to numbers outside India. Reach out to Gupshup support to enable it for your account. | | HEADER\_NOT\_REGISTERED\_FOR\_TEMPLATE | Header added in the template is not registered on DLT or is different from the one added in approved template. | | 067 - HEADER\_NOT\_FOUND | Header added in the template is not registered on DLT or is different from the one added in approved template. | | 00b - BLOCKED\_MASK | The sender ID / Header (or mask) you are using to send the SMS has been blocked. It could happen due to non-compliance with regulatory guidelines or have exceeded the allowed limits of sending message. | | error: 124: Validity of your SMS pack has expired on "Sat Aug 20 00:00:00 IST 2022." You are not allowed to send messages now. | SMS pack on Gupshup has expired. You need to renew the pack to start sending messages again. | | 038 - MSG\_DOES\_NOT\_MATCH\_TEMPLATE | The template passed in the message content does not match with dltTemplateId uploaded on DLT portal. | | error: 102: Authentication failed due to invalid userId or password | Userid or password added in Gupshup vendor form is incorrect. Update it to fix this error. Reach out to SuprSend support if the issue persists. | | TEMPLATE\_NOT\_FOUND | The template\_id of the approved template is not found on DLT portal. Reach out to our support if SuprSend is taking care of SMS template approval for you. | | Get "[https://enterprise.smsgupshup.com/GatewayAPI/rest?xxxx"](https://enterprise.smsgupshup.com/GatewayAPI/rest?xxxx%22): dial TCP 202.87.33.165:443: i/o timeout | Gupshup server didn't respond within a given duration. Most likely reason being server is unavailable/on high load. If you are getting this error for all requests, it could be possible that you IP whitelisting enabled and SuprSend's IP is not whitelisted. | | 00a - OTHER | Messages that are sent but can not be delivered for reasons that don’t fall under any mentioned category. | | 074 - TEMPLATE\_NOT\_MATCHED | The template content of the approved template doesn't match the template content on SuprSend platform. Reach out to our support if SuprSend is taking care of SMS template approval for you. | | 070 - HEADER\_RESERVED | Indicates that the header (also known as the sender ID) used in the SMS does not match any of the registered headers in the DLT platform | | error: 106: The method "SENDMESSAGE" is not supported. | The "SENDMESSAGE" method might be outdated or not supported in the current version of the Gupshup API. Verify that you are using a supported method as per the latest API documentation. Reach out to our support if you get this error. | *** # Gupshup Source: https://docs.suprsend.com/docs/gupshup-whatsapp Guide to connect your Gupshup account with SuprSend to send Whatsapp notifications. ## Pre-Requisites You'll need a Gupshup account to complete this tutorial. You can use your existing Gupshup account to integrate, or [Create a Gupshup account](https://www.gupshup.io/auth/signup) ## Gupshup integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click WhatsApp -> Gupshup from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | App Name | App name of your gupshup WhatsApp account. SuprSend uses this information to send WhatsApp on your behalf via your Gupshup account | | WhatsApp Mobile Number | Mobile number of your WhatsApp business account. SuprSend uses this information to send WhatsApp on your behalf via your registered WhatsApp number | | Notification Account User ID | Gupshup creates 2 accounts - one for sending one way messages and other for 2-way messages. Add the user ID of 1-way message account here. SuprSend will use this information to send 1-way messages on your behalf via your Gupshup account | | Notification Account Password | Login password of 1-way message account. SuprSend will use this information to send 1-way messages on your behalf via your Gupshup account | | Two way Account User ID | User ID of 2-way message account. SuprSend will use this information to send 2-way messages on your behalf via your Gupshup account | | Two way Account Password | Password of 2-way message account. SuprSend will use this information to send 2-way messages on your behalf via your Gupshup account | | WhatsApp Analytics Panel User ID | User ID of [WhatsApp analytics](https://unify.smsgupshup.com/WhatsApp/Analytics/) account. SuprSend will use this information to upload templates on your behalf in your gupshup account for approval | | WhatsApp Analytics Panel Password | Password of [WhatsApp analytics](https://unify.smsgupshup.com/WhatsApp/Analytics/) account. SuprSend will use this information to upload templates on your behalf in your gupshup account for approval | | Facebook Page URL | URL of your Facebook business page | | Price per notification | This is the amount you pay per SMS notification to Gupshup. It helps us to calculate, estimate and optimise your cost spent on notifications. | You'll get all the account credentials from Gupshup team as soon as your account is created ## Setting callback URL in Gupshup account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Send a mail to Gupshup team to enable below webhook URL to your account: > HTTP Method: POST >
URL: [`https://hub.suprsend.com/webhook/gupshup/whatsapp/`](https://hub.suprsend.com/webhook/gupshup/whatsapp/) >
Headers: Content-Type: application/json *** # Handlebars Helpers Source: https://docs.suprsend.com/docs/handlebars-helpers List of supported handlebars helpers that can be used in a template to format data or add conditions on the data passed in workflow trigger. SuprSend template editor uses handlebars templating language to add variables and support conditional content rendering in a template. You can find the list of inbuilt handlebars helpers [here](https://handlebarsjs.com/guide/builtin-helpers.html). Apart from the inbuilt helper functions, we have also created some custom helper functions which you can use in template editors. **Not supported in SMS and Whatsapp:** Since, SMS (in India) and Whatsapp require pre-approved templates to send messages, we can't support conditional content rendering in SMS and Whatsapp templates. You can use handlebars helpers if you are sending SMS in US with vendors like Twilio and Messagebird. ### default Can be used to add a default value if the variable values are `" "` , `null` , `undefined`. ```handlebars Syntax theme={"system"} {{default variable "default_value" }} ``` ```Example theme={"system"} {{default name "user" }} ``` > **Returns:** > > * Variable value if the variable value is present. For instance, the above example with data `{"name":"Joe"}` will return `Joe` > * Default value if variable value is in `" "` , `null` , `undefined`. For instance, the above example with data `{"name":""}`, `{"name":null}` or `{"city":"Bangalore"}` will return `user` ### compare Can be used to show some content in the template based on a condition. For email, you can use [display condition](/docs/email-template#display-email-blocks-based-on-condition) to show or hide a particular content block ```handlebars Syntax theme={"system"} {{#compare name '==' "Mike"}} true_block {{else}} false_block {{/compare}} ``` ```Example Example theme={"system"} Example-1: with else statement {{#compare candidate_count '==' 1}} is {{else}} are {{/compare}} Example-2: without else statement {{#compare candidate_count '==' 1}} is {{/compare}} ``` > **Returns:** > > * Returns if the condition inside compare statement returns truthy value. For instance, the above example with data`{"candidate_count":1}`will return`is` > * Returns (in case else statement is present) if the condition inside compare statement returns falsy value. For instance, Example-1 with data`{"candidate_count":3}`will return`are` > * or Returns if the else statement is not present. For instance, Example-2 with data`{"candidate_count":3}`will not return anything **Supported Conditional operators in compare statement:** | Operator | Returns truthy when | Sample Statement | | :------- | :---------------------------------------------- | :--------------------- | | `==` | LHS equals RHS | 1 `==` "1" | | `===` | LHS value as well as data type matches with RHS | 1 `===` 1 | | `>` | LHS is greater than RHS | 2 `>` 1 | | `\<` | LHS is less than RHS | 1 `\<` 2 | | `>=` | LHS is greater than equals to RHS | 2 `>=` 2 or 3 `>=` 2 | | `\<=` | LHS is less than equal to RHS | 2 `\<=` 2 or 1 `\<=` 2 | | `!=` | LHS is not equal RHS | 3 `!=` 1 | | `!==` | LHS value or data type does not equal RHS | 1 `!==` "1" | Builtin helpers and custom helpers in handlebars can only work with nested objects if the value of parent key is present. For instance, `{{default place.city "San Francisco"}}` will return `"San Francisco"` only if `place` key is present, that is `{"place":[]}` However, you can use the nested `if` condition to check if the parent is present and if so, then render nested objects value. Refer below example: ```text Sample text theme={"system"} {{#if place}}   {{default place.city "San Francisco"}} {{else}}   San Francisco {{/if}} {{#if person.address}}   {{#if person.address.current}}     {{{default person.address.current.city "Los Angeles"}}   {{/if}} {{/if}} ``` ### each You can iterate over a static or dynamic length list using each helper. Inside the block, you add the array object key which you want to iterate over. ```handlebars Syntax theme={"system"} {{#each array_object}} {{variable_key}} {{/each}} ``` ```json Mock JSON Data theme={"system"} {{#each admins}} {{name}} {{/each}} ``` ```File Response Return theme={"system"} { "admins":[ { "name":"steve", "id":"1", "city":"Palo Alto" }, { "name":"Olivia", "id":2, "city":"Houston" } ] } ``` > **Returns:** Above example for below mock data will return `steve Olivia` ### datetime-format Returns a formatted date string. This helper will throw error if an invalid date is provided. ```Syntax Syntax theme={"system"} {{datetime-format variable "format string" "timezone"}} ``` ```json Mock JSON Data theme={"system"} { "date":"2023-09-26T00:00:00Z" } ``` | parameter | Obligation | description | | :---------------- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **variable** | *mandatory* | should be a date or timestamp. It will throw an error for invalid date format. To get today's date (date on which template is getting rendered) you can use `"now"` string. | | **format string** | *mandatory* | date string defining the format in which date should be printed. See all [formatting options here](https://momentjs.com/docs/#/displaying/format/). e.g. **"dddd, MMMM Do YYYY, h:mm:ss a"** will return datetime as **"Sunday, February 14 2010, 3:25:50 PM"** | | **timezone** | *optional* | you can add timezone as a third parameter to convert time in a given timezone. See the list of [all possible timezones here](https://support.sendwithus.com/jinja/jinja_time/#complete-list-of-all-available-timezones) | **Examples:** | Example | Output | | :----------------------------------------------------------- | :-------------------------------- | | `{{datetime-format date "ddd, MMM Do YYYY, h:mm:ss a"}}` | `Tue, Sep 26th 2023, 12:00:00 am` | | `{{datetime-format date "[Today is] dddd"}}` | `Today is Tuesday` | | `{{datetime-format date "Do MMMM, YYYY" "America/Chicago"}}` | `25th September, 2023` | ### jsonStringify Coverts any type of JSON input to a string. Can be used to print JSON and array directly in templates without having to convert it into individual strings. One of the common use case is to pass complete JSON in Inbox template -> custom JSON field to generate custom UI. ```Syntax Syntax theme={"system"} {{jsonStringify variable}} ``` ```json Mock JSON Data theme={"system"} { "likes":3, "comments":4, "users":["Olivia","Steve"] } ``` variable should be a valid JSON input. **Examples:** | Example | Output | | :------------------------ | :------------------------------------------------------ | | `{{jsonStringify this}}` | `\{"likes":3,"comments":4,"users":\["Olivia","Steve"]}` | | `{{jsonStringify users}}` | `\["Olivia","Steve"]` | ### lowercase This handlebar helper is used to convert string to lowercase. If non string values are provided empty string `''` is returned. ```Syntax Syntax theme={"system"} {{lowercase "string"}} ``` ### uppercase This handlebar helper is used to convert string to uppercase. If non string values are provided empty string `''` is returned. ```Syntax Syntax theme={"system"} {{uppercase "string"}} ``` ### capitalize This handlebar helper is used to capitalize the first character in provided string. If non string values are provided empty string `''` is returned. ```Syntax Syntax theme={"system"} {{capitalize "string"}} ``` ## Math Operators: ### add Returns the sum of two operands. Both input values should be numbers, else it will throw an error. ```Syntax Syntax theme={"system"} {{add number1 number2}} ``` ```json Mock JSON Data theme={"system"} { "likes":3, "comments":4 } ``` **Examples:** | Example | Output | | :----------------------- | :----- | | `{{add 9 4}}` | `13` | | `{{add likes comments}}` | `7` | ### subtract Returns the difference between two operands. Both input values should be numbers, else it will throw an error. ```handlebars Syntax theme={"system"} {{subtract number1 number2}} ``` ```json Mock JSON Data theme={"system"} { "likes":3, "comments":4 } ``` **Examples:** | Example | Output | | :--------------------- | :----- | | `{{subtract 9 4}}` | `5` | | `{{subtract likes 1}}` | `2` | ### multiply Returns the product of two operands. Both input values should be numbers, else it will throw an error. ```handlebars Syntax theme={"system"} {{multiply number1 number2}} ``` ```json Mock JSON Data theme={"system"} { "ARR":300, "billing_months":12 } ``` **Examples:** | Example | Output | | :-------------------------------- | :----- | | `{{multiply 9 4}}` | `36` | | `{{multiply ARR billing_months}}` | `3600` | ### divide Returns the quotient of the left operand divided by the right operand. Both input values should be numbers; else it will throw an error. ```handlebars Syntax theme={"system"} {{divide dividend divisor}} ``` ```json Mock JSON Data theme={"system"} { "total_bill":3600, "billing_months":12 } ``` **Examples:** | Example | Output | | :------------------------------------- | :----- | | `{{divide 14 4}}` | `3.5` | | `{{divide total_bill billing_months}}` | `300` | ### round Returns the value of a number rounded to the nearest integer. The input value should be a number; else it will throw an error. ```handlebars Syntax theme={"system"} {{round number}} ``` **Examples:** | Example | Output | | :------------------------ | :----- | | `{{round 12.5}}` | `13` | | `{{round (divide 15 4)}}` | `4` | ### mod Returns the remainder left over when one operand is divided by a second operand. It always takes the sign of the dividend. This helper will throw an error if it encounters an input value that is not a number or if the divisor is 0. ```handlebars Syntax theme={"system"} {{mod dividend divisor}} ``` ```json Mock JSON Data theme={"system"} { "total_bill":3600, "billing_months":12 } ``` **Examples:** | Example | Output | | :---------------------------------- | :----- | | `{{mod 14 4}}` | `2` | | `{{mod total_bill billing_months}}` | `0` | ## Array Operators ### unique Returns unique items in an array. ```handlebars Syntax theme={"system"} {{unique variable "key_string" }} ``` ```json Mock JSON Data theme={"system"} { "array": [ { "city": "San Francisco", "name": "Mike" }, { "city": "Austin", "name": "Juliana" }, { "city": "Austin", "name": "Nova" } ], "personal_details": [ { "name": { "first_name": "john", "last_name": "doe" } }, { "name": { "first_name": "mike", "last_name": "waz" } } ], "email": [ "joe@abc.com", "steve@abc.com", "joe@abc.com" ] } ``` | parameter | description | | :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **variable** | should be an array. If not helper function will throw an error. | | **key\_string** | mandatory only if items in the array are objects. If a *unique* operation needs to be performed on nested objects, then object notation *(a.b or a\['b'])* can be used. | **Examples:** | Example | Output | | :----------------------------------------------- | :--------------------------- | | `{{unique email }}` | `joe@abc.com, steve@abc.com` | | `{{unique array "city" }}` | `San Francisco, Austin` | | `{{unique personal_details "name.first_name" }}` | `john, mike` | ### itemAt Returns item at a particular index in the array. It can be useful in scenarios where you want to display a message like `liked by Mike and 3 others`. ```handlebars Syntax theme={"system"} {{itemAt variable index }} ``` ```json Mock JSON Data theme={"system"} { "array": [ { "city": "San Francisco", "name": "Mike" }, { "city": "Austin", "name": "Juliana" }, { "city": "Austin", "name": "Nova" } ], "email":["joe@abc.com","steve@abc.com","joe@abc.com"] } ``` | Parameter | Description | | :----------- | :----------------------------------------------------------------- | | **variable** | should be an array else the helper function will throw an error. | | **index** | should be an integer else the helper function will throw an error. | **Examples:** | Example | Output | | :------------------- | :-------------- | | `{{itemAt email 1}}` | `steve@abc.com` | **Note:** This helper only works with an array of primitive data types like string, boolean, etc. If you have an array with objects in it, you can combine it with a unique helper to return the indexed value of a particular key inside the object. For instance, `{{itemAt (unique array "city") 0}}` in above mock will return `San Francisco`. ### join Concatenates all the values in an array, using a specified separator string between each value. ```handlebars Syntax theme={"system"} {{join variable "separator"}} ``` ```json Mock JSON Data theme={"system"} { "array": [ { "city": "San Francisco", "name": "Mike" }, { "city": "Austin", "name": "Juliana" }, { "city": "Austin", "name": "Nova" } ], "email":["joe@abc.com","steve@abc.com","joe@abc.com"] } ``` | Parameter | Description | | :------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | **variable** | should be an array and items in the array should be of primitive data types like string, number, etc, else the helper function will throw an error. | | **separator** | should be string else by default separator `,` will be used. If not provided `,` is used. | **Examples:** | Example | Output | | :--------------------- | :------------------------------------------ | | `{{join email }}` | `joe@abc.com , steve@abc.com , joe@abc.com` | | `{{join email ' - '}}` | `joe@abc.com - steve@abc.com - joe@abc.com` | **Note:** This helper only works with an array of primitive data types like string, boolean, etc. If you have an array with objects in it, you can combine them with a unique helper to join the value of a particular key inside the object. For instance, `{{join (unique array "city")}}` in above mock will return `San Francisco, Austin` ### length Can be used to get the number of items in an array or the character length of a string. It can be useful in scenarios where you want to display a message like `your post has got 100 likes`. ```handlebars Syntax theme={"system"} {{length variable}} ``` ```json Mock JSON Data theme={"system"} { "array": [ { "city": "San Francisco", "name": "Mike" }, { "city": "Austin", "name": "Juliana" }, { "city": "Austin", "name": "Nova" } ], "email":["joe@abc.com","steve@abc.com","joe@abc.com"], "name":"Justin", "active":true } ``` | Property | Description | | :----------- | :--------------------------------------------------------------------------------------------------------- | | **variable** | should be an array or string to get the actual count of items in the variable provided, else it returns 0. | **Examples:** | Example | Output | | :------------------ | :----- | | `{{length array}}` | 3 | | `{{length name}}` | 6 | | `{{length active}}` | 0 | We are adding further helpers to this list. If you have a use case that is not covered, ping us on our [Slack Support Channel](https://suprsendcommunity.slack.com/ssb/redirect) *** # HMAC Authentication Source: https://docs.suprsend.com/docs/hmac-authentication Steps to safely authenticate users and generate subscriber-id in headless Inbox implementation. ## Why HMAC authentication is required? When you initialize SuprSend's Inbox on your website, you provide your SuprSend workspace API Key and a user's distinct id. A savvy user can obtain this API Key with this setup and can initialize the inbox on their website with your API Key but with a different distinct id and start viewing that user's notifications. With HMAC authentication, an SHA-256 HMAC string (`subscriber_id`) is generated for each `distinct_id` and prevents unauthorized access to Inbox service by just spoofing `distinct_id`. ## How to generate subscriber\_id? Use the below function in your server-side code to generate a unique unguessable `subscriber_id` using your `distinct_id` and **inbox-secret** (picked from the Inbox Vendor Integration page). * `subscriber_id`is unique to each`distinct_id`and should be generated for each user. * Inbox Secret is the`Shared Secret`key available in your[Inbox vendor page](https://app.suprsend.com/en/production/vendors/inbox/suprsend-inbox). This key is unique to your workspace and should not be shared with anyone for security purposes ```python python theme={"system"} import base64 import hashlib import hmac def hmac_rawurlsafe_base64_string(distinct_id: str, secret: str): digest = hmac.HMAC(secret.encode(), msg=distinct_id.encode(), digestmod=hashlib.sha256).digest() encoded = base64.urlsafe_b64encode(digest).decode() return encoded.rstrip("=") ``` ```go go theme={"system"} package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" ) func HmacRawURLSafeBase64String(message, secret string) string { hash := hmac.New(sha256.New, []byte(secret)) hash.Write([]byte(message)) return base64.RawURLEncoding.EncodeToString(hash.Sum(nil)) } ``` ```javascript Node theme={"system"} import crypto from "crypto"; // node versions >= v15.7.0, v14.18.0 function hmac_rawurlsafe_base64_string(distinct_id, secret) { const hash = crypto .createHmac("sha256", secret) .update(distinct_id) .digest("base64url"); return hash.replace(/=+$/, ""); } // node versions < v15.7.0, v14.18.0 function hmac_rawurlsafe_base64_string(distinct_id, secret) { return crypto .createHmac("sha256", secret) .update(distinct_id) .digest("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); } ``` ```java java theme={"system"} package test; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class TestHmacGeneratation { public static void main(String[] args) throws Exception { String distinct_id = "b8278572-2929-4af6-be2b-cdc2bc1f6256"; String secret = "IG-J8Wvf7M-w4ll13h53NJAMQQNHdUqFTSJ2JVAZl0s"; TestHmacGeneratation instance = new TestHmacGeneratation(); String output = instance.hmacRawURLSafeBase64String(distinct_id, secret); System.out.println(output); // prints dHBWYF4oV190o4j-e3eYxB-SCkeHnoaiofe8EmGk9JQ } private String hmacRawURLSafeBase64String(String distinctId, String secret) throws InvalidKeyException, NoSuchAlgorithmException { Mac sha256mac = getSha256macInstance(secret); byte[] macData = sha256mac.doFinal(distinctId.getBytes(StandardCharsets.UTF_8)); String hmacString = Base64.getUrlEncoder().withoutPadding().encodeToString(macData); return hmacString; } private Mac getSha256macInstance(String secret) throws NoSuchAlgorithmException, InvalidKeyException { final byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8); SecretKeySpec keySpec = new SecretKeySpec(secretBytes, "HmacSHA256"); Mac sha256mac; try { sha256mac = Mac.getInstance("HmacSHA256"); sha256mac.init(keySpec); } catch (NoSuchAlgorithmException e) { throw e; } catch (InvalidKeyException e) { throw e; } return sha256mac; } } ``` ```pgsql PL/pgSQL theme={"system"} CREATE EXTENSION "pgcryto"; CREATE OR REPLACE FUNCTION hmac_rawurlsafe_base64_string(distinct_id VARCHAR, secret VARCHAR) RETURNS VARCHAR LANGUAGE PLPGSQL AS $func$ DECLARE hmac_string VARCHAR; BEGIN hmac_string = encode(hmac(distinct_id::TEXT, secret, 'SHA256'), 'base64'); hmac_string = replace(replace(hmac_string, '+', '-'), '/', '_'); hmac_string = RTRIM(hmac_string, '='); RETURN hmac_string; END; $func$; ``` It is imperative that the inbox secret is stored safely on your server side and not exposed to client-side code. **NOTE:** The subscriber\_id must be generated by server-side code (not in browser) Even after setting up the inbox, if you are not able to see notifications then cross-check if your subscriber\_id mentioned is exactly correct by opening the user's tab in the Suprsend dashboard. *** # How Auto Translation works? Source: https://docs.suprsend.com/docs/how-auto-translation-works The process of language translation at SuprSend At SuprSend, we have a team of highly skilled translators who handles the translation tasks in an accurate and timely manner. ### Why manual translation? Though machine translators are fast, it still lags in accuracy of translation, which are crucial to delivering the right message to users. Even google translate has an accuracy of \~85% which is known to provide one of the best accuracies. ### How it works? On the language modal, you'll see an option to enable **"Auto Translate"**. Click on it if you want us to translate content in your preferred language. ![Fig 1. French and Spanish Language added, with Auto Translation off](https://files.readme.io/9d4fb1e-auto_translate_image.png) Fig 1. French and Spanish Language added, with Auto Translation off Once, you enable **"Auto Translate"**, our team will receive a request that you have opted-in for translation. To ensure that we deliver high quality translations, we would reach out to you to better understand your requirement - * What languages to translate * What will be the required SLA * Who is the customer - it will help to set the tone right for translation etc. After the discussion is done, we'll setup the translation process. All you have to do is then, For instance, in *Fig 2*, ***Spanish*** and ***French*** are the added languages. ![Fig 2. English language selected while publishing the version](https://files.readme.io/4fd4c19-Publish_language.png "Fig 2. English language selected while publishing the version") Fig 2. English language selected while publishing the version As soon as the version is published, our team will translate english content in other languages which were not published and publish a new version. e.g., ***French*** and ***Russian*** in the above image will be translated by the team from the english content which was published. A new version with French, English and Russian content will then be published. ### Pricing Auto translation is a paid feature at SuprSend which will be billed separately. Pricing varies based on the language of translation: | Language | Rate per Word (\$) | | ---------------- | ------------------ | | Spanish | 0.188 | | French | 0.188 | | Italian | 0.188 | | German | 0.188 | | Russian | 0.188 | | Mandarin Chinese | 0.188 | | Malay | 0.188 | | Portuguese | 0.188 | | Vietnamese | 0.188 | | Japanese | 0.234 | | Arabic | 0.156 | | Hindi | 0.039 | | Marathi | 0.039 | | Malayalam | 0.039 | | Kannada | 0.039 | | Tamil | 0.039 | | Telugu | 0.039 | | Assamese | 0.039 | | Punjabi | 0.039 | | Gujarati | 0.039 | | Kashmiri | 0.039 | | Bengali | 0.039 | | Oriya | 0.039 | | Urdu | 0.039 | | Sindhi | 0.047 | | Nepali | 0.078 | # In-App Inbox Template Source: https://docs.suprsend.com/docs/in-app-inbox-template How to design Inbox template with customisation options like action buttons, tags, pinning, and expiry. In-App Inbox notification template, you can add header, body, buttons, avatar and card click action and other advanced configurations like pinning, expiry, tags to customize the view and behaviour of Inbox notification. ## Designing Template Designing template inside SuprSend is quite intuitive with WYSWYG editors for all channels. We use [handlebars](https://handlebarsjs.com/guide/#what-is-handlebars) as the templating language. You can add variable in the template as `{{var}}`. We also support [handlebars helpers](/docs/handlebars-helpers) for handling complex template use case like adding if-else condition, showing a default value when variable is absent or handling complex arrays. To create a template, go to [SuprSend dashboard -> templates](https://app.suprsend.com/en/staging/templates/) tab and enable Inbox channel. ### Form fields description | Field | Type | Description | | -------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Header | Single line text field | Heading of the message shown in bold at the top of the content. Use it to show the summary of your message like `New comment received`, `Your story has got 30 views` | | Text | Multi-line text field | Body of your message. This is where the message content will be shown. We support markdown syntax in body field to add links, blockquotes, showing some content in bold etc. All supported markdown syntax in text field are mentioned here. | | Avatar | image public URL in `.jpeg`, `.png` format | This can be used to show image of the actor as LinkedIn or some static images based on the type of message as used in HubSpot. | | Subtext | Single line text field | Subtext is like the footer of your content. It is clickable, so you can use it to show subtle information like in LinkedIn, you would see number of comments and likes in subtext or Jira uses it to show the task's card number. | | Action URL | `http / https` URL | This is the URL user will be redirected on card click. You can select `Open in new tab` if you want the link to open in a new tab. | | Action buttons | text - URL pair | You can add up to 2 buttons in your template. Buttons can be used to redirect users to a link or perform any inline actions like opening a modal or calling an internal function like `Approve` button to internally approve the request. Refer [Custom click handler](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/customization.md#action-button-custom-click-handlers) to customize click action on a button. You can select `Open in new tab` if you want the button link to open in a new tab. | ### Supported markdown syntax in text field We support [headings](https://www.markdownguide.org/basic-syntax/#headings), [bold](https://www.markdownguide.org/basic-syntax/#bold), [Italic](https://www.markdownguide.org/basic-syntax/#italic), [Blockquotes](https://www.markdownguide.org/basic-syntax/#blockquotes-1), [Nested Blockquotes](https://www.markdownguide.org/basic-syntax/#nested-blockquotes), [links](https://www.markdownguide.org/basic-syntax/#links) and [code](https://www.markdownguide.org/basic-syntax/#code). ## In-App inbox - advanced configurations (Optional) ### Tags You can add tags to filter and organize notifications inside [multiple tabs](/docs/multi-tabs). Other than tags, you can filter out tabs based on [notification category](/docs/notification-category) or notification read status. e.g., show all `unread` notifications with `mentions` tag inside `Mentions` tab. ### Pin notification Pinned notifications are shown with a `pinned` tag on top in your notification list. Enable the switch to pin the notification. You can use this to send critical alerts where you want user to complete some critical action within your platform like finish compliance or renew plan or some limited time offer. ### Expiry Setting expiry will auto archive the notification when the expiry period is reached. You can use it to send notifications which are relevant till a particular timestamp like limited time offers or reminder to join an upcoming event or set a fixed expiry to all notifications, in general to keep user's Inbox clean. You can either set [fixed](/docs/in-app-inbox-template#fixed-expiry) or [dynamic](/docs/in-app-inbox-template#dynamic-expiry) expiry. Dynamic expiry are computed using data in your event or user properties and can vary for each user. An example of dynamic expiry could be reminder notifications of some upcoming event. You can also [show expiry timer](/docs/in-app-inbox-template#show-expiry-timer) on the notification to drive action urgency. #### Fixed expiry Fixed expiry can be a relative time, like - `**d **h **m **s` or an absolute timestamp, like `2024-04-01 2:00 pm`. Absolute timestamp added in form takes the time in your local timezone. #### Dynamic expiry In case of dynamic expiry, expiry is computed using the data from your event or user properties. You can add dynamic expiry as handlebars variable, like `{{expiry_time}}`. Your duration key variable can be computed to either: * An ISO-8601 timestamp (e.g. 2024-03-02T20:34:07Z) which must be a datetime in the future, or * A relative duration unit, which can be * an integer like `50`, considered as duration in seconds. * an interval string defined as `**d **h **m **s`, where d = day, h = hour, m = minutes and s = seconds #### Show expiry timer Enable it to show expiry timer on your Inbox notification. This helps to drive action urgency. The expiry timer shows in a grey background if the time left is greater than 1 hour and goes red when the difference goes below 1 hour so as to draw user's attention when the expiry time is near. ## Adding dynamic content in the template There will always be the case where you would require to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending the message. You'll need to pass these while triggering the communication from one of our frontend or backend SDKs. Here is a step-by-step guide on how to add dynamic content in Inbox: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates,refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing the android push template. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable_name}}` If you have declared the variables in the global 'Mock data' button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of errors like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the `Variables` button. To manually enter the variable name, follow the [handlerbarsjs guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below is an example of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the `Templates` section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "3,950.00" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "2,549.00" }, { "product_name": "RayBan Sunglasses", "product_price": "7,899.00" } ], "event": { "location": { "city": "Bangalore", "state": "KA" }, "order_id": "11200123", "first_name": "Nikita" }, "product_page": "https://www.suprsend.com" } ``` To enter a nested variable, enter in the format `{{var1.var2.var3}}`. e.g. to refer to city in the example above, you need to enter `{{event.location.city}}` To refer to an array element, enter in format `{{var1.[*index*].var2}}. e.g. to refer to `product\_name`of the first element of the array`array, enter `` {{array.[0].product_name}}` `` Enclose your variable name in square bracket as shown here: `{{event.[first name]}}` You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: 1. Make sure you have entered the variable name and the sample value in the `Variables` button. 2. Make sure you have entered the correct variable name in the template, as per the `handlebarsjs` guideline. **What happens if there is variable mismatch at the time of sending?** At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. e.g. if there is an error in rendering Android Push template, but email template is successfully rendered, Android Push notification will not be triggered, but email notification will be triggered by SuprSend. ## Best Practices - notification design Here's a breakdown of which form fields to use for different types of notifications: * **Tags** act as filters to create Inbox tabs, a useful way to organize notifications. For instance, LinkedIn uses tabs to separate mentions and reactions to your posts in distinct tabs. You can use tabs to differentiate regular updates from more relevant ones. A generic example of tabs for a SAAS application could be `All, Product releases and Upcoming events`. Learn how to implement tabs in notification [here](/docs/multi-tabs). * [Expiry](/docs/in-app-inbox-template#expiry): It's a good practice to add expiry of 15 days or more to all notifications except long lived notifications to maintain clean user inbox. A shorter expiry duration can be set for notifications valid for a limited time, such as webinars and upcoming events. You can also show [expiry timer](/docs/in-app-inbox-template#show-expiry-timer) to prompt action urgency. For instance, when you need users to respond to feedback within three days or for events with impending registration closures. The color change of the expiry timer based on remaining time is an effective way to convey urgency. 📘 Avoid adding expiry to long-lived notifications that users might want to reference later, like product updates or blog posts. * [Pinning](/docs/in-app-inbox-template#pin-notification) is used for notifications that should always show on top until user reads it or completes related action. Examples include compliance-related actions, system updates, or urgent releases requiring app version update. Always combine it with expiry otherwise the notification will always be pinned in user's inbox until they archive it. *** # Flutter (Headless) Source: https://docs.suprsend.com/docs/inbox-flutter Integrate SuprSend inbox in Flutter using the headless SDK and hooks. SuprSend uses flutter hooks to provide inbox functionality in flutter applications. ## Installation Add the following line of code inside dependencies in the `pubspec.yaml` file under the dependencies section ```yaml pubspec.yaml theme={"system"} dependencies: flutter: sdk: flutter suprsend_flutter_inbox: "^0.0.1" ``` ```shell Bash theme={"system"} $ flutter pub get ``` ## Initialization Enclose your Material App inside *SuprSendProvider* and pass the workspace key, workspace secret, distinct\_id, and [subscriber\_id](/docs/hmac-authentication). ```javascript main.dart theme={"system"} import 'package:suprsend_flutter_inbox/main.dart'; SuprSendProvider( workspaceKey: , workspaceSecret: , distinctId: distinct_id, subscriberId: subscriber_id, child: YourAppComponent() ) ``` SuprSend hooks can only be used inside of SuprSendProvider. ## Adding SuprSend inbox component ### useBell hook This hook provides unSeenCount, markAllSeen which is related to the Bell icon in the inbox **unSeenCount**: Use this variable to show the unseen notification count anywhere in your application. **markAllSeen**: Used to mark seen for all notifications. Call this method on clicking the bell icon so that it will reset the notification count to 0. ```javascript bell.dart theme={"system"} import 'package:suprsend_flutter_inbox/main.dart'; final bellData = useBell(); // bellData structure: { "unSeenCount": int, "markAllSeen": ()=>void } ``` ### useNotifications hook This hook provides a notifications list, unSeenCount, markClicked, and markAllSeen. **notifications**: List of all notifications. This array can be looped and notifications can be displayed. **unSeenCount**: Use this variable to show the unseen notification count anywhere in your application. **markClicked**: Method used to mark a notification as clicked. Pass notification id which is clicked as the first param. ```javascript code.dart theme={"system"} import 'package:suprsend_flutter_inbox/main.dart'; final notifData = useNotifications(); // notifData structure: { "notifications": List, "unSeenCount": int, "markAllSeen": ()=>void "markClicked":(n_id)=>void } // Notification structure: { "n_id": string, "n_category": string, "created_on": int, "seen_on": int, "message": { "header": string, "text": string, "url": string, "extra_data": string, "avatar":{ "action_url": string, "avatar_url": string }, "subtext":{ "text": string, "action_url": string } "actions":[ { "name": string, "url": string } ] } } ``` ## Example implementation Example implementation can be found [here](https://github.com/suprsend/suprsend-flutter-sdk/blob/main/example/lib/main.dart). *** # Overview Source: https://docs.suprsend.com/docs/inbox-overview Learn about features and benefits of SuprSend's notification inbox, with link to live demo and git repository. A notification inbox is a centralized place for all your in-app notifications, offering several advantages over other notification channels. With a notification inbox, users can receive real-time transactional updates related to payment reminders, software updates, new features, etc. within the app. You can use SuprSend Inbox to easily integrate feeds, inboxes, and toasts into your product. ## Benefits of notification inbox over other communication channels * **Real-time updates:** A notification inbox delivers real-time updates to users, providing timely and relevant information. * **100% deliverability:** Notifications sent through a notification inbox have a high deliverability rate and are perfect for sending important updates and messages. * **Flexibility in message design:** There is no limitation to the type and length of content that you can send with Inbox. Hence, the messages can be designed as per your requirement. Plus, you can add any type of click action to inbox message components which offer great flexibility in driving user engagement. ## Integrating SuprSend inbox With SuprSend Inbox, * You can effortlessly add a **beautifully designed, highly functional inbox** to your product in an hour. * There is **no infrastructure required** at your end to manage and store inbox notifications, and for state management such as read, seen, and archive tracking * You get **ready components to handle any use** such as showing toast, showing profile avatar in your notification, handling different click actions in your notification component, etc. * Inbox messages are **completely secure with HMAC encoded user identification** to safeguard them from unauthorized access. This means you can use it to send sensitive information related to payments, billing, account updates, etc. * You can **customize it to match your brand style** using pre-defined UI customization options or build your headless UI using hooks * SDKs are available in all common languages (Web) (Web) (App) (App) # Inbox Source: https://docs.suprsend.com/docs/inbox-quick-start Set up guide to send In-app Inbox notifications via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### See a live demo of Inbox Implementation in playground You'll also get the sample code for all types of Inbox views in the [GitHub repo](https://inbox-playground.suprsend.com/) linked in the playground. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period. 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on **`+ Create workflow`** button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger): You can trigger these events from your frontend application or from your backend systems, depending on the use case. (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * Template in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Whatsapp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. [Learn more about how to design In-app Inbox template here](/docs/in-app-inbox-template). ![](https://files.readme.io/e5b9db7-template_edit.gif) 3. **Functional nodes (Optional)**- These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the Workflow You can trigger a test workflow directly from dashboard by clicking on '**`Test`**' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**- End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id` within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile](/docs/users#creating-user-profile-on-suprsend) first for event based trigger.In Sandbox environment, a sample user with your registered email ID is pre-created for testing. You can always add more users or edit existing user profile from subscriber page on UI. 2. **Data or Event Properties**- This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow)
**Sample Payload for API based trigger:** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys ](https://app.suprsend.com/en/sandbox/developers/api-keys). Inbox channel is automatically updated in user profile on user creation. So, you won't need to pass Inbox channel in your trigger call. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Integrate Inbox in your product We have ready UI codes to add In-App Inbox in your web and mobile application. All you have to do is copy the code into your project and you are good to go. Here are respective codes and example apps to implement Inbox as per your tech stack. | Language | Documentation | Example App / Code | | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | React (Web) | [link](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/intergration.md) | not needed. It is a drop-in component of Inbox with pre-designed UI and available [customization options](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/customization.md) to make it native looking. | | React Headless (Web)- to design custom Inbox UI | [link](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/headless.md) | [https://github.com/suprsend/suprsend-inbox-example](https://github.com/suprsend/suprsend-inbox-example) | | Angular (Web) | [link](/docs/web-components-integration) | [https://github.com/suprsend/angular-example](https://github.com/suprsend/angular-example) | | Embeddable Inbox (Web)- used in other javascript applications like **Vue**, **Next** etc. | [link](/docs/web-components-integration) | [https://github.com/suprsend/suprsend-web-inbox/blob/main/index.html](https://github.com/suprsend/suprsend-web-inbox/blob/main/index.html) | | React Native (Mobile App) | [link](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/intergration.md-native) | [https://codesandbox.io/s/suprsend-react-headless-example-qkxobd?file=/src/App.tsx](https://codesandbox.io/s/suprsend-react-headless-example-qkxobd?file=/src/App.tsx) | | Flutter (Mobile App) | [link](/docs/inbox-flutter) | [https://github.com/suprsend/suprsend-flutter-sdk/blob/main/example/lib/main.dart](https://github.com/suprsend/suprsend-flutter-sdk/blob/main/example/lib/main.dart) | ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Also, you'll have to change the Inbox secret added in your server-side code to generate subscriber\_id for your productions users. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # React Native (Headless) Source: https://docs.suprsend.com/docs/inbox-react-native Integrate SuprSend inbox in React Native using the headless library and hooks. The Headless Inbox library provides hooks that can be integrated into React Native components for building inbox, and toast functionality in your applications. ## Installation ```shell npm theme={"system"} npm install @suprsend/react-headless ``` ```shell yarn theme={"system"} yarn add @suprsend/react-headless ``` ## Initialization Enclose your app in **SuprSendProvider** like below and pass the `workspace key`, `distinct_id`, and [`subscriber_id`](/docs/hmac-authentication). ```javascript App.js theme={"system"} import { SuprSendProvider } from "@suprsend/react-headless"; function App() { return ( ); } ``` SuprSend hooks can only be used inside of SuprSendProvider. ## Adding SuprSend inbox component ### 1) useBell hook This hook provides unSeenCount, markAllSeen which is related to the Bell icon in the inbox **unSeenCount**: Use this variable to show the unseen notification count anywhere in your application. **markAllSeen**: Used to mark seen for all notifications. Call this method on clicking the bell icon so that it will reset the bell count to 0. ```javascript Bell.js theme={"system"} import { useBell } from "@suprsend/react-headless"; function Bell() { const { unSeenCount, markAllSeen } = useBell(); return

markAllSeen()}>{unSeenCount}

; } ```
### 2) useNotifications hook This hook provides a notifications list, unSeenCount, markClicked, markAllSeen. **notifications**: List of all notifications. This array can be looped and notifications can be displayed. **unSeenCount**: Use this variable to show the unseen notification count anywhere in your application. **markClicked**: Method used to mark a notification as clicked. Pass notification id which is clicked as the first param. **markAllRead**: This method is used to mark all individual notifications as seen. Add a button anywhere in your notification tray as **Mark all as read** and on clicking of that call this method. ```javascript Notifications.js theme={"system"} import { useNotifications } from "@suprsend/react-headless"; function Notifications() { const { notifications, markAllRead } = useNotifications(); return (

Notifications

{markAllRead()}}>Mark all read

{notifications.map((notification) => { return ( ); })}
); } function NotificationItem({ notification, markClicked }) { const message = notification.message; const created = new Date(notification.created_on).toDateString(); return (
{ markClicked(notification.n_id); }} style={{ backgroundColor: "lightgray", margin: 2, borderRadius: 5, padding: 4, cursor: "pointer", }} >

{message.header}

{!notification.seen_on &&

*

}

{message.text}

{created}

); } ```
##### Notification object structure: ```javascript Notification.js theme={"system"} interface IRemoteNotification { n_id: string n_category: string created_on: number seen_on?: number message: IRemoteNotificationMessage } interface IRemoteNotificationMessage { header: string schema: string text: string url: string extra_data?: string actions?: { url: string; name: string }[] avatar?: { avatar_url?: string; action_url?: string } subtext?: { text?: string; action_url?: string } } ``` ### 3) useEvent hook This hook is an event emitter when and takes arguments event type and callback function when the event happens. Must be called anywhere inside SuprSendProvider **Handled Events:** 1. new\_notification: Called when the new notification occurs can be used to show toast in your application. ```javascript Sample.js theme={"system"} import { useEvent } from "@suprsend/react-headless"; function Home() { useEvent("new_notification", (newNotification) => { console.log("new notification data: ", newNotification); alert("You have new notifications"); }); return

Home

; } ```
## Example implementation Check the [example implementation.](https://codesandbox.io/s/suprsend-react-headless-example-qkxobd?file=/src/App.tsx) *** # Integrate Android SDK Source: https://docs.suprsend.com/docs/integrate-android-sdk SDK Integration steps to enable AndroidPush notification in your native android app. ## Installation ```groovy Add it in your root build.gradle at the end of repositories theme={"system"} allprojects { repositories { ... mavenCentral() } } ``` Add following line of code inside dependencies in app build.gradle ```java build.gradle theme={"system"} dependencies { implementation("com.suprsend:native:1.0.1") } ``` ## Initialization To integrate SuprSend in your Android app, initialise the Suprsend android sdk in MainApplicaNtion inside `onCreate` method and just above `super.onCreate()` line. ```kotlin kotlin theme={"system"} class MyApplication : Application() { override fun onCreate() { SSApi.init(this,"WORKSPACE KEY", "WORKSPACE SECRET") super.onCreate() } } ``` Replace **`WORKSPACE KEY`** and **`WORKSPACE SECRET`** with your workspace values. You will get both the tokens from [Suprsend API Keys ](https://app.suprsend.com/en/demo/developers/api-keys)[page ](https://app.suprsend.com/en/demo/developers/api-keys)***` (Settings page -> "API Keys"`***` section)` ```kotlin kotlin theme={"system"} val ssApi = SSApi.getInstance() ``` ## Logging By default the logs of SuprSend SDK are disabled. We recommend you to enable the SDK logs by setting its value to VERBOSE. You can enable the logs just in debug mode while in development by below condition. ```kotlin kotlin theme={"system"} ssApi.setLogLevel(level: LogLevel) if (BuildConfig.DEBUG) ssApi.setLogLevel(LogLevel.VERBOSE) or //You can send the SDK exception to your Crashlytics server SSApi.setLogger(object : LoggerCallback { override fun i(tag: String, message: String) { // you will receive SDK info messages here } override fun e(tag: String, message: String, throwable: Throwable?) { throwable ?: return //Ex - FirebaseCrashlytics.getInstance().recordException(throwable) } }) ``` ## ProGuard Rules (For release build) For creating a release build you will need to add these below pro-guard rules: ```yaml yaml theme={"system"} # SuprSend Sdk -dontwarn app.suprsend.** -keep class app.suprsend.**{*;} # Xiaomi -keep class app.suprsend.xiaomi.SSXiaomiReceiver {*;} #SDK has been obfuscated and compressed to avoid class not found error due to re-obfuscation. -keep class com.xiaomi.** #If the compiling Android version you are using is 23, you can prevent getting a false warning which makes it impossible to compile. -dontwarn com.xiaomi.push.** -keep class com.xiaomi.mipush.sdk.MiPushMessage {*;} -keep class com.xiaomi.mipush.sdk.MiPushCommandMessage {*;} -keep class com.xiaomi.mipush.sdk.PushMessageReceiver {*;} -keep class com.xiaomi.mipush.sdk.MessageHandleService {*;} -keep class com.xiaomi.push.service.XMJobService {*;} -keep class com.xiaomi.push.service.XMPushService {*;} -keep class com.xiaomi.mipush.sdk.PushMessageHandler {*;} -keep class com.xiaomi.push.service.receivers.NetworkStatusReceiver {*;} -keep class com.xiaomi.push.service.receivers.PingReceiver {*;} -keep class com.xiaomi.mipush.sdk.NotificationClickedActivity {*;} ``` *** # Integrate Go SDK Source: https://docs.suprsend.com/docs/integrate-go-sdk Install & Initialize SuprSend Go SDK using your workspace credentials for sending notifications. ## Installation Install `suprsend-go` sdk ```shell bash theme={"system"} go get github.com/suprsend/suprsend-go ``` ## Initialization For initializing SDK, you need workspace\_key and workspace\_secret. You will get both the tokens from your [Suprsend dashboard](https://app.suprsend.com/en/production/developers/api-keys) (Developers -> API Keys). ```go Request theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { opts := []suprsend.ClientOption{ // suprsend.WithDebug(true), } suprClient, err := suprsend.NewClient("__workspace_key__", "__workspace_secret__", opts...) if err != nil { log.Println(err) } } ``` *** # Integrate Java SDK Source: https://docs.suprsend.com/docs/integrate-java-sdk Install & Initialize SuprSend Java SDK using your workspace credentials for sending notifications. ## Installation For SDK installation, you'll have to add the SuprSend jar file. You can include the jar using following two ways: `suprsend-java-sdk` is present as a maven dependency on maven central. Add following code to your pom.xml to include the sdk ```xml xml theme={"system"} com.suprsend suprsend-java-sdk 0.5.0 ``` Click [here](https://github.com/suprsend/suprsend-java-sdk/releases/download/v0.5.0/suprsend-java-sdk-0.5.0-jar-with-dependencies.jar) to download the latest version of java SDK from releases section and add it as an External Jar in your build path. suprsend-java-sdk is available as a JAR with name- `suprsend-java-sdk-0.5.0-jar-with-dependencies.jar` **JDK version 8 and above is supported** Please check your Java development kit version. If it is lower than supported version, upgrade it to the latest version ## Initialization For initializing SDK, you need **WORKSPACE KEY** and **WORKSPACE SECRET**. ```java Request theme={"system"} import suprsend.Suprsend; Suprsend suprsend = new Suprsend("WORKSPACE KEY", "WORKSPACE SECRET"); ``` Replace `WORKSPACE KEY` and `WORKSPACE SECRET` with your workspace values. You will get both the tokens from Developers -> API Keys section. ## Constructor to test SDK in debug mode Constructor allows you to view HTTP calls to Suprsend in your console. The final parameter is a boolean parameter which denotes whether value for "debug" is true or false. Default value for the same is false. ```java Request theme={"system"} import suprsend.Suprsend; Suprsend suprsend = new Suprsend("WORKSPACE KEY", "WORKSPACE SECRET", true); ``` *** # Integrate Javascript SDK Source: https://docs.suprsend.com/docs/integrate-javascript-sdk Web SDK Integration to enable WebPush, Preferences, & In-app feed in javascript websites like React, Vue, and Next.js. 📘 **Upgrading major version of SDK:** We have changed the web SDK authentication from workspace key-secret to public key and JWT based authentication. This is done to improve security in frontend applications. * Refer the v1 SDK [documentation](https://github.com/suprsend/suprsend-browser-sdk/tree/main/docs) * For migrating to v2, follow this [guide](/docs/migration-guide-from-v1) This is the client JavaScript SDK used to integrate SuprSend features like Webpush, Preferences in JavaScript websites like React, Next.js, Angular, Vue.js etc. ## Installation ```shell npm theme={"system"} npm install @suprsend/web-sdk@latest ``` ```shell yarn theme={"system"} yarn add @suprsend/web-sdk@beta ``` ## Integration Create suprSendClient instance and use same instance to access all the methods of SuprSend library. ```typescript syntax theme={"system"} import SuprSend from '@suprsend/web-sdk'; export const suprSendClient = new SuprSend(publicApiKey: string); ``` | Params | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------ | | publicApiKey\* | This is public Key used to authenticate API calls to SuprSend. Get it in SuprSend dashboard **ApiKeys -> Public Keys** section | Authenticate user so that all the actions performed after authenticating will be w\.r.t that user. This is mandatory step and need to be called before using any other method. This is usually performed after successful login and on reload of page to re-authenticate user. ```typescript syntax theme={"system"} const authResponse = await suprSendClient.identify( distinctId: any, userToken?: string, // only needed in production environments for security { refreshUserToken: (oldUserToken: string, tokenPayload: Dictionary) => Promise } ); ``` | Properties | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | distinctId\* | Unique identifier to identify a user across platform. | | userToken | Mandatory when enhanced security mode is on. This is ES256 JWT token generated in your server-side. Refer [docs](/docs/client-authentication#enhanced-security-mode-with-signed-user-token) to create userToken. | | refreshUserToken | This function is called by SDK internally to get new userToken before existing token is expired. The returned string is used as the new userToken. | **Returns:** `Promise` #### 2.1 Check if user is authenticated This method will check if user is authenticated i.e. `distinctId` is attached to SuprSend instance. To check for userToken also pass checkUserToken flag true. ```typescript syntax theme={"system"} suprSendClient.isIdentified(checkUserToken?: boolean): boolean ``` This will remove user data from SuprSend instance. This is usually called on logout action. ```typescript syntax theme={"system"} await suprSendClient.reset(); ``` **Returns:** `Promise` ## Response structure Almost all the methods in this SDK return response type `Promise` ```typescript syntax theme={"system"} interface ApiResponse { status: 'success' | 'error'; statusCode?: number; error?: { type?: string; message?: string }; body?: any; } // success response { status: "success", body?: any, statusCode?: number } // error response { status: "error", error: { type: string, message: string } } ``` *** # Integrate Node SDK Source: https://docs.suprsend.com/docs/integrate-node-sdk Install & Initialize SuprSend NodeJS SDK using your workspace credentials for sending notifications. ## Installation ```shell npm theme={"system"} npm install @suprsend/node-sdk@latest ``` ```shell yarn theme={"system"} yarn add @suprsend/node-sdk@latest ``` ## Initialization ```javascript javascript theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("WORKSPACE KEY", "WORKSPACE SECRET"); ``` Replace `WORKSPACE KEY` and `WORKSPACE SECRET` with your workspace values. You will find them on SuprSend Dashboard Developers -> API Keys page. *** # Integrate Python SDK Source: https://docs.suprsend.com/docs/integrate-python-sdk Install & Initialize SuprSend Python SDK using your workspace credentials for sending notifications. ## Installation You can skip this step if you already have this package installed in your system. ```shell bash theme={"system"} # if you are using linux / debian based systems sudo apt install libmagic # If you are using macOS brew install libmagic ``` ```shell bash theme={"system"} $ pip install suprsend-py-sdk # to upgrade to latest SDK version $ pip install suprsend-py-sdk --upgrade ``` **Python version 3.7 or later is required** If your python3 version is lower than 3.7, upgrade it. ## Initialization For initializing SDK, you need workspace\_key and workspace\_secret. You will get both the tokens from your [Suprsend dashboard](https://app.suprsend.com/en/production/developers/api-keys) (Developers -> API Keys). ```python python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("WORKSPACE KEY", "WORKSPACE SECRET") ``` *** # Invoke Workflow Source: https://docs.suprsend.com/docs/invoke-workflow Trigger another workflow as a step in running workflow. Use this node to trigger a workflow from within another workflow. It is typically used when the recipient list or data context changes between workflow steps. A common example is escalation workflows, where if a team member doesn't take action, the workflow escalates to notify their manager. The payload for the triggered workflow is dynamically computed using data from the current workflow run. All fields support variables in [JSONNET](https://jsonnet.org/ref/language.html) format. ## Constructing Workflow Payload ### Recipient Recipient refers to the recipient of the target workflow. It can be: * The current workflow's **recipient** or **actor** * Any key from the node's **input data**, including data added or modified during the workflow run (e.g. from fetch or webhook nodes). 📘 Please note Data modifications after a batch or digest node will not affect the invoke node's variables, which means `$batched...` variables will not be available in this node's input. You can refer to input data variables in JSONNET format, prefixed by `data` key. The same format is followed for adding variable in actor and data field as well. Following data types are available in workflow: | Data Type | Referring in JSONNET | Description | | ------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Input Payload | pass as `data.` | This includes the data from your trigger payload and any data modified or added by nodes such as data transform, or webhook/fetch nodes before the invoke node. | | Actor | pass as `data["$actor"].` | Actor properties. In case of event trigger, `distinct_id` works both as actor and recipient and for inline workflow trigger, it is the `distinct_id` in actor object. | | Recipient | pass as `data["$recipient"].` | Recipient properties. It is the `distinct_id` in your event trigger or the key value defined in [override recipient](/docs/override-recipient-list) field. For inline workflow trigger, it is the `distinct_id` in recipient object. | | Tenant | pass as`data["$brand"].` | Tenant properties corresponding to the tenant\_id passed in workflow trigger. | ### Actor The actor defines the user who performed the action in the target workflow. It can be: * The current workflow's **actor** or **recipient**. * Any key from the node's **input data**, referred in the same format as in the [recipient](/docs/invoke-workflow#recipient) field. ### Data This defines the data context for the target workflow, which is used to render templates and workflow variables. By default, data from the current workflow run is passed. * To exclude the current workflow data, uncheck the `Append current workflow execution data` option. * Additional data can be provided in the data JSON field, using the node's input data in JSONNET format, referred in the same format as in the [recipient](/docs/invoke-workflow#recipient) field. #### Merge strategy We use a shallow merge strategy to combine extra input data (defined in data JSON field) with the current workflow execution data. If the same key exists in both, the value from data JSON field will override the value in existing workflow data. *** # APNS Push Integration Source: https://docs.suprsend.com/docs/ios-apns-push Integrate APNS Push in your Swift application ## Adding push capability 1. Inside your Target select **signing and capabilities** 2. Click on **+capabilities** and select **Push Notifications** ## Registering for push notifications AppDelegate should implement `UNUserNotificationCenterDelegate` from `UserNotifications` and then add `registerForPush` method to AppDelegate and call that method inside `application(_:didFinishLaunchingWithOptions:)`. ```swift AppDelegate.swift theme={"system"} import UserNotifications // Add this class AppDelegate: NSObject, UNUserNotificationCenterDelegate { // implement UNUserNotificationCenterDelegate func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.configure(publicKey: "PUBLIC_KEY") registerForPush() // add this return true } // add this method func registerForPush() { UNUserNotificationCenter.current().delegate = self // this willregister push delegate // ask user for permission // options = [.sound, .badge, .alert] for explicit authorization // options = [.badge, .alert, .sound, .provisional] for provisional authorization UNUserNotificationCenter.current().requestAuthorization( options: [.sound, .badge, .alert], completionHandler: { granted, error in if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } }) } } ``` ### Asking user permission Explicit authorization allows you to display alerts, add a badge to the app icon, or play sounds whenever a notification is delivered. In this type of authorization, the request is made the first time user launches your app. If the user denies the request, you can't send subsequent prompts to send the notification. Explicit authorization is default authorization method as it automatically sets alert, sound and badge as soon as the user allows this request. Provisional Authorization (Supported in iOS 12.0 and above) are sent quietly to the users —they don’t interrupt the user with a sound or banner. Also, they will not be shown when your app is in foreground. First time this type of notifications are sent, user is asked to "Keep" or "Turn off" the notifications. Further notifications continue to be sent if they click on "Keep". ## Adding delegate methods for push handling ```swift AppDelegate.swift theme={"system"} func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() Task { await SuprSend.shared.user.addPush(token) } } func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { SuprSend.shared.push.application( application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) completionHandler(.newData) } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { SuprSend.shared.push.userNotificationCenter( center, didReceive: response, withCompletionHandler: completionHandler) completionHandler() } func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { SuprSend.shared.push.userNotificationCenter( center, willPresent: notification, withCompletionHandler: completionHandler) if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { // Fallback on earlier versions completionHandler([.alert, .badge, .sound]) } } ``` ## Changes in Notification Service Extension ### Adding Notification Service Extension 1. In Xcode go to **File > New > Target**. 2. Select Notification Service Extension from the template list. 3. Then in Next popup give it any product name, select your team, select swift language and click finish. After clicking on "Finish", a folder will be created with your given product name. ### Installing SuprSend SDK in Notification Service In Xcode, go to File > AddPackages to add a new dependency. In that search bar, add suprsend-swift-sdk project github url `https://github.com/suprsend/suprsend-swift-sdk` and keep the default version settings and click `Add Package` button. In second dialog box, select your Notification Service target from dropdown and click `Add Package` button. Add the SuprSendSwift SDK to your Podfile as dependency to Notification Service Extension like below and then run `pod install`. ```ruby Podfile theme={"system"} target '' do pod "SuprSend" end ``` ### Adding code in Notification Service Paste the below code in NotificationService.swift file. Replace `YOUR_PUBLIC_KEY` with your public key. ```swift NotificationService.swift theme={"system"} import UIKit import UserNotifications import SuprSendSwift class NotificationService: SuprSendNotificationService { override func publicKey() -> String { "YOUR_PUBLIC_KEY" } } ``` ## Handling deep links By default SDK will handle only http deeplinks. If you want to handle custom deeplinks, implement `SuprSendDeepLinkDelegate` in AppDelegate class and add the below code. ```swift AppDelegate.swift theme={"system"} // implement SuprSendDeepLinkDelegate class AppDelegate: NSObject, UNUserNotificationCenterDelegate, SuprSendDeepLinkDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.configure(publicKey: "YOUR_PUBLIC_KEY") SuprSend.shared.setDeepLinkDelegate(self) // Add this registerForPush() return true } func shouldHandleSuprSendDeepLink(_ url: URL) -> Bool { print("Handling URL: \(url)") // write your linking logic here and return false return false } } ``` ## Final AppDelegate.swift file Example of `AppDelegate.swift` file with all the above code. ```swift AppDelegate.swift theme={"system"} import Foundation import SuprSend import UIKit class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, SuprSendDeepLinkDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.enableLogging() SuprSend.shared.configure(publicKey: "Testing") SuprSend.shared.setDeepLinkDelegate(self) registerForPush() return true } func registerForPush() { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization( options: [.sound, .badge, .alert], completionHandler: { granted, error in if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } }) } func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() Task { await SuprSend.shared.user.addiOSPush(token) } } func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { SuprSend.shared.push.application( application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler ) completionHandler(.newData) } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { SuprSend.shared.push.userNotificationCenter( center, didReceive: response, withCompletionHandler: completionHandler) completionHandler() } func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { SuprSend.shared.push.userNotificationCenter( center, willPresent: notification, withCompletionHandler: completionHandler) if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { // Fallback on earlier versions completionHandler([.alert, .badge, .sound]) } } func shouldHandleSuprSendDeepLink(_ url: URL) -> Bool { print("Handling URL: \(url)") UIApplication.shared.open(url, options: [:], completionHandler: nil) return false } } ``` # Events and User methods Source: https://docs.suprsend.com/docs/ios-events-and-user-methods ## Trigger Events You can trigger events from client to SuprSend using `track` method. This can be used to trigger [event-based workflows](/docs/trigger-workflow#event-based-trigger). ```swift syntax theme={"system"} await SuprSend.shared.track(event: String, properties: [String: Any]?) ``` ```swift example theme={"system"} await SuprSend.shared.track(event: "test", properties: ["name": "john doe"]) ``` **Returns:** `async -> APIResponse` ## Update User Profile **Returns:** `async -> APIResponse` ### Update User channels Set user channel related information using following methods. Its recommended to use SuprSend's Backend SDK's to set user channels instead of Client SDK's. ```swift syntax theme={"system"} await SuprSend.shared.user.addEmail(String) await SuprSend.shared.user.removeEmail(String) // mobile should be as per E.164 standard: https://www.twilio.com/docs/glossary/what-e164 await SuprSend.shared.user.addSMS(String) await SuprSend.shared.user.removeSMS(String) // mobile should be as per E.164 standard await SuprSend.shared.user.addWhatsapp(String) await SuprSend.shared.user.removeWhatsapp(String) ``` ### Update User properties This is the list of available user update methods: This method will set users timezone. Timezone value should be in [IANA timezone format](https://timeapi.io/documentation/iana-timezones). ```swift syntax theme={"system"} await SuprSend.shared.user.setTimezone(String) ``` ```swift example theme={"system"} await SuprSend.shared.user.setTimezone("America/Bogota"); ``` This method will set users preferred language. Language value should be in [ISO 639-1 Alpha-2 format](https://gist.github.com/jrnk/8eb57b065ea0b098d571). ```swift syntax theme={"system"} await SuprSend.shared.user.setPreferredLanguage(String) ``` ```swift example theme={"system"} await SuprSend.shared.user.setPreferredLanguage("en"); ``` Set is used to set the custom user property or properties. If already property is already present value will be replaced. ```swift syntax theme={"system"} await SuprSend.shared.user.set(key: String, value: String) await SuprSend.shared.user.set(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.set(key: "name", value: "John Doe"); await SuprSend.shared.user.set(properties: ["name": "John Doe", "designation": "manager"]); ``` This method will remove user property. To remove channel pass `$email`, `$sms`, `$whatsapp`. ```swift syntax theme={"system"} await SuprSend.shared.user.unset(key: String) await SuprSend.shared.user.unset(keys: [String]) ``` ```swift example theme={"system"} await SuprSend.shared.user.unset(key: "wishlist"); await SuprSend.shared.user.unset(keys: ["wishlist", "$email"]); ``` This method will add a value to the list for a given property. ```swift syntax theme={"system"} await SuprSend.shared.user.append(key: String, value: String) await SuprSend.shared.user.append(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.append(key: "wishlist", value: "iphone12"); await SuprSend.shared.user.append(properties: ["wishlist": "iphone12", "cart": "Apple airpods"]); ``` This method will remove a value from the list for a given property. ```swift syntax theme={"system"} await SuprSend.shared.user.remove(key: String, value: String) await SuprSend.shared.user.remove(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.remove(key: "wishlist", value: "iphone12"); await SuprSend.shared.user.remove(properties: ["wishlist": "iphone12", "cart": "Apple airpods"]); ``` This method is similar to set method but values once set cannot be updated. ```swift syntax theme={"system"} await SuprSend.shared.user.setOnce(key: String, value: String) await SuprSend.shared.user.setOnce(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.setOnce(key: "DOB", value: "1991-10-02"); await SuprSend.shared.user.setOnce(properties: ["first_login": "2021-11-02", "DOB": "1991-10-02"]); ``` Add the given amount to an existing user property. If the user does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number as the value. ```swift syntax theme={"system"} await SuprSend.shared.user.increment(key: String, value: Int) await SuprSend.shared.user.increment(properties: [String: Int]) ``` ```swift example theme={"system"} await SuprSend.shared.user.increment(key: "login_count", value: 1); await SuprSend.shared.user.increment(properties: ["login_count": 1, "order_count": 1]); ``` Keys starting with `ss_` or `$` are reserved and will be ignored. *** # Integration Source: https://docs.suprsend.com/docs/ios-integration Integrate SuprSend SDK in your Swift project **`SuprSendSdk` is deprecated. Please migrate to `SuprSend` SDK** This documentation is for new version of iOS sdk. If you are using older version of sdk `SuprSendSdk` please refer [documentation](https://github.com/suprsend/SuprSend-iOS-SDK/tree/main/documentation) ## Installation There are two ways you can install SuprSend SDK into your app: In Xcode, go to File > AddPackages to add a new dependency. In that search bar, add suprsend-swift-sdk project github url `https://github.com/suprsend/suprsend-swift-sdk` and keep the default version settings and click `Add Package` button. In second dialog box, select your project's target from dropdown and click `Add Package` button. Add the SuprSendSwift SDK to your Podfile as `pod "SuprSend"` and run `pod install` to install the SDK. ## Integration Import SDK inside `AppDelegate.swift` and then initialize the SuprSend class inside `application(_:didFinishLaunchingWithOptions:)` method. ```swift AppDelegate.swift theme={"system"} import SuprSend SuprSend.shared.configure(publicKey: "YOUR_PUBLIC_API_KEY") ``` | Params | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------ | | publicApiKey\* | This is public Key used to authenticate API calls to SuprSend. Get it in SuprSend dashboard **ApiKeys -> Public Keys** section | Authenticate user so that all the actions performed after authenticating will be w\.r.t that user. This is mandatory step and need to be called before using any other method. This is usually performed after successful login and on reload of page to re-authenticate user. ```swift syntax theme={"system"} await SuprSend.shared.identify(distinctID: "YOUR_USER_ID", userToken: userTokenData, options: AuthenticateOptions(refreshUserToken: {oldUserToken,tokenPayload in return refreshedUserToken()})); ``` | Properties | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | distinctId\* | Unique identifier to identify a user across platform. | | userToken | Mandatory when enhanced security mode is on. This is ES256 JWT token generated in your server-side. Refer [docs](/docs/client-authentication#enhanced-security-mode-with-signed-user-token) to create userToken. | | refreshUserToken | This function is called by SDK internally to get new userToken before existing token is expired. The returned string is used as the new userToken. | **Returns:** `async -> APIResponse` #### 2.1 Check if user is authenticated This method will check if user is authenticated i.e. `distinctId` is attached to SuprSend instance. To check for userToken also pass checkUserToken flag true. ```swift syntax theme={"system"} SuprSend.shared.isIdentified(checkUserToken: true) ``` This will remove user data from SuprSend instance. This is usually called on logout action. ```swift syntax theme={"system"} await SuprSend.shared.reset() ``` **Returns:** `async -> APIResponse` ## Response Structure ```swift syntax theme={"system"} struct APIResponse { /// The status of the response (success or error). public let status: ResponseStatus /// The HTTP status code associated with the response. public let statusCode: StatusCode? /// The JSON response body. public let body: ResponseBody? /// Any error that occurred during the request. {type: string, message: string} public let error: ResponseError? } ``` ``` ``` # Preferences Source: https://docs.suprsend.com/docs/ios-preferences Preferences in your iOS application ### Pre-Requisites * Integration of [iOS SDK](/docs/ios-integration) * [Configure notification categories](/docs/user-preferences#create-notification-category) on SuprSend dashboard ## Understanding preference structure This is how a typical preference page will look like: Description Preference Page contains 2 sections: 1. Category-level preference settings (Sections) * [Sections](/docs/js-preferences#11-sections) * [Categories](/docs/js-preferences#12-categories-sections---sub-categories) * [Category Channel](/docs/js-preferences#13-category-channels-sections---sub-categories---channels) Description 2. [Overall Channel-level preference](/docs/js-preferences#2-overall-channel-preferences) Description ### Preferences data structure ```swift Preferences Types theme={"system"} struct PreferenceData: Codable { var sections: [Section]? var channelPreferences: [ChannelPreference]? enum CodingKeys: String, CodingKey { case sections case channelPreferences = "channel_preferences" } } struct ChannelPreference: Codable { var channel: String var isRestricted: Bool enum CodingKeys: String, CodingKey { case channel case isRestricted = "is_restricted" } } struct Section: Codable { var name: String? var description: String? var subcategories: [Category]? } struct Category: Codable { var name: String var category: String var description: String? var preference: PreferenceOptions var isEditable: Bool var channels: [CategoryChannel]? enum CodingKeys: String, CodingKey { case name case category case description case preference case isEditable = "is_editable" case channels } } struct CategoryChannel: Codable { var channel: String var preference: PreferenceOptions var isEditable: Bool enum CodingKeys: String, CodingKey { case channel case preference case isEditable = "is_editable" } } enum PreferenceOptions: String, Codable { case optIn = "opt_in" case optOut = "opt_out" } enum ChannelLevelPreferenceOptions: String, Codable { case all = "all" case required = "required" } ``` ```swift Example theme={"system"} { "sections": [ { "name": null, "subcategories": [ { "name": "Payment and History", "category": "payment-and-history", "description": "Send updates related to my payment history.", "preference": "opt_in", "is_editable": false, "channels": [ { "channel": "androidpush", "preference": "opt_in", "is_editable": true }, { "channel": "email", "preference": "opt_in", "is_editable": false } ] } ] }, { "name": "Product Updates", "description": "Non-marketing notifications related to authentication, activity updates, reminders etc.", "subcategories": [ { "name": "Newsletter", "category": "newsletter", "description": "Send updates on new feature in the product", "preference": "opt_in", "is_editable": true, "channels": [ { "channel": "androidpush", "preference": "opt_in", "is_editable": true }, { "channel": "email", "preference": "opt_out", "is_editable": false } ] } ] } ], "channel_preferences": [ { "channel": "androidpush", "is_restricted": false }, { "channel": "email", "is_restricted": true } ] } ``` ### 1.1 Sections This contains the name, description, and subcategories. We have to loop through the sections list and for every section item if there is a name and description present, then show the heading, and if a subcategories list is present, loop through that subcategories list and show all subcategories under that section heading. Subcategories can exist without sections as the section is an optional field. In that case, the section's name will not be available. For sections where the name is not present, you can directly show its subcategories list without showing Heading for the section in UI. ```swift syntax theme={"system"} struct Section: Codable { var name: String? var description: String? var subcategories: [Category]? } ``` | Property | Description | | ------------- | --------------------------------------------------------- | | name | name of the section | | description | description of the section | | subcategories | data of all sub-categories to be shown inside the section | ### 1.2 Categories (sections -> sub-categories) This is the place where the user sets his category-level preferences. While looping through the subcategories list for every subcategory item, show the name and description in UI. ```swift syntax theme={"system"} struct Category: Codable { var name: String var category: String var description: String? var preference: PreferenceOptions var isEditable: Bool var channels: [CategoryChannel]? enum CodingKeys: String, CodingKey { case name case category case description case preference case isEditable = "is_editable" case channels } } ``` | Property | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | category | This key is the id of the category which is used while updating the preference. | | name | name of the category to be shown on the UI | | description | description of the category to be shown on the UI | | preference | This key indicates if the category's preference switch is on or off. Get **OPT\_IN** when the switch is on and **OPT\_OUT** when the switch is off | | is\_editable | Indicates if the preference switch button is disabled or not. If its value is false then the preference setting for that category can't be edited | | channels | data of all category channels to be shown below the sub-category. Loop through it to show checkboxes under every subcategory item. | ### 1.3 Category channels (sections -> sub-categories -> channels) This contains a list of channels, channel preference status and whether it's editable or not. While looping through the subcategory list for every subcategory item we have to loop through its channels list and for every channel to show channel level checkbox. Description ```swift syntax theme={"system"} struct CategoryChannel: Codable { var channel: String var preference: PreferenceOptions var isEditable: Bool enum CodingKeys: String, CodingKey { case channel case preference case isEditable = "is_editable" } } ``` | Property | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | channel | name of the channel to be shown on UI. The same key will be used as id of the channel while updating the preference. | | preference | This key indicates if the channel's preference switch is on or off. Get OPT\_IN when the switch is on and OPT\_OUT when the switch is off | | is\_editable | Indicates if the preference checkbox is disabled or not. If its value is false then the preference setting for that channel can't be edited | ### 2. Overall channel preferences It's a list of all channel-level preferences. We have to loop through the list and for each item, show the UI as given in the below image. Description ```swift syntax theme={"system"} struct ChannelPreference: Codable { var channel: String var isRestricted: Bool enum CodingKeys: String, CodingKey { case channel case isRestricted = "is_restricted" } } ``` | Property | Description | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | channel | name of the channel to be shown on UI. The same key will be used as id of the channel while updating the preference. | | is\_restricted | This key indicates the restriction level of channel. If restricted, notification will only be sent in the category where this channel is added as mandatory in notification category settings. **True** means Required radio button is selected. **False** means All radio button is selected. | ## Integration ### Get preferences data Use this method to get preferences data and create the preferences UI by following the above sections. This method should be called first before any update preference methods. ```swift syntax theme={"system"} await SuprSend.shared.preferences.getPreferences(args: Preferences.Args(tenantId: "")) ``` **Returns:** `async -> PreferenceAPIResponse` ### Update channel preference in category Calling this method will opt-in/opt-out users from that category-level channel. When the category's channel checkbox is editable and the user clicks on the checkbox you can call this method. ```swift syntax theme={"system"} await SuprSend.shared.preferences.updateChannelPreferenceInCategory( channel: "channel", preference: PreferenceOptions, category: "category" ) enum PreferenceOptions: String, Codable { case optIn = "opt_in" case optOut = "opt_out" } ``` **Returns:** `async -> PreferenceAPIResponse` Description ### Update category preference This is category level preference changing method. Calling this method will opt-in/opt-out user from that category. When the category is editable and the switch is toggled you can call this method. ```swift syntax theme={"system"} await SuprSend.shared.preferences.updateCategoryPreference(category: "category_value", preference: PreferenceOptions) enum PreferenceOptions: String, Codable { case optIn = "opt_in" case optOut = "opt_out" } ``` **Returns:** `async -> PreferenceAPIResponse` ### Update overall channel preference This method updated the channel-level preference of the user. ```swift syntax theme={"system"} await SuprSend.shared.preferences.updateOverallChannelPreference( channel: "channel", preference: ChannelLevelPreferenceOptions ) enum ChannelLevelPreferenceOptions: String, Codable { case all = "all" case required = "required" } ``` **Returns:** `async -> PreferenceAPIResponse` Description ### Event listeners All preferences update api's are optimistic updates. Actual API call will happen in background with 1 second debounce. Since its a background task SDK provides event listeners to get updated preference data based on API call status. Listen to this event listeners and update the UI accordingly. ```swift syntax theme={"system"} SuprSend.shared.emitter.on(.preferencesUpdated) { data in // update local store so that UI is updated with latest data } SuprSend.shared.emitter.on(.preferencesError) { error in // show error toast to user } ``` ## Example Preferences UI example code: [PreferencesView.swift](https://github.com/suprsend/suprsend-swift-sdk/blob/main/Example/SuprSendSwiftExample-iOS/Views/Profile/Preferences/PreferencesView.swift) and [PreferenceModel.swift](https://github.com/suprsend/suprsend-swift-sdk/blob/main/Example/SuprSendSwiftExample-iOS/Model/PreferenceModel.swift) # iOS Push Template Source: https://docs.suprsend.com/docs/ios-push-template How to design simple iOS Push template with click action and image. ## Design Template You can design template with a simple form editor tool. You can add variables with `Handlebarsjs` language. You can check how the message will look in the preview section on the right side. Once designed, you can save the push notification template by clicking on Save Draft. When you are ready, you can Publish Draft by providing a name to the version. This will become the Live version, and will be used whenever the associated workflow is triggered. ## iOS Push notification fields description | Field | Description | | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Title | Small message text box. Note that this field will be displayed in single line only, and very long content can get curtailed. Use handlebarsjs to add variables. | | Body | Large message text box. Use handlebarsjs to add variables. | 📘 **Note:** By default, Clicking on the Notification will redirect the users to your iOS App. We'll be soon be adding the option to add custom Action URL ## Adding dynamic content in iOS Push There will always be the case where you would require to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending the message. You'll need to pass these while triggering the communication from one of our frontend or backend SDKs. Here is a step-by-step guide on how to add dynamic content in iOS Push: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global `Mock data` button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing the iOS Push template. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable\_name}}` If you have declared the variables in the global `Mock data` button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of errors like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the `Mock data` button. To manually enter the variable name, follow the [handlerbarsjs guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below is an example of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the `Templates` section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "3,950.00" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "2,549.00" }, { "product_name": "RayBan Sunglasses", "product_price": "7,899.00" } ], "event": { "location": { "city": "San Francisco", "state": "California" }, "order_id": "11200123", "first_name": "Joe" }, "product_page": "https://www.suprsend.com" } ``` 1. To enter a nested variable, enter in the format `{{var1.var2.var3}}`. E.g. to refer to city in the example above, you need to enter `{{event.location.city}}` 2. To refer to an array element, enter in format `{{var1.[*index*].var2}}. E.g. to refer to `product\_name`of the first element of the array`array`, enter `\{\{array.\[0].product\_name}}\` 3. If you have any space in the variable name, enclose it in square bracket `{{event.[first name]}}` You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: * Make sure you have entered the variable name and the sample value in the `Mock data` button. * Make sure you have entered the correct variable name in the template, as per the `handlebarsjs` guideline. **What happens if there is variable mismatch at the time of sending?** At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. E.g. if there is an error in rendering iOS Push template, but email template is successfully rendered, iOS Push notification will not be triggered, but email notification will be triggered by SuprSend. *** # iOS Push Source: https://docs.suprsend.com/docs/ios-push-vendor-integration Guide to setup APNS iOS Push configuration in SuprSend. ## Add APNs configuration on SuprSend dashboard On the SuprSend dashboard, go to vendor page from side panel, select iOSpush and fill in below details. You'll get all these information from [Apple developer account](https://developer.apple.com/account/) | Form Field | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Mode/Environment | Use "Production" if you are adding it in your final app or testing app. Use "Development" if you are testing in your local environment | | Upload p8 file | You'll get .p8 key file from your apple developer account. Refer [section below](/docs/ios-push-vendor-integration#generating-p8-key-file-and-auth-key-id) to see how to generate .p8 file | | Auth Key ID | Auth key ID is a part of the filename of your .p8 file. Refer [section below](/docs/ios-push-vendor-integration#generating-p8-key-file-and-auth-key-id) to see how to get Auth key ID | | Team ID | You'll get Team ID from your apple developer account. Refer [section below ](/docs/ios-push-vendor-integration#how-to-get-team-id)to see how to get Team ID | | Bundle ID | Bundle ID is a unique identifier of your app. SuprSend will require this key to identify your app for sending push notification.[Refer section](/docs/ios-push-vendor-integration#how-to-get-bundle-id) to see how to get Bundle ID | ### Generate .p8 key file and Auth key ID To generate a `.p8 key` file, login to your [Apple developer account](https://developer.apple.com/account/) page, then select **Certificates, IDs & Profiles**. Under Certificates, IDs & Profiles section, select **"Keys"** Click on "`+`" button next to keys to add a new key On the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click “Continue” and “Register". Once the key is generated, **`download the p8 key`** file by clicking on "`Download`" The Auth Key filename will look like this: `**AuthKeyABCD1234.p8**`, the `**ABCD1234**` is the Key ID for this key, add this key ID in Auth key ID field on Vendor configuration page ### How to get team ID You can get team ID from your [Apple developer account membership page](https://developer.apple.com/account/#/membership) ### How to get bundle ID You can get this ID from your app project *** # Manage Users Source: https://docs.suprsend.com/docs/java-create-user-profile Manage user profiles and communication channels programmatically with the Java SDK. ## How Suprsend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). **Please note:** you cannot change a user's id once it has been set, so we recommend you use a non-transient id like a primary key rather than a phone number or email address. ## Create User To create a new user or to update an existing user, you'll have to fetch user instance. Call `supr_client.user.get_instance` to instantiate user object. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.Subscriber; public class UserEdit { public static void main(String[] args) throws Exception { getInstance(); } private static Subscriber getSuprClient() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); return suprsendClient; } private static Subscriber getInstance() throws SuprsendException { Suprsend suprsendClient = getSuprClient(); // Instiantiate user String distinctId = "_distinct_id_"; Subscriber user = suprClient.user.getInstance(distinctId); return user; } } ``` ```java Response theme={"system"} // Response structure { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } { "success": false, // error will be present in message "status": "fail", "status_code": 500, // http status code "message": "error message", } ``` ## Edit User To Edit user, you need to first fetch user instance, call all the update methods and save changes using `user.save` method. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.Subscriber; public class UserEdit { public static void updateProfile() throws Exception { Suprsend suprsendClient = getSuprClient(); // User Edit Instance String distinctID = "_distinct_id_"; Subscriber user = suprsendClient.user.getInstance(distinctID); // Edit Helper methods user.addEmail("example@example.com"); user.setTimezone("America/New_York"); // Save JSONObject response = user.save(); System.out.println(response); } } ``` Here's a list of all edit methods: Add communication channels on which you want to notify user. Push sand Inbox tokens are automatically tracked on user identification when the corresponding frontend SDK is integrated. Other channels (Email, SMS, Slack, MS teams, Whatsapp) need to be explicitly set in user profile. Use `user.add_*` method(s) to add user channels. ```java Request theme={"system"} // User Edit Instance String distinctID = "_distinct_id_"; Subscriber user = suprsendClient.user.getInstance(distinctID); // Add Email user.addEmail("example@example.com"); //Add SMS user.addSms("+919999999999"); //Add Whatsapp user.addWhatsapp("+919999999999"); // Add Androidpush token.Pass the vendor as 2nd param. user.addAndroidpush("androidpush_fcm_token__", "fcm"); // Add iospush token user.addIospush("__iospush_apns_token__"); // Add Slack using user email id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("email", "user@example.com"); user.addSlack(slackIdent); // Add Slack using member_id of the user if known JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("user_id", "U03XXXXXXXX"); user.addSlack(slackIdent); // Add Slack channel_id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("channel_id", "C04XXXXXXXX"); user.addSlack(slackIdent); // Add Slack incoming webhook JSONObject slackIdent = new JSONObject() .put("incoming_webhook", new JSONObject().put("url", "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX")) user.addSlack(slackIdent); // Add Webpush token json (VAPID) JSONObject webpush = new JSONObject() .put("endpoint", "__end_point__") .put("expirationTime", "") .put("keys", new JSONObject() .put("p256dh", "__p256dh__") .put("auth", "__auth_key__")); user.addWebpush(webpush, "vapid"); ``` Use `user.remove_*` method(s) to remove channels. ```java Request theme={"system"} // Remove Email user.removeEmail("example@example.com"); //Remove SMS user.removeSms("+919999999999"); //Remove Whatsapp user.removeWhatsapp("+919999999999"); // Remove Androidpush token.Pass the vendor as 2nd param. user.removeAndroidpush("androidpush_fcm_token__", "fcm"); // Remove iospush token user.removeIospush("__iospush_apns_token__"); // Remove Slack using user email id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("email", "user@example.com"); user.removeSlack(slackIdent); // Remove Slack using member_id of the user if known JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("user_id", "U03XXXXXXXX"); user.removeSlack(slackIdent); // Remove Slack channel_id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("channel_id", "C04XXXXXXXX"); user.removeSlack(slackIdent); // Remove Slack incoming webhook JSONObject slackIdent = new JSONObject() .put("incoming_webhook", new JSONObject().put("url", "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX")) user.removeSlack(slackIdent); // Remove Webpush token json (VAPID) JSONObject webpush = new JSONObject() .put("endpoint", "__end_point__") .put("expirationTime", "") .put("keys", new JSONObject() .put("p256dh", "__p256dh__") .put("auth", "__auth_key__")); user.removeWebpush(webpush, "vapid"); ``` This method will delete/unset all values in specified channel for user (ex: remove all emails attached to user). ```java Request theme={"system"} // --- To unset one channel, e.g. to delete all emails associated with user // -- Channel keys - ("$email","$whatsapp","$sms," $androidpush","$iospush","$webpush","$slack") user.unset("$email"); // --- multiple channels can also be deleted in one call by passing argument as a list ArrayList < String > channels = new ArrayList < > (Arrays.asList("$email", "$slack", "$androidpush", "$iospush", "$webpush", "$whatsapp")); user.unset(channels); ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```java Request theme={"system"} // User Edit Instance String distinctID = "_distinct_id_"; Subscriber user = suprsendClient.user.getInstance(distinctID); // Pass language in ISO 639-1 code user.setPreferredLanguage("es"); ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```java Request theme={"system"} // User Edit Instance String distinctID = "_distinct_id_"; Subscriber user = suprsendClient.user.getInstance(distinctID); // Pass IANA timezone user.setTimezone("America/New_York"); ``` Set is used to add custom user properties. It is an upsert function, meaning any existing property value with the same key will be overwritten on subsequent updates. ```java Request theme={"system"} user.set(key, value) user.set("name","John Doe") user.set({ key1: value1, key2: value2 }) user.set({"name": "John Doe","city": "San Francisco"}) ``` Works just like user.set, except it will not override already existing property values. This is useful for properties like first\_login\_date. ```java theme={"system"} user.set_once(key, value) user.set_once("first_login","2021-11-02") user.set_once({ key1: value1, key2: value2 }) user.set_once({"first_login": "2021-11-02","signup_date": "2021-11-02"}) ``` Unset is used to remove a property key. ```java theme={"system"} user.unset(key) user.unset("name") user.unset([key1, key2]) user.unset(["name","city"]) ``` This method will append a value to the array list. ```java theme={"system"} user.append(key, value) user.append("played_games", "game_1") user.append({ key1: value1, key2: value2 }) user.append({"played_games": "game_1", "liked_games": "game_2"}) ``` This method will remove a value from the array list. ```java theme={"system"} user.remove(key, value) user.remove("played_games", "game_1") user.remove({ key1: value1, key2: value2 }) user.remove({"played_games": "game_1", "liked_games": "game_2"}) ``` Increase or decrease integer values on consecutive action, like login count. To reduce a property, provide a negative number for the value. ```java theme={"system"} user.increment(key, value) user.increment("login_count", 1) user.increment({ key1: value1, key2: value2 }) user.increment({"login_count" : 1, "order_count" : 1}) ``` After calling `add_*/remove_*/unset` methods, don't forget to call `users.save()` since user edit is async update and the changes will be sent to SuprSend only after calling this method. ## Bulk Update Users There isn't any limit on number-of-records that can be added to `bulk_users` instance. Use `.append()` on bulk\_users instance to add however-many-records to call in bulk. ```java Request theme={"system"} //Creating bulk instance BulkSubscribers bulkIns = suprClient.bulkUsers.newInstance(); // Prepare multiple users String distinctID1 = "__distinct_id1__"; // User 1 User u1 = suprsendClient.user.getInstance(distinctID1); u1.addEmail("u1@example.com"); String distinctID2 = "__distinct_id2__"; // User 2 User u2 = suprsendClient.user.getInstance(distinctID2); u2.addEmail("u2@example.com"); // --- use .append on bulk instance to add one or more records bulkIns.append(u1); bulkIns.append(u2); // OR bulkIns.append(u1, u2); // Save JSONObject response = bulkIns.save(); System.out.println(response); ``` ```java Response theme={"system"} // Response structure import suprsend.BulkResponse; BulkResponse{status: 'success' | total: 2 | success: 2 | failure: 0 | warnings: 0} BulkResponse{status: 'fail' | total: 2 | success: 0 | failure: 2 | warnings: 0} BulkResponse{status: 'partial' | total: 2 | success: 1 | failure: 1 | warnings: 0} ``` **Bulk API supported in SDK version 0.2.0 and above:** Bulk API is supported in SuprSend python-sdk version 0.2.0 and above. If you are using an older version, please upgrade to the latest SDK version. ## Get user details Fetch User by passing `distinct_id` ```java Request theme={"system"} String distinctId = "_distinct_id_"; JSONObject res = suprClient.users.get(distinctId); System.out.println(response); ``` ```java Response theme={"system"} { "distinct_id": "_distinct_id_", "properties": { "name": "John Doe", "email": ["johndoe@example.com"], "created_at": "2024-01-01T12:00:00Z" }, "status": "active" } ``` ## Delete user Delete User by passing `distinct_id`. Delete action will take into immediate effect. ```java Request theme={"system"} String distinctId = "_distinct_id_"; JSONObject res = suprClient.users.delete(distinctId); System.out.println(response); ``` ```java Response theme={"system"} { "success": True, "status_code": 204 } ``` ## Get list of objects subscribed by user You can pass optional query parameters -`limit`,`before`,`after` ```java Request theme={"system"} String distinctId = "_distinct_id_"; // optional params to pass limit (default=10) and cursor pointer for fetching the next set of results HashMap < String, Object > opts = new HashMap < String, Object > () { { put("limit", 10); put("after", "01HFS04E4J29KHPYRK7HT3YQQ5"); } }; JSONObject res = suprClient.users.getObjectsSubscribedTo(distinctId, opts); System.out.println(response); ``` ```java Response theme={"system"} { "objects": [ { "object_id": "Frontend", "type": "Developers", "subscribed_at": "2024-02-20T10:15:30Z" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": false } } ``` ## Get lists subscribed by user You can pass optional query parameters -`limit`, `before`, `after` ```java Request theme={"system"} String distinctId = "_distinct_id_"; // optional params to pass limit (default=10) and cursor pointer for fetching the next set of results HashMap < String, Object > opts = new HashMap < String, Object > () { { put("limit", 10); put("after", "01HFS04E4J29KHPYRK7HT3YQQ5"); } }; JSONObject res = suprClient.users.getListsSubscribedTo(distinctId, opts); System.out.println(response); ``` ```java Response theme={"system"} { "lists": [ { "list_id": "product_updates", "subscribed_at": "2024-02-20T12:00:00Z", "status": "subscribed" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": true } } ``` # Objects Source: https://docs.suprsend.com/docs/java-objects Create, update, & manage objects and their subscriptions with Java SDK. Objects in SuprSend represent non-user entities such as organizations, teams , roles, and projects. Understand more about objects from our [objects documentation](/docs/objects) ## Upsert (create/update) an object Object updating is an upsert function, meaning it would always override existing key values on further updates. Object id and type is mandatory to create object. You can optionally pass object properties (to use in template or workflow condition) or channel information (send notification on object channels) in the payload. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; public class ObjectEdit { public static void main(String[] args) throws Exception { objectEditInstance(); } public static void getSuprClient() throws Exception { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); return suprsendClient; } public static void objectEditInstance() throws Exception { Suprsend suprsendClient = getSuprClient(); // Instiantiate object String objectType = "departments"; String objectId = "engineering"; // Object Payload to upsert properties and communication channels JSONObject payload = new JSONObject() .put("k1", "value1"); .put("$email", "devs@abc.com"); JSONObject object = suprClient.objects.upsert(objectType, objectId, payload); System.out.println(object); } } ``` ```java Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "k1": "value1" }, "created_at":"2025-01-20T19:21:42.030343+00:00", "updated_at":"2025-02-14T14:06:05.928675+00:00", "$inbox":[ { "value":"H4iGeGSHyXXXXXXSeuBb_PJGXXXgWu_LsXXXXXyiw", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ], "$email":[ { "value":"devs@abc.com", "status":"active", "perma_status":"active" } ] } ``` ## Edit an object There are 2 ways in which you can edit an object data. * Build edit payload yourself * Use helper methods provided by SDK (Recommended) ### 1. Build edit payload yourself Use this to modify an object, typically for removing channels or unsetting properties. The payload will follow the same structure as the [Object Edit](https://docs.suprsend.com/reference/edit-object-profile) API. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; public class ObjectEdit { public static void objectEditInstance() throws Exception { Suprsend suprsendClient = getSuprClient(); // Instiantiate object String objectType = "departments"; String objectId = "engineering"; // Object Payload JSONObject payload = new JSONObject().put("operations", new JSONObject[] { new JSONObject().put("$set", new JSONObject().put("k2", "value2")) }); JSONObject object = suprClient.objects.edit(objectType, objectId, payload); System.out.println(object); } } ``` ```java Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "k1": "value1", "k2": "value2" }, "created_at":"2025-03-14T15:31:59.151061+00:00", "updated_at":"2025-03-14T15:31:59.182616+00:00", "$inbox":[ { "value":"75Ev_m8yln0N_i1iXXXXXXr2eNOE_4ROXXXXXX1TgTc", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` ### 2. Edit using helper methods \[Recommended] It is possible to use the SDK's helper methods to perform edit operations on an object. For this, first create object instance then call any of the helper methods mentioned below and finally save the changes. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.ObjectEdit; public class ObjectEdit { public static void objectEditInstance() throws Exception { Suprsend suprsendClient = getSuprClient(); //Fetch Object Instance String objectType = "departments"; String objectId = "engineering"; ObjectEdit object_ins = suprClient.objects.getInstance(objectType, objectId); // Call object update methods object_ins.set("company_name", "ABC company"); object_ins.setTimezone("America/Los_Angeles"); // Call edit api JSONObject res = suprClient.objects.edit(object_ins); System.out.println(res); } } ``` ```java Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "$timezone":"America/Los_Angeles", "company_name":"ABC company" }, "created_at":"2025-03-04T08:34:58.061696+00:00", "updated_at":"2025-03-14T14:40:21.467156+00:00", "$inbox":[ { "value":"dHvPs6pmUXXXXXXTAsfM2yroXXXX2CXZOtjXXXXXX8", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` Use `object_ins.add*` method(s) to add user channels in a profile ```java Request theme={"system"} // Add Email object_ins.addEmail("example@example.com"); //Add SMS object_ins.addSms("+919999999999"); //Add Whatsapp object_ins.addWhatsapp("+919999999999"); // Add Androidpush token.Pass the vendor as 2nd param. object_ins.addAndroidpush("androidpush_fcm_token__","fcm"); // Add iospush token object_ins.addIospush("__iospush_apns_token__"); // Add Slack using user email id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("email", "user@example.com"); object_ins.addSlack(slackIdent); // Add Slack using member_id of the user if known JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("user_id", "U03XXXXXXXX"); object_ins.addSlack(slackIdent); // Add Slack channel_id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("channel_id", "C04XXXXXXXX"); object_ins.addSlack(slackIdent); // Add Slack incoming webhook JSONObject slackIdent = new JSONObject() .put("incoming_webhook", new JSONObject().put("url", "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX")) object_ins.addSlack(slackIdent); // Add Webpush token json (VAPID) JSONObject webpush = new JSONObject() .put("endpoint", "__end_point__") .put("expirationTime", "") .put("keys", new JSONObject() .put("p256dh", "__p256dh__") .put("auth", "__auth_key__")); object_ins.addWebpush(webpush, "vapid"); ``` Use `object_ins.remove*` method(s) to remove channels from an object profile ```java Request theme={"system"} // Remove Email object_ins.removeEmail("example@example.com"); //Remove SMS object_ins.removeSms("+919999999999"); //Remove Whatsapp object_ins.removeWhatsapp("+919999999999"); // Remove Androidpush token.Pass the vendor as 2nd param. object_ins.removeAndroidpush("androidpush_fcm_token__","fcm"); // Remove iospush token object_ins.removeIospush("__iospush_apns_token__"); // Remove Slack using user email id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("email", "user@example.com"); object_ins.removeSlack(slackIdent); // Remove Slack using member_id of the user if known JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("user_id", "U03XXXXXXXX"); object_ins.removeSlack(slackIdent); // Remove Slack channel_id JSONObject slackIdent = new JSONObject() .put("access_token", "xoxb-XXXXXXXX") .put("channel_id", "C04XXXXXXXX"); object_ins.removeSlack(slackIdent); // Remove Slack incoming webhook JSONObject slackIdent = new JSONObject() .put("incoming_webhook", new JSONObject().put("url", "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX")) object_ins.removeSlack(slackIdent); // Remove Webpush token json (VAPID) JSONObject webpush = new JSONObject() .put("endpoint", "__end_point__") .put("expirationTime", "") .put("keys", new JSONObject() .put("p256dh", "__p256dh__") .put("auth", "__auth_key__")); object_ins.removeWebpush(webpush, "vapid"); ``` This method will delete/unset all values in specified channel for object (ex: remove all emails attached to object). ```java Request theme={"system"} object_ins.unset("$email"); object_ins.unset(Arrays.asList(new String[] { "$sms", "$email" })); // available channel keys - //{"$email","$whatsapp","$sms","$androidpush","$iospush","$webpush","$slack","$ms_teams"} ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```java Request theme={"system"} object_ins.setPreferredLanguage("es") ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```java Request theme={"system"} object_ins.setTimezone("America/Los_Angeles"); ``` Set any custom property using this method. It will shallow merge existing properties with new values. Key shouldn't start with `$` or `ss`. ```java Request theme={"system"} //set single property object_ins.set(key, value); object_ins.set("company_name", "ABC company"); //set multiple properties JSONObject objectsProperties = new JSONObject() .put("company_name", "ABC company") .put("city", "San Francisco") object_ins.set(objectsProperties); ``` Remove any custom property key using this method. ```java Request theme={"system"} //unset single property object_ins.unset(key); object_ins.unset("company_name"); //unset multiple properties object_ins.unset(Arrays.asList(new String[] { "$sms", "wishlist" })); ``` This method will append a value to the list for a given property. ```java Request theme={"system"} //append single property object_ins.append("array", "k1"); //append multiple properties JSONObject objectsProperties = new JSONObject() .put("wishlist", "iphone12") .put("products", "tenancy"); object_ins.append(objectsProperties); ``` This method will remove a value from the list for a given property. ```java Request theme={"system"} //append single property object_ins.remove("array", "k1"); //append multiple properties JSONObject objectsProperties = new JSONObject() .put("wishlist", "iphone12") .put("products", "tenancy"); object_ins.remove(objectsProperties); ``` Works just like `object_ins.set`, except it will not overwrite existing property values. This is useful for properties like *First login date* ```java Request theme={"system"} //single property object_ins.setOnce("first_login", "2021-11-02"); //multiple properties JSONObject objectsProperties = new JSONObject() .put("first_login", "2021-11-02") .put("DOB", "1991-10-02"); object_ins.setOnce(objectsProperties); ``` Add the given amount to an existing property on the object. If the object does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number for the value. ```java Request theme={"system"} //single property object_ins.increment("login_count", 1); //multiple properties JSONObject objectsProperties = new JSONObject() .put("login_count", 1) .put("remaining_credits", -1); object_ins.increment(objectsProperties); ``` *** ## List objects List objects for an `object_type`. You can also pass listing options in the payload which includes `limit`,`before`,`after` ```java Request theme={"system"} String objectType = "__objecttype__"; // optional params to pass limit (default=10) and cursor pointer for fetching the next set of results HashMap opts = new HashMap() { { put("limit", 10); put("after", "01HFS04E4J29KHPYRK7HT3YQQ5"); } }; JSONObject res = suprClient.objects.list(objectType, opts); System.out.println(res); ``` ## Get object details ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; JSONObject res = suprClient.objects.get(objectType, objectId); System.out.println(res); ``` ## Create subscriptions ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; JSONObject payload = new JSONObject().put("recipients", Arrays.asList("__recipient_id_1__", new JSONObject().put("distinct_id", "__recipient_id_2__"), new JSONObject().put("object_type", objectType).put("id", objectId))); JSONObject res = suprClient.objects.createSubscriptions(objectType, objectId, payload); System.out.println(res); ``` ## List subscriptions ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; // optional params to pass limit (default=10) and cursor pointer for fetching the next set of results HashMap opts = new HashMap() { { put("limit", 10); put("after", "01HFS04E4J29KHPYRK7HT3YQQ5"); } }; JSONObject res = suprClient.objects.getSubscriptions(objectType, objectId, opts); System.out.println(res); ``` ## Remove object subscription ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; JSONObject payload = new JSONObject().put("recipients", Arrays.asList("__recipient_id_1__", new JSONObject().put("distinct_id", "__recipient_id_2__"), new JSONObject().put("object_type", "__objecttype__").put("id", "__objectid_child_"))); JSONObject res = suprClient.objects.deleteSubscriptions(objectType, objectId, payload); System.out.println(res); ``` ## Get list of objects subscribed by object An object can subscribe to other objects. Use this method to get the list of all objects that the current object has subscribed to. ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; // optional params to pass limit (default=10) and cursor pointer for fetching the next set of results HashMap opts = new HashMap() { { put("limit", 10); put("after", "01HFS04E4J29KHPYRK7HT3YQQ5"); } }; JSONObject res = suprClient.objects.getObjectsSubscribedTo(objectType, objectId, opts); System.out.println(res); ``` ## Delete object ```java Request theme={"system"} String objectType = "__objecttype__"; String objectId = "__objectid__"; JSONObject res = suprClient.objects.delete(objectType, objectId); System.out.println(res); ``` ### Bulk delete object ```java Request theme={"system"} String objectType = "__objecttype__"; JSONObject payload = new JSONObject().put("object_ids", Arrays.asList("__objectid__")); JSONObject res = suprClient.objects.bulkDelete(objectType, payload); System.out.println(res); ``` # Send and Track Events Source: https://docs.suprsend.com/docs/java-send-event-data Learn how to send events to trigger workflows, with code snippets and examples. ## Send Event You can send event to Suprsend platform by using the `suprClient.trackEvent` method. When you call `suprClient.trackEvent`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. The actual processing/execution of event happens asynchronously. ```java Request theme={"system"} package suprsend; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.Event; public class Event { public static void main(String[] args) throws Exception { trackEvent(); } private static void trackEvent() throws SuprsendException, UnsupportedEncodingException{ // Initialize SDK Suprsend suprClient = new Suprsend("_workspace_key_", "_workspace_secret_"); String distinctId = "_distinct_id_"; String eventName = "__event_name__"; // dynamic data to render template and workflow variables JSONObject eventProps = new JSONObject() .put("key1", "value1") .put("key2", "value2"); Event e = new Event(distinctId, eventName, eventProps); JSONObject response = suprClient.trackEvent(e); System.out.println(response); } } ``` ```java Sample Code theme={"system"} package suprsend; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.Event; public class Event { public static void main(String[] args) throws Exception { trackEvent(); } private static void trackEvent() throws SuprsendException, UnsupportedEncodingException{ // Initialize SDK Suprsend suprClient = new Suprsend("_workspace_key_", "_workspace_secret_"); String distinctId = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08"; String eventName = "product_purchased"; // dynamic data to render template and workflow variables JSONObject eventProps = new JSONObject() .put("first_name", "User") .put("spend_amount", "$10") .put("nested_key_example", new JSONObject().put("nested_key1", "some_value_1")); Event e = new Event(distinctId, eventName, eventProps); JSONObject response = suprClient.trackEvent(e); System.out.println(response); } } ``` ```java Response theme={"system"} // Response structure { "success": True, # if true, request was accepted. "status": "success", "status_code": 202, # http status code "message": "OK", } { "success": False, # error will be present in message "status": "fail", "status_code": 500, # http status code "message": "error message", } ``` | Parameter | Description | Format | Obligation | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----------- | | `distinct_id` | `distinct_id` of subscriber performing the event | int, bigint, string, UUID | *mandatory* | | `event_name` | string identifier for the event like `product_purchased` | string | *mandatory* | | `properties` | a dictionary representing event attributes like `first_name` Event properties can be used to pass template variables in case of event based trigger | Dictionary | *optional* | **Event naming guidelines:** When you create an Event or a property, please ensure that the Event Name or Property Name does not start with or , as we have reserved these symbols for our internal events and property names. ### Trigger events for a brand/tenant If you handle communications to end users on behalf of your customers and want to send custom notifications for each brand/tenant, you can do that with the help of [brands (now called tenants)](/docs/tenants). Just pass the **brand\_id** of your customer brand as the **fifth parameter in your event instance** like shown below and the properties of that brand will be used to replace brand variables in your template. ```java Request theme={"system"} Event e = new Event(distinctId, eventName, eventProps, , brandId); ``` ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. You can use this key to track webhooks related to workflow notifications. To make an idempotent request, pass `idempotency_key` as the **fourth parameter in your event instance** like shown below. Idempotency key should be unique that you generate for each request. You may use any string up to 255 characters in length as an idempotency key. Ensure that you don’t add any space in start and end of the key as it will be trimmed. ```java Request theme={"system"} Event e = new Event(distinctId, eventName, eventProps, idempotencyKey); ``` Here are some common approaches for assigning idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like user147-new-comment-1687437670 * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ### Add file attachment (for email) You can add file attachment by appending the attachment filepath to each event instance. * Call `event.addAttachment()` for each file with an accessible URL. Ensure that file\_path is a publicly accessible URL. * Since event API size can't be > 100 KB, local file paths can't be passed in event attachment. ```java Request theme={"system"} ... Event e1 = new Event(distinctId1, eventName1, eventProps1); //Event 1 // this snippet can be used to add attachment to event String filePath1 = "https://www.africau.edu/images/default/sample.pdf"; e1.addAttachment(filePath1); ... ``` Please add public accessible URL only as attachment file otherwise it will throw an error `404 not found` and workflow will not be triggered ## Bulk event trigger You can use Bulk API to send multiple events. Use `.append()` on bulk\_events instance to add however-many-records to call in bulk. ```java java theme={"system"} ... BulkEvents bulkIns = suprClient.bulkEvents.newInstance(); Event e1 = new Event(distinctId1, eventName1, eventProps1); //Event 1 Event e2 = new Event(distinctId2, eventName2, eventProps2); //Event 2 // --- use .append on bulk instance to add one or more records bulkIns.append(e1) bulkIns.append(e2) // OR bulkIns.append(e1, e2) // Track event JSONObject response = bulkIns.trigger(); System.out.println(response); ``` Bulk API is supported in SuprSend java-sdk version 0.5.0 and above. If you are using an older version, please upgrade to the latest SDK version. *** # Trigger Workflow from API Source: https://docs.suprsend.com/docs/java-trigger-workflow-from-api Learn how to trigger workflows using direct workflow API, with code snippets and examples. It is a unified API to trigger workflow and doesn't require user creation before hand to trigger notification. Recommended for platforms transitioning their existing notifications to SuprSend. If you are using our frontend SDKs to configure notifications and passing events and user properties from third-party data platforms like Segment, then [event-based trigger](/docs/java-send-event-data) would be a better choice. 📘 Available in SDK version >= v0.7.0 ## Sample Payload Once your request is accepted, you can check the status of your request in the [SuprSend Logs](https://app.suprsend.com/en/staging/log). ```java Workflow.java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; String tenantId = "tenant_id1"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("actor", new JSONObject() .put("distinct_id", "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08") .put("name", "actor_1") .put("$skip_create", true) ) .put("recipients", new JSONArray() // notify user .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$email", Arrays.asList("abc@example.com")) .put("name", "recipient_1") .put("$preferred_language", "en") .put("$timezone", "America/New_York") .put("$skip_create", true) ) // notify object .put(new JSONObject() .put("object_type", "teams") .put("id", "finance") .put("$skip_create", true) ) ) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ```java Helper.java theme={"system"} package test; import suprsend.Suprsend; import suprsend.SuprsendException; public class TestHelper { public static Suprsend getClientInstance() throws SuprsendException { String apiKey = "_workspace_key_"; String apiSecret = "_workspace_secret_"; return new Suprsend(apiKey, apiSecret); } } ``` ```java Response theme={"system"} { "success": true, "status": "success", "status_code": 202, "message": "{\"status\":\"success\"}" } ``` To prevent automatic creation of an actor, or recipient (user/object) in SuprSend (the case where they already exist in your system), you can use the `"$skip_create": true` flag. This can be applied inside the actor, individual user recipient objects, or object recipient objects. | Property | Type | Description | | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `workflow` | `string` | Slug of designed workflow on SuprSend dashboard. You'll get the slug from workflow settings. | | `recipients` | `array of string / array of objects` | List of users who need to be notified. You can add up to 100 recipients in a workflow trigger. You can either pass recipients as an array of `distinct_id` (if the user is pre-synced in SuprSend database) or [define recipient information inline](https://docs.suprsend.com/docs/java-trigger-workflow-from-api#identifying-recipients-inline). | | `actor` *(Optional)* | `string / object` | Includes `distinct_id` and properties of the user who performed the action. You can use it for [cross-user notifications](https://docs.suprsend.com/docs/java-trigger-workflow-from-api#sending-cross-user-notifications), where you need to include actor properties in the notification template. Actor properties can be added as `$actor.`. | | `data` | `object` | Variable data required to render dynamic template content or workflow properties such as dynamic delay or channel override in send node. | | **[tenant\_id](https://docs.suprsend.com/docs/java-trigger-workflow-from-api#multi-tenant-notifications)** *(Optional)* | `string` | Trigger workflow for a specific tenant/brand. | | **[idempotency\_key](https://docs.suprsend.com/docs/java-trigger-workflow-from-api#idempotent-requests)** *(Optional)* | `string` | Unique identifier of the request. We'll be returning `idempotency_key` in our [outbound webhook response](https://docs.suprsend.com/docs/outbound-webhook). You can use it to map notification statuses and replies in your system. | | `recipients\[].$timezone` | `string` | Sets the recipient's timezone. Used to send notifications in the user's local timezone. You can pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | `recipients\[].$preferred_language` | `string` | Sets the recipient's preferred language. This supports localization in notification content. You can pass the language in `ISO 639-1` 2-letter format. [Refer to all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | ## Sending notification to multiple users Recipients in workflow call is an array of `distinct_ids` or recipient objects. You can pass up to 100 recipients in a single workflow trigger. SuprSend will internally convert it into multiple workflow triggers, one for each recipient in the array. ```java Request theme={"system"} JSONObject body = new JSONObject() .put("recipients", Arrays.asList("id1","id2")) // ---- OR ------ JSONObject body = new JSONObject() .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "id1") .put("$email", Arrays.asList("recipient1@example.com")) .put("name", "recipient_1")) .put(new JSONObject() .put("distinct_id", "id2") .put("$email", Arrays.asList("recipient2@example.com")) .put("name", "recipient_2")) ) ``` **Use lists to broadcast to a large list of users:** We recommend you to use [lists](/docs/lists-java) and [broadcast](/docs/broadcast-java) to send notifications to a user list larger than 1000 users. This approach allows bulk processing, resulting in significantly faster delivery compared to individual workflow calls. Sending individual workflows to a large set of users may introduce delays in your notification queue. ## Identifying recipients inline One of the benefits of using direct workflow trigger is that you can identify recipients inline. You can include recipient channel information, their channel preferences, and their user properties along with the workflow trigger. Upon triggering the workflow, the recipient will be automatically created in the SuprSend database in the background. This facilitates dynamic synchronization of your user data within SuprSend and eliminates the need for any migration efforts on your end to start sending notifications from SuprSend. You can also use recipient properties in your template as `$recipient.`. This is how the complete recipient object with look like ```java Request theme={"system"} JSONObject body = new JSONObject() .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$email", Arrays.asList("abc@example.com")) // Email communication channel .put("$sms", Arrays.asList("+15555555555")) // SMS communication channel .put("$channels", Arrays.asList("email","inbox")) .put("$preferred_language", "en") .put("$timezone", "America/New_York") .put("custom_prop1", "value_1") // custom property .put("custom_prop2", "value_2") // custom property )) ``` | Property | Type | Description | | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `distinct_id` | `string` | Unique identifier of the user to be notified. | | **[communication channels](https://docs.suprsend.com/docs/java-trigger-workflow-from-api#communication-channels-of-recipient)** (`$email`, `$sms`, etc.) | `array of string / objects` | The communication channels info provided will be updated to the user profile in the background. For this workflow, only channel values specified for this recipient will be used for sending notifications instead of all channel values present in the user profile. | | `channels` | `array of string` | By default, notifications will be sent to all channels defined in the workflow delivery nodes. However, if a user has a specific channel preference for a notification (e.g. they only want to receive payment reminders via email), you can include that preference in the workflow payload. This will ensure that notifications are sent only to the specified channels. Supported channels: `email, sms, whatsapp, androidpush, iospush, slack, webpush, ms_teams`.You can always use our [in-built preference APIs](/docs/user-preferences) to maintain user notification preferences. Preferences defined within SuprSend will automatically apply with workflow triggers. | | `$preferred_language` | `string` | Sets the recipient's preferred language to support localization in notification content. You can pass the language in `ISO 639-1` 2-letter format. [Refer to all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | | `$timezone` | `string` | Sets the recipient's timezone. Used to send notifications in the user's local timezone. You can pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | `\*` | `object` | You can pass other user properties to render dynamic template content. These properties will also be set in the user profile and can be used in the template as `$recipient.`. | ### Communication channels of recipient ```java Request theme={"system"} JSONObject body = new JSONObject() .put("recipients", new JSONArray() .put(new JSONObject() .put("$email", Arrays.asList("abc@example.com")) // Email communication channel .put("$sms", Arrays.asList("+15555555555")) // SMS communication channel .put("$whatsapp", Arrays.asList("+15555555555")) // WhatsApp communication channel .put("$androidpush", new JSONObject() // androidpush communication channel .put("token", "__android_push_token__") .put("provider", "fcm") .put("device_id", "")) .put("$iospush", new JSONObject() // iospush communication channel .put("token", "__ios_push_token__") .put("provider", "apns") .put("device_id", "")) .put("$slack", new JSONObject() // slack using user email id .put("email", "abc@example.com") .put("access_token", "xoxb-XXXXXXXX")) .put("$slack", new JSONObject() // slack using member_id .put("user_id", "U/WXXXXXXXX") .put("access_token", "xoxb-XXXXXXXX")) .put("$slack", new JSONObject() // slack channel .put("channel", "CXXXXXXXX") .put("access_token", "xoxb-XXXXXXXX")) .put("$slack", new JSONObject() // slack using incoming webhook .put("incoming_webhook", new JSONObject() .put("url", "https://hooks.slack.com/services/T0XXXXX/B0XXXXXX/XXXXXXX"))) .put("$ms_teams", new JSONObject() // MS teams user or channel using conversation_id .put("tenant_id", "c1981ab2-9aaf-xxxx-xxxx") .put("service_url", "https://smba.trafficmanager.net/amer") .put("conversation_id", "19:c1524d7c-a06f-456f-8abe-xxxx")) .put("$ms_teams", new JSONObject() // MS teams using user id .put("tenant_id", "c1981ab2-9aaf-xxxx-xxxx") .put("service_url", "https://smba.trafficmanager.net/amer") .put("user_id", "29:1nsLcmJ2RKtYH6Cxxxx-xxxx")) .put("$ms_teams", new JSONObject() // MS teams using incoming webhook .put("incoming_webhook", new JSONObject() .put("url", "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX"))) )) ``` ## Sending cross-user notifications In scenarios where you need to notify a group of users based on another user's action, such as sending a notification to the document owner when someone comments on it, you can specify the actor in your workflow call. This allows you to use actor's name or other properties in your notification template. Actor properties can be included in the template as `$actor.`. ```javascript Sample Template theme={"system"} //handlebar template Hi {{$recipient.name}}, {{$actor.name}} added {{length comments}} new comments on the {{doc_name}}. // rendered content with Sample Data Hi recipient_1, actor_1 added 2 new comments on the annual IT report. ``` ```java API request payload theme={"system"} private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "new_comment") .put("actor", new JSONObject() .put("distinct_id", "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08") .put("name", "actor_1") ) .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$email", Arrays.asList("abc@example.com")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("doc_name", "annual IT report") .put("date", "2024-01-01") .put("comments", Arrays.asList("change the date","rest looks good")) ); return body; } ``` ## Sending notification to anonymous user You can send notifications to anonymous users by passing ` "is_transient": true` in your recipient object. This approach is recommended for scenarios where you need to send notifications to unregistered users without creating them in the SuprSend platform. The same way, you can pass ` "is_transient": true` in your actor object to use actor properties in template without creating user profile. ```java Request theme={"system"} private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "_workflow_slug_") .put("actor", new JSONObject() .put("is_transient", true) // for anonymous actor .put("name", "actor_1") ) .put("recipients", new JSONArray() .put(new JSONObject() .put("is_transient", true) // for anonymous recipient .put("$email", Arrays.asList("abc@example.com")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("doc_name", "annual IT report") .put("date", "2024-01-01") .put("comments", Arrays.asList("change the date","rest looks good")) ); return body; } ``` ## Multi-tenant notifications For cases where you want to send notifications to your enterprise customers end users, pass the `tenant_id` in your workflow instance. You can use this to dynamically manage [tenant level notification customizations](/docs/tenants). This includes the ability to customize template design or content and route notifications via tenant vendors. ```java Request theme={"system"} private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String tenantId = "tenant_id1"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } ``` ## Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. Idempotency key should be uniquely generated for each request (max 255 characters allowed). Spaces in start and end of the key will be trimmed. Here are some common approaches for generating idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ```java Request theme={"system"} private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } ``` ## Bulk API for triggering multiple workflows Bulk API allows you to send multiple workflow requests in a single call. Use `.append()` and `workflows.bulkTriggerInstance()` to add however-many-records to call in bulk. ```java Workflow.java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.BulkResponse; import suprsend.BulkWorkflowTrigger; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowBulkTrigger(); } private static void WorkflowBulkTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload of first workflow JSONObject body1 = getWorkflowBody1(); String idempotencyKey1 = "_unique_request_identifier"; String tenantId1 = "tenant_id1"; WorkflowTriggerRequest wf1 = new WorkflowTriggerRequest(body1, idempotencyKey1, tenantId1); // payload for second workflow JSONObject body2 = getWorkflowBody2(); String idempotencyKey2 = "_unique_request_identifier"; String tenantId2 = "tenant_id1"; WorkflowTriggerRequest wf2 = new WorkflowTriggerRequest(body2, idempotencyKey2, tenantId2); // BulkWorkflowTrigger bulkIns = suprClient.workflows.bulkTriggerInstance(); bulkIns.append(wf1,wf2); // BulkResponse resp = bulkIns.trigger(); System.out.println(resp); } private static JSONObject getWorkflowBody1() {...} private static JSONObject getWorkflowBody2() {...} ``` ```java Helper.java theme={"system"} package test; import suprsend.Suprsend; import suprsend.SuprsendException; public class TestHelper { public static Suprsend getClientInstance() throws SuprsendException { String apiKey = "_workspace_key_"; String apiSecret = "_workspace_secret_"; return new Suprsend(apiKey, apiSecret); } } ``` ```java Response theme={"system"} { status: "success/fail/partial", total: 10, success: 10, failure: 0, failed_records: [{"record": {...}, "error": "error_str", "code": ""}], warnings: [] } ``` ## Add attachment (in email) To add one or more attachments to a notification (viz. Email), call `wf.addAttachment()` for each file with local path or remote attachment url. Ensure that file path is valid, and public(for remote url) otherwise it will raise error. ```javascript Request theme={"system"} private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body); String filePath = "https://lightning.network/lightning-network-paper.pdf"; wf.addAttachment(filePath, "MyFile.pdf", true); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } ``` 🚧 A single workflow instance size (including attachment) must not exceed 100KB (100 x 1024 bytes). *** ## Dynamic workflow trigger You can configure workflow from backend API by following below steps: ```json Input.json theme={"system"} // Prepare Workflow body workflow_body = { "name": "workflow_name", "template": "template_slug", "notification_category": "notification_category", // notification category transactional/promotional/system "delay": "time_delay", // time delay after which the first notification will be sent "trigger_at": "date string in ISO 8601", //to trigger scheduled notifications "users": [ { "distinct_id": "distinct_id", // unique identifier of the user // if $channels is present, communication will be triggered on mentioned channels only. // "$channels": ["email"], // User communication channel can be added as [optional]: // "$email":["user@example.com"], // "$whatsapp":["+15555555555"], // "$sms":["+15555555555"], // "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], // "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], // "$slack": { // "email": "e@example.com", // "access_token": "xoxb-XXXXXXXX" //} --- slack using email // "$slack": { // "user_id": "U/WXXXXXXXX", // "access_token": "xoxb-XXXXXX" //} --- slack using member_id // "$slack": { // "channel": "CXXXXXXXX", // "access_token": "xoxb-XXXXXX" //} --- slack channel // "$slack": { // "incoming_webhook": { // "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" // } //} --- slack incoming webhook } ], // delivery instruction [optional]. how should notifications be sent, and whats the success metric "delivery": { "smart": , "success": "success_metric", "time_to_live": "TTL duration", // will be applicable for smart = TRUE "mandatory_channels": [] // list of mandatory channels e.g ["email"], will be applicable for smart = TRUE }, // data can be any json / serializable python-dictionary "data": { "key":"value", "nested_key": { "nested_key1": "some_value_1", "nested_key2": { "nested_key3": "some_value_3", }, } } } ``` For configuring a workflow from backend, you can pass following properties in your method | Parameter | Description | Format | Obligation | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `name` | It is the unique name of the workflow. You can see workflow-related analytics on the workflow page (how many notifications were sent, delivered, clicked or interacted). The workflow name should be easily identifiable for your reference at a later stage | *text* | *Mandatory* | | `template` | It is the unique slug of the template created on SuprSend platform. You can get this slug by clicking on the clipboard icon next to the Template name on SuprSend templates page. It is the same for all channels | slug name | *Mandatory* | | `notification_category` | You can understand more about them in the [Notification Category](/docs/notification-category) documentation | system / transactional / promotional | *Mandatory* | | `delay` | Workflow will be halted for the time mentioned in delay, and become active once the delay period is over. | **XX**d**XX**h**XX**m**XX**s or if its number (n) then delay is in seconds (n) | *Optional* | | `trigger_at` | Trigger workflow on a specific date-time | date string in ISO 8601 e.g. "2021-08-27T20:14:51.643Z" | *Optional* | | `users` | Array object of target users. Atleast 1 user mandatory. `distinct_id` for each user mandatory Channel information is non-mandatory. If you pass channel information here, then these channels will be used for sending notification otherwise channels will be picked from user profile. | `"users": [ { "distinct_id": "value", "$channels":[], channel_information_dict #(optional) }], ` | *Mandatory* | | `delivery` | Delivery instructions for the workflow. You can enable smart delivery by setting `"smart":True` By default, delivery instruction will be `"delivery": { "smart": False, "success": "seen" }` Checkout the [docs](/docs/java-trigger-workflow-from-api#smart-delivery) for more info. | `delivery = { "smart": True/False, "success": "delivered/seen/interaction/", "time_to_live": "", "mandatory_channels": []}`, # list of mandatory channels e.g gation" | *Optional* | | `data` | JSON. To replace the variables in the template, templates use [handlebars](https://handlebarsjs.com/guide/) language | `"data": { "key": { "key": "value", "key": "value" } },` | *Optional* | 👍 **+CountryCode Required for SMS and Whatsapp:** For setting `$sms` and `$whatsapp`, `+` is mandatory to send along with phone number. e.g. +91 for India Once you have the object initialized you can make a call to suprsend backend using following line: ```java Request theme={"system"} JSONObject response = suprsend.triggerWorkflow(body); ``` ```java Sample Code theme={"system"} package tests; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import org.json.JSONObject; import org.json.JSONTokener; import suprsend.Suprsend; public class TestSuprsendSDK { private JSONObject loadBody(String fileName) throws FileNotFoundException { JSONObject jsonObject; String relativePath = String.format("%s/src/%s/resources/%s.json", System.getProperty("user.dir"), this.getClass().getPackage().getName(), fileName); InputStream schemaStream = new FileInputStream(new File(relativePath)); jsonObject = new JSONObject(new JSONTokener(schemaStream)); return jsonObject; } public static void main(String[] args) throws Exception { TestSuprsendSDK sdk = new TestSuprsendSDK(); // Load workflow body json JSONObject body = sdk.loadBody("input"); // SDK instance Suprsend suprsend = new Suprsend("WORKSPACE KEY", "WORKSPACE KEY"); Workflow wf = new Workflow(body, idempotencyKey, tenantId) // Trigger workflow JSONObject resp = suprClient.triggerWorkflow(wf); System.out.println(resp); } }} ``` ```java Response theme={"system"} // Response structure { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } { "success": false, // error will be present in message "status": "fail", "status_code": 500, // http status code "message": "error message", } ``` Alternatively you can find sample java client code snippet, which demonstrates how to make a call to Suprsend backend using Java SDK below: * [Maven Client](https://suprsend-java-sdk.s3.ap-south-1.amazonaws.com/example-client/suprsend-java-sdk-client.zip) * [Non Maven Client](https://suprsend-java-sdk.s3.ap-south-1.amazonaws.com/example-client/suprsend-java-sdk-non-maven-client.zip) \*\*Response Structure: \*\*When you call `suprClient.triggerWorkflow`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. Possible values for status key in the response: | status | Description | | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- | | `202` | Request was successfully accepted | | `400` | Some of the backend validations failed due to which request could not be accepted. More details will be present against the message key. | | `401` | Unauthorised access made to Suprsend backend. Please check the credentials that are being passed. | | `500` | Any other error which was not handled by the Suprsend system. You will find more details against the message key in response. | \*\*Note: \*\*The actual processing/execution of workflow happens asynchronously. Once your request is accepted, you can check what is the status of your request in the '[ SuprSend Logs](https://app.suprsend.com/en/staging/logs)' section. #### Bulk API for triggering multiple workflows. Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.append()` on `bulk_workflows` instance to add however-many-records to call in bulk. Response is an instance of `suprsend.BulkResponse` class. ```javascript Request theme={"system"} package tests; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import org.json.JSONObject; import org.json.JSONTokener; import suprsend.Suprsend; public class TestSuprsendSDK { private JSONObject loadBody(String fileName) throws FileNotFoundException { JSONObject jsonObject; String relativePath = String.format("%s/src/%s/resources/%s.json", System.getProperty("user.dir"), this.getClass().getPackage().getName(), fileName); InputStream schemaStream = new FileInputStream(new File(relativePath)); jsonObject = new JSONObject(new JSONTokener(schemaStream)); return jsonObject; } public static void main(String[] args) throws Exception { TestSuprsendSDK sdk = new TestSuprsendSDK(); // Load workflow body json JSONObject body = sdk.loadBody("input"); // SDK instance Suprsend suprsend = new Suprsend("WORKSPACE KEY", "WORKSPACE KEY"); // Create one or more workflow instances Workflow wf1 = new Workflow(body1) Workflow wf2 = new Workflow(body2) // --- use .append on bulk instance to add one or more records BulkWorkflows bulkIns = suprClient.bulkWorkflows.newInstance(); bulkIns.append(wf1,wf2); // trigger workflow BulkResponse resp = bulkIns.trigger(); System.out.println(resp); } }} ``` ```java Response theme={"system"} // Response structure import suprsend.BulkResponse; BulkResponse{status: 'success' | total: 2 | success: 2 | failure: 0 | warnings: 0} BulkResponse{status: 'fail' | total: 2 | success: 0 | failure: 2 | warnings: 0} BulkResponse{status: 'partial' | total: 2 | success: 1 | failure: 1 | warnings: 0} ``` *** # Events and User methods Source: https://docs.suprsend.com/docs/js-events-and-user-methods Methods to send event or manage user updates based on user action in javascript websites like React, Angular,Vue, and Next.js. ## Trigger Events You can trigger events from client to SuprSend using `track` method. This can be used to trigger [event-based workflows](/docs/trigger-workflow#event-based-trigger). ```typescript syntax theme={"system"} const resp = await suprSendClient.track(event: string, properties?: Dictionary) ``` ```typescript example theme={"system"} const resp = await suprSendClient.track("test", {name:'john doe'}) ``` **Returns:** `Promise` ## Update user profile **Returns:** `Promise` ### Update user channels Set user channel related information using following methods. Its recommended to use SuprSend's Backend SDK's to set user channels instead of Client SDK's. ```typescript syntax theme={"system"} await suprSendClient.user.addEmail(email: string) await suprSendClient.user.removeEmail(email: string) // mobile should be as per E.164 standard: https://www.twilio.com/docs/glossary/what-e164 await suprSendClient.user.addSms(mobile: string) await suprSendClient.user.removeSms(mobile: string) // mobile should be as per E.164 standard await suprSendClient.user.addWhatsapp(mobile: string) await suprSendClient.user.removeWhatsapp(mobile: string) ``` ### Update user properties This is the list of available user update methods: This method will set users timezone. Timezone value should be in [IANA timezone format](https://timeapi.io/documentation/iana-timezones). ```typescript syntax theme={"system"} await suprSendClient.user.setTimezone(timezone: string) ``` ```typescript example theme={"system"} await suprSendClient.user.setTimezone("America/Bogota") ``` This method will set users preferred language. Language value should be in [ISO 639-1 Alpha-2 format](https://gist.github.com/jrnk/8eb57b065ea0b098d571). ```typescript syntax theme={"system"} await suprSendClient.user.setPreferredLanguage(language: string) ``` ```typescript example theme={"system"} await suprSendClient.user.setPreferredLanguage("en") ``` Set is used to set the custom user property or properties. If already property is already present value will be replaced. ```typescript syntax theme={"system"} await suprSendClient.user.set(arg1: string | Dictionary, arg2?: unknown) ``` ```typescript example theme={"system"} await suprSendClient.user.set("name", "John Doe") await suprSendClient.user.set({"name": "John Doe", "designation": "manager"}) ``` This method will remove user property. To remove channel pass `$email`, `$sms`, `$whatsapp`. ```typescript syntax theme={"system"} await suprSend.user.unset(arg: string | string[]) ``` ```typescript example theme={"system"} await suprSendClient.user.unset("wishlist") await suprSendClient.user.unset(["wishlist", "$email"]); ``` This method will add a value to the list for a given property. ```typescript syntax theme={"system"} await suprSendClient.user.append(arg1: string | Dictionary, arg2?: unknown) ``` ```typescript example theme={"system"} await suprSendClient.user.append("wishlist", "iphone12") await suprSendClient.user.append({"wishlist" : "iphone12", "cart" : "Apple airpods"}); ``` This method will remove a value from the list for a given property. ```typescript syntax theme={"system"} await suprSendClient.user.remove(arg1: string | Dictionary, arg2?: unknown) ``` ```typescript example theme={"system"} await suprSendClient.user.remove("wishlist", "iphone12") await suprSendClient.user.remove({"wishlist" : "iphone12", "cart" : "Apple airpods"}); ``` This method is similar to set method but values once set cannot be updated. ```typescript syntax theme={"system"} await suprSendClient.user.setOnce(arg1: string | Dictionary, arg2?: unknown) ``` ```typescript example theme={"system"} await suprSendClient.user.setOnce("DOB", "1991-10-02") await suprSendClient.user.setOnce({"first_login" : "2021-11-02", "DOB" : "1991-10-02"}); ``` Add the given amount to an existing user property. If the user does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number as the value. ```typescript syntax theme={"system"} await suprSendClient.user.increment(arg1: string | Dictionary, arg2?: number) ``` ```typescript example theme={"system"} await suprSendClient.user.increment("login_count", 1); await suprSendClient.user.increment({"login_count" : 1, "order_count" : 1}); ``` Keys starting with `ss_` or `$` will be ignored. *** # InApp Feed Source: https://docs.suprsend.com/docs/js-inapp-feed Integrate SuprSend’s InApp feed in non-React apps using headless SDK methods. **NOTE:** Refer type definitions for this guide [here](https://github.com/suprsend/suprsend-web-sdk/blob/main/src/interface.ts) . ## Initialise feed client Using SuprSend client instance create Feed client instance. ```typescript theme={"system"} const feedClient: Feed = suprSendClient.feed.initialize(options?: IFeedOptions); interface IFeedOptions { tenantId?: string; pageSize?: number; stores?: IStore[] | null; host?: { socketHost?: string; apiHost?: string }; } ``` If you are passing `tenant_id` in feed, make sure to pass scope key while creating [userToken](/docs/client-authentication#2-creating-signed-user-jwt-token) else 403 error will be thrown due to scope mismatching. ## Feed client ### Get feed data This returns notification store which contains list of notifications and other meta data like page information etc. You can call this anytime to get updated store data. ```typescript theme={"system"} const feedData: IFeedData = feedClient.data; ``` ### Initialise socket for realtime update ```typescript theme={"system"} feedClient.initializeSocketConnection(); ``` ### Fetching notification data This method will get first page of notifications from SuprSend server and set data in notification store. ```typescript theme={"system"} feedClient.fetch(); ``` ### Fetch more notifications This method will get next page of notifications from SuprSend server and set data in notification store. ```typescript theme={"system"} feedClient.fetchNextPage(); ``` ### Listening for updates to store Whenever there is update in notification store (ex: on new notification or existing notification state updated) this event is fired by library. You can listen to this event and update your local state so that UI of you application is refreshed. ```typescript theme={"system"} feedClient.emitter.on("feed.store_update", (updatedStoreData: IFeedData) => { // update your local state to refresh UI }); ``` ### Listening for new notification In case you want to show toast notification on receiving new notification you can use this listener. ```typescript theme={"system"} feedClient.emitter.on( "feed.new_notification", (notificationData: IRemoteNotification) => { // your logic to trigger toast with new notification data } ); ``` ### Removing feed This will remove feed client data and abort socket connection. Additionally calling `suprSendClient.reset` method during logout will also remove all feedClient instances attached SuprSend client instance. ```typescript theme={"system"} feedClient.remove(); ``` ### Other methods ```typescript theme={"system"} // If stores are used, this method will change active store feedClient.changeActiveStore(storeId: string) // Used to reset badge count which is shown on bell icon. This count is latest notifications that user received from the last he opened inbox popup. // call this on click of bell icon feedClient.resetBadgeCount() // mark notification as seen await feedClient.markAsSeen(notificationId: string) // mark notification as read await feedClient.markAsRead(notificationId: string) // mark notification as unread await feedClient.markAsUnread(notificationId: string) // mark notification as archived await feedClient.markAsArchived(notificationId: string) // mark notification as interacted await feedClient.markAsInteracted(notificationId: string) // bulk mark all notifications as read await feedClient.markAllAsRead() // bulk mark given notification id's as seen await feedClient.markBulkAsSeen(notificationIds: string[]) ``` ## Example For understanding purpose we have added current simple example in react. You could refer this headless example and design the feed in your Angular or Vue.js etc. If you want to implement in react please refer `@suprsend/react-core` or `@suprsend/react` ```jsx theme={"system"} import { SuprSend } from "@suprsend/web-sdk"; import { useEffect, useState } from "react"; export default function Example() { const [feedData, setFeedData] = useState(); const [feedInstance, setFeedInstance] = useState(); const initializeFeed = async (suprSendClient) => { await suprSendClient.identify("YOUR_DISTINCT_ID"); // authenticating user const feedClient = suprSendClient.feeds.initialize(); // creating feed instance using suprsend client instance setFeedInstance(feedClient); // storing it in state so that feed client methods like markRead can be accessed outside const initialFeedData = feedClient?.data; // get initial store data from feed instance setFeedData(initialFeedData); // storing that data in react state so that UI is rendered accordingly feedClient?.emitter.on("feed.store_update", (updatedStoreData) => { setFeedData(updatedStoreData); // register listener to get updated store data and store it in local react state so that UI is updated w.r.t new state }); feedClient.initializeSocketConnection(); // register for socketio connection for realtime updated in feed data feedClient.fetch(); // fetch existing notifications. Once API call is success you get first page notifications in feed.store_update listener and react state update happens which cause UI to renrender. }; useEffect(() => { const suprSendClient = new SuprSend("YOUR_PUBLIC_API_KEY"); // creating suprsend client instance initializeFeed(suprSendClient); return () => suprSendClient.reset(); // up on unmounting remove user so that inbox data will also be cleared }, []); if (!feedData) return null; if (feedData.apiStatus === "LOADING") return

Loading Data

; if (feedData.apiStatus === "SUCCESS" && !feedData?.notifications?.length) { return

No Notifications

; } if (feedData.notifications) { return (
{feedData.notifications.map((notification) => { return (
{ feedInstance.markAsRead(notification.n_id); }} > {notification.n_id}
); })}
{feedData.apiStatus === "FETCHING_MORE" ? (

Loading More

) : (
{feedData.pageInfo.hasMore && ( )}
)}
); } return null; } ``` ### # Preferences Source: https://docs.suprsend.com/docs/js-preferences Step-by-Step Guide to add SuprSend notification preference centre in javascript websites like React, Vue, and Next.js. ### Pre-Requisites * Integration of [JavaScript SDK](/docs/integrate-javascript-sdk) * [Configure notification categories](/docs/user-preferences#create-notification-category) on SuprSend dashboard ## Understanding preference structure This is how a typical preference page will look like: Preference Page contains 2 sections: 1. Category-level preference settings (Sections) * [Sections](/docs/js-preferences#11-sections) * [Categories](/docs/js-preferences#12-categories-sections---sub-categories) * [Category Channel](/docs/js-preferences#13-category-channels-sections---sub-categories---channels) 2. [Overall Channel-level preference](/docs/js-preferences#2-overall-channel-preferences) ### Preferences data structure ```typescript Preferences Types theme={"system"} interface PreferenceData { sections: Section[] | null; channel_preferences: ChannelPreference[] | null; } interface ChannelPreference { channel: string; is_restricted: boolean; } interface Section { name?: string | null; description?: string | null; subcategories?: Category[] | null; } interface Category { name: string; category: string; description?: string | null; preference: PreferenceOptions; is_editable: boolean; channels?: CategoryChannel[] | null; } interface CategoryChannel { channel: string; preference: PreferenceOptions; is_editable: boolean; } enum PreferenceOptions { OPT_IN = "opt_in", OPT_OUT = "opt_out", } enum ChannelLevelPreferenceOptions { ALL = "all", REQUIRED = "required", } ``` ```typescript Example theme={"system"} { "sections": [ { "name": null, "subcategories": [ { "name": "Payment and History", "category": "payment-and-history", "description": "Send updates related to my payment history.", "preference": "opt_in", "is_editable": false, "channels": [ { "channel": "androidpush", "preference": "opt_in", "is_editable": true }, { "channel": "email", "preference": "opt_in", "is_editable": false } ] } ] }, { "name": "Product Updates", "description": "Non-marketing notifications related to authentication, activity updates, reminders etc.", "subcategories": [ { "name": "Newsletter", "category": "newsletter", "description": "Send updates on new feature in the product", "preference": "opt_in", "is_editable": true, "channels": [ { "channel": "androidpush", "preference": "opt_in", "is_editable": true }, { "channel": "email", "preference": "opt_out", "is_editable": false } ] } ] } ], "channel_preferences": [ { "channel": "androidpush", "is_restricted": false }, { "channel": "email", "is_restricted": true } ] } ``` ### 1.1 Sections This contains the name, description, and subcategories. We have to loop through the sections list and for every section item if there is a name and description present, then show the heading, and if a subcategories list is present, loop through that subcategories list and show all subcategories under that section heading. Subcategories can exist without sections as the section is an optional field. In that case, the section's name will not be available. For sections where the name is not present, you can directly show its subcategories list without showing Heading for the section in UI. ```typescript syntax theme={"system"} interface Section { name?: string | null; description?: string | null; subcategories?: Category[] | null; } ``` | Property | Description | | ------------- | --------------------------------------------------------- | | name | name of the section | | description | description of the section | | subcategories | data of all sub-categories to be shown inside the section | ### 1.2 Categories (sections -> sub-categories) This is the place where the user sets his category-level preferences. While looping through the subcategories list for every subcategory item, show the name and description in UI. ```typescript syntax theme={"system"} interface Category { name: string; category: string; description?: string | null; preference: PreferenceOptions; is_editable: boolean; channels?: CategoryChannel[] | null; } ``` | Property | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | category | This key is the id of the category which is used while updating the preference. | | name | name of the category to be shown on the UI | | description | description of the category to be shown on the UI | | preference | This key indicates if the category's preference switch is on or off. Get **OPT\_IN** when the switch is on and **OPT\_OUT** when the switch is off | | is\_editable | Indicates if the preference switch button is disabled or not. If its value is false then the preference setting for that category can't be edited | | channels | data of all category channels to be shown below the sub-category. Loop through it to show checkboxes under every subcategory item. | ### 1.3 Category channels (sections -> sub-categories -> channels) This contains a list of channels, channel preference status and whether it's editable or not. While looping through the subcategory list for every subcategory item we have to loop through its channels list and for every channel to show channel level checkbox. ```typescript syntax theme={"system"} interface CategoryChannel { channel: string; preference: PreferenceOptions; is_editable: boolean; } ``` | Property | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | channel | name of the channel to be shown on UI. The same key will be used as id of the channel while updating the preference. | | preference | This key indicates if the channel's preference switch is on or off. Get OPT\_IN when the switch is on and OPT\_OUT when the switch is off | | is\_editable | Indicates if the preference checkbox is disabled or not. If its value is false then the preference setting for that channel can't be edited | ### 2. Overall channel preferences It's a list of all channel-level preferences. We have to loop through the list and for each item, show the UI as given in the below image. ```typescript syntax theme={"system"} interface ChannelPreference { channel: string; is_restricted: boolean; } ``` | Property | Description | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | channel | name of the channel to be shown on UI. The same key will be used as id of the channel while updating the preference. | | is\_restricted | This key indicates the restriction level of channel. If restricted, notification will only be sent in the category where this channel is added as mandatory in notification category settings. **True** means Required radio button is selected. **False** means All radio button is selected. | ## Integration ### Get preferences data Use this method to get preferences data and create the preferences UI by following the above sections. This method should be called first before any update preference methods. ```typescript syntax theme={"system"} const preferencesResp = await suprSendClient.user.preferences.getPreferences(args?: {tenantId?: string, tags?: string | Dictionary}); ``` **Returns:** `Promise` ### Update category preference Calling this method will opt-in/opt-out user from that category. When the category is editable and the switch is toggled you can call this method. ```typescript syntax theme={"system"} const updatedPreferencesResp = await suprSendClient.user.preferences.updateCategoryPreference(category: string, preference: PreferenceOptions); enum PreferenceOptions { OPT_IN = "opt_in", OPT_OUT = "opt_out" } ``` **Returns:** `Promise` ### Update channel preference in category Calling this method will opt-in/opt-out users from that category-level channel. When the category's channel checkbox is editable and the user clicks on the checkbox you can call this method. ```typescript syntax theme={"system"} const updatedPreferencesResp = await suprSendClient.user.preferences.updateChannelPreferenceInCategory(channel: string, preference: PreferenceOptions, category: string); enum PreferenceOptions { OPT_IN = "opt_in", OPT_OUT = "opt_out" } ``` **Returns:** `Promise` ### Update overall channel preference This method updated the channel-level preference of the user. ```typescript syntax theme={"system"} const updatedPreferencesResp = await suprSendClient.user.preferences.updateOverallChannelPreference(channel: string, preference: ChannelLevelPreferenceOptions); enum ChannelLevelPreferenceOptions { ALL = "all", REQUIRED = "required" } ``` **Returns:** `Promise` ### Event listeners All preferences update api's are optimistic updates. Actual API call will happen in background with 1 second debounce. Since its a background task SDK provides event listeners to get updated preference data based on API call status. Listen to this event listeners and update the UI accordingly. ```typescript syntax theme={"system"} suprSendClient.emitter.on('preferences_updated', (preferenceDataResp: ApiResponse) => void); suprSendClient.emitter.on('preferences_error', (errorResponse: ApiResponse) => void); ``` Example Usage: For **preferences\_error** event you could show error toast. For **preferences\_updated** event you could update UI with latest data returned in as param in callback function. ## Example For understanding purpose we have added simple example of preferences implementation in React. You could refer this headless example and design the preferences in your Angular or Vue.js etc. If you want to implement in react please refer [@suprsend/react](https://docs.suprsend.com/docs/preferences-1). ```javascript Example.js theme={"system"} import { useState, useEffect } from "react"; import Switch from "react-switch"; import { SuprSend, ChannelLevelPreferenceOptions, PreferenceOptions, } from "@suprsend/web-sdk"; // -------------- Category Level Preferences -------------- // const handleCategoryPreferenceChange = async ({ data, subcategory, setPreferenceData, }) => { const resp = await suprSendClient.user.preferences.updateCategoryPreference( subcategory.category, data ? PreferenceOptions.OPT_IN : PreferenceOptions.OPT_OUT ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; const handleChannelPreferenceInCategoryChange = async ({ channel, subcategory, setPreferenceData, }) => { if (!channel.is_editable) return; const resp = await suprSendClient.user.preferences.updateChannelPreferenceInCategory( channel.channel, channel.preference === PreferenceOptions.OPT_IN ? PreferenceOptions.OPT_OUT : PreferenceOptions.OPT_IN, subcategory.category ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; function NotificationCategoryPreferences({ preferenceData, setPreferenceData, }) { if (!preferenceData.sections) { return null; } return preferenceData.sections?.map((section, index) => { return (
{section?.name && (

{section.name}

{section.description}

)} {section?.subcategories?.map((subcategory, index) => { return (

{subcategory.name}

{subcategory.description}

{ handleCategoryPreferenceChange({ data, subcategory, setPreferenceData, }); }} uncheckedIcon={false} checkedIcon={false} height={20} width={40} onColor="#2563EB" checked={subcategory.preference === PreferenceOptions.OPT_IN} />
{subcategory?.channels.map((channel, index) => { return ( { handleChannelPreferenceInCategoryChange({ channel, subcategory, setPreferenceData, }); }} /> ); })}
); })}
); }); } // -------------- Channel Level Preferences -------------- // const handleOverallChannelPreferenceChange = async ({ channel, status, setPreferenceData, }) => { const resp = await suprSendClient.user.preferences.updateOverallChannelPreference( channel.channel, status ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; function ChannelLevelPreferernceItem({ channel, setPreferenceData }) { const [isActive, setIsActive] = useState(false); return (
setIsActive(!isActive)} >

{channel.channel}

{channel.is_restricted ? "Allow required notifications only" : "Allow all notifications"}

{isActive && (

{channel.channel} Preferences

{ handleOverallChannelPreferenceChange({ channel, status: ChannelLevelPreferenceOptions.ALL, setPreferenceData, }); }} />

Allow All Notifications, except the ones that I have turned off

{ handleOverallChannelPreferenceChange({ channel, status: ChannelLevelPreferenceOptions.REQUIRED, setPreferenceData, }); }} />

Allow only important notifications related to account and security settings

)}
); } function ChannelLevelPreferences({ preferenceData, setPreferenceData }) { return (

What notifications to allow for channel?

{preferenceData.channel_preferences ? (
{preferenceData.channel_preferences?.map((channel, index) => { return ( ); })}
) : (

No Data

)}
); } // -------------- Main component -------------- // const suprSendClient = new SuprSend(publicApiKey); // create suprsend client export default function Preferences() { const [preferenceData, setPreferenceData] = useState(); const getPreferencesData = async () => { // get preferences data tree and set in local state suprSendClient.user.preferences.getPreferences().then((resp) => { if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }); // listen for update in preferences data suprSendClient.emitter.on("preferences_updated", (preferenceData) => { setPreferenceData({ ...preferenceData.body }); }); // listen for errors suprSendClient.emitter.on("preferences_error", (response) => { console.log("ERROR:", response?.error?.message); }); }; useEffect(() => { // autheticate user to suprsend suprSendClient.identify(distinctId).then((resp) => { // call suprsend.identify method before getting preferences getPreferencesData(); }); }, []); if (!preferenceData) return

Loading...

; return (

Notification Preferences

); } // -------------- Custom Checkbox Component -------------- // function Checkbox({ title, value, onClick, disabled }) { const selected = value === PreferenceOptions.OPT_IN; return (

{title}

); } function Circle({ selected, disabled }) { const bgColor = selected ? disabled ? "#BDCFF8" : "#2463EB" : disabled ? "#D0CFCF" : "#FFF"; return (
); } ``` *** # WebPush Source: https://docs.suprsend.com/docs/js-webpush Step-by-Step Guide to setup WebPush notification in javascript websites like React, Vue, and Next.js. WebPush notifications only work over HTTPS. Most browsers allow push to work in localhost also for development purposes. ### Pre-Requisites * Integration of [JavaScript SDK](/docs/integrate-javascript-sdk) ## Integration Steps While creating SuprSend instance you have to pass vapidKey (get it in SuprSend Dashboard --> Vendors --> WebPush). If you want to have custom serviceworker file name instead of `serviceworker.js`(mentioned in step2), you can pass name of it in `swFileName`. ```typescript typescript theme={"system"} new SuprSend(publicApiKey: string, {vapidKey?: string, swFileName?: string}) ``` Service worker file is the background worker script which handles push notifications. Create `serviceworker.js` file such that it should be publicly accessible from `https:///serviceworker.js`. Add below lines of code in that file and replace publicApiKey with key you find in API Keys page in SuprSend Dashboard. ```javascript javascript theme={"system"} importScripts('https://cdn.jsdelivr.net/npm/@suprsend/web-sdk@3.0.3/public/serviceworker.min.js'); initSuprSend(publicApiKey); ``` Call `registerPush` in your code, which will perform following tasks: * Ask for notification permission. * Register push service and generate webpush token. * Send webpush token to SuprSend. ```typescript typescript theme={"system"} const response = await suprSendClient.webpush.registerPush(); ``` **Returns:** `Promise` **NOTE:** This method should be called on user action like button click for better UX, don't call this on page load. ## Other available methods ### Check for permission To check if user has enabled notifications permission use method provided by library ```typescript syntax theme={"system"} suprSendClient.webpush.notificationPermission(): ``` This will return a string representing the current permission. The value can be: **granted:** The user has granted permission for the current origin to display notifications. **denied:** The user has denied permission for the current origin to display notifications. **default:** The user's decision is unknown. This will be permission when user first lands on website. *** # Karix Source: https://docs.suprsend.com/docs/karix-sms Guide to connect your Karix account with SuprSend to send SMS notifications. This section is a step by step guide to set Karix as your SMS service provider. You can use your existing Karix account to integrate, or create a new account from [here](https://manage.karix.solutions/register) ## Pre-Requisites 1. [Create a Karix account](https://manage.karix.solutions/register) 2. [Create an Enterprise DLT account](https://dltconnect.airtel.in/signup/) ## Karix integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click SMS -> Karix from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Account key | You will get this API Key from your Karix account. SuprSend uses this API Key to send SMS on your behalf via your registered Karix account. | | Price per notification | This is the amount you pay per SMS notification to Karix. It helps us to calculate, estimate and optimise your cost spent on notifications. | | DLT Integration -> 'Mobile Operator' | Mobile Operator of your enterprise DLT account | | DLT Integration -> 'Headers' | 6 digit/character sender id registered for your entity ( *You can get the header details from your DLT portal*) *e.g. SPRSND* Also, you can add multiple headers in the list by just typing the header name and clicking on enter | | DLT Integration -> 'User Name' | User Name of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Password' | Password of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Entity ID' | Entity Registration ID linked to your DLT account. You can get the Registration Id from your DLT account homepage. SuprSend uses this info to send messages on your behalf through your registered DLT platform. | ### How to get account key from your Karix account Login to Karix lounge account and follow the below steps: 1. Go to **"Whatsapp Campaign" -> "Profiles"** page from your left navigation menu 2. Click on **"Create keys"** button. 3. Enter the *"key name"* and *"description"* in the create keys modal and save 4. Copy the API Key generated by clicking on the "eye" button and add it to SuprSend Vendor settings page ## Setting callback URL in Karix account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Send a mail to \[[provisioning@karix.com](mailto:provisioning@karix.com)]]\(mailto:[provisioning@karix.com](mailto:provisioning@karix.com)) to enable below tracking to your account level settings: 1. Add the below webhook URL in your account level DLR settings: Post URL: [`https://hub.suprsend.com/webhook/karix/sms/`](https://hub.suprsend.com/webhook/karix/sms/) 2. Enable cust-ref & all tags parameters in the webhook URL ## Enabling URL shortener service in Karix account URL shortening is essential if you are sending URL links in your SMS content. It reduces the number of characters in your SMS and is required for tracking the click rate of your messages. To activate URL shortening, write a mail to \[[provisioning@karix.com](mailto:provisioning@karix.com)]]\(mailto:[provisioning@karix.com](mailto:provisioning@karix.com)) to enable `Auto_shortening service` in your account level settings ## How to register headers through Airtel DLT platform To register header on Airtel DLT platform, you can refer the section: [Sender ID/Mask/Header Registration- DLT Platform](https://enterprise.smsgupshup.com/DLT/senderidRegistration) *** # Karix Source: https://docs.suprsend.com/docs/karix-whatsapp Guide to connect your Karix account with SuprSend to send Whatsapp notifications. This section is a step-by-step guide to set Karix as your WhatsApp service provider. ## Pre-Requisites You'll need a Karix account to complete this tutorial. You can use your existing Karix account to integrate, or [Create a Karix account](https://manage.karix.solutions/register) ## Karix integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click WhatsApp -> Karix from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Sender name | Sender name of your WhatsApp business account on Karix dashboard. SuprSend uses this information to send WhatsApp on your behalf via your registered WhatsApp number in Karix account. | | WhatsApp Mobile Number | Mobile number of your WhatsApp business account. SuprSend uses this information to send WhatsApp on your behalf via your registered WhatsApp number | | Login Username | Username of your Karix WhatsApp dashboard. SuprSend uses this information to upload templates on your behalf on karix dashboard for approval | | Login Password | Password of your Karix WhatsApp dashboard. SuprSend uses this information to upload templates on your behalf on karix dashboard for approval | | Account key | You will get this API Key from your Karix account. SuprSend uses this API Key to send WhatsApp on your behalf via your registered Karix account. | | Price per notification | This is the amount you pay per SMS notification to Karix. It helps us to calculate, estimate and optimise your cost spent on notifications. | ### How to get sender name and Whatsapp mobile number from your Karix account Login to [Karix managebot account](https://managebot.karix.solutions/bot_builder_gui/#/login) and follow the below steps: Go to **"WhatsApp Campaign" -> "Profiles"** page on the left navigation menu If you have already added a WhatsApp number, you'll see Mobile number and Sender Name on the right side of the page or skip to step-3 ## How to get account key from your Karix account Login to Karix lounge account and follow the below steps: 1. Go to [Power APIs](https://karix.solutions/lounge/LoungePage/manageApi.php) page 2. Click on **the `Create keys`** button. 3. Enter the *"key name"* and *"description"* in the create keys modal and save 4. Copy the API Key generated by clicking on the "eye" button and add it to SuprSend Vendor settings page ## Setting callback URL in Karix account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Login to [Karix managebot account](https://managebot.karix.solutions/bot_builder_gui/#/login) and follow the below steps to setup webhook URL: 1. Go to **"WhatsApp Campaign" -> "Webhook\_Config"** page on the left navigation menu 2. Add below webhook URL in "`Delivery rule`"/blo > POST URL : [`https://hub.suprsend.com/webhook/karix/whatsapp/`](https://hub.suprsend.com/webhook/karix/whatsapp/) >
Header: Key - "Content-Type", Value - "application/json" >
Select all delivery events for tracking *** # List sync via database Source: https://docs.suprsend.com/docs/list-sync-via-database How to setup automated user sync in List from your database using SQL queries. Database connector allows you to sync list users using your true source of data from your data warehouse. This way data, product and growth teams can directly setup production notifications and automated marketing campaigns on the 360 degree data without doing any trade-offs on the amount of data available in click stream platforms. This is also one of the best ways of automating your list updates. You can set it up once and set the recurring list sync based on your notifications requirement. ## Pre-Requisites [Setup database connection](/docs/database) ## Setting up list update To update users in the list via database, you need to first create a task with SQL query to extract and transform data and then set an update schedule to define how each sync will be timed and what data will be updated in each sync. You can create a new task by clicking on update users on the list page or by clicking on `New task` button on Sync tasks tab. Please note that lists having database connector as source will only open the database task window on `Update users` click. If you want to update users in a list with different datasource, update it directly from sync tasks tab. **"Sync tasks"** **"database\_sync"** Next, add the task name and list ID and click on `Add SQL query`. You can only add the list ID of existing lists. If you don't have the list pre-created, [create one](/docs/lists#creating-lists) before setting up the sync. When on your task edit screen, select your database connection and start writing the SQL query to fetch users for list sync. If you haven't added your database connection, you can add it directly from your task screen. Read more about [database connection](/docs/database) and how to set it up. ![](https://files.readme.io/0bc69e9-query_editor.gif) Please make sure that you add `distinct_id` column in your query output. This is a reserved column used to identify `distinct_id` of user to be synced in the list. Click `Run` to preview results and make sure that your query selects the right information. The preview will always show the first 10 results. Make sure that your query returns only the rows and columns that you need to update the list or user profile. Each row in your extracted data represents a user that needs to be synced in the list. `distinct_id` is a mandatory column to identify the users to be synced in the list. If you want to update properties in user profile, add one column each for the property that needs to be synced. Avoid using `select *` in your final query since it may overload your database. Though, you can use `select *` while running your query for preview since it puts limit 10 in your query. Also, use variable to only sync source data that changed since the last successful sync interval. **Optimizing your query with Last sync time:** You can use `{{last_sync_time}}` variable in your query to sync only the records that have changed since the last successful sync. This helps you avoid syncing the same records over and over again. We strongly recommend that you index a column in your database representing the date-time each row was last-updated. When you write your query, you should add a WHERE clause comparing your “last updated” column to the `{{last_sync_time}}`. The `last_sync_time` is a ISO date-time representing the timestamp of your previous successful sync. Add relevant data type conversion if you use any other date-time format. Once you are satisfied with your query results, `Save query` and `Commmit changes` it. You'll see sync settings on commit. | Field | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Update type | - Select `Add` if you want to add users on every sync. E.g., you are maintaining a list of new registrations and your query returns all users registered today. - Select `Replace` if you want to replace the list with a fresh set of users on every sync. E.g., you have a list of all users who made a purchase in the last 30 days. | | Schedule | - Set `One-time` sync if the list is created for one-time campaigns and will never be used again. - Set `Recurring` sync if your list is used in a cron or you are syncing user profile. You can setup recurring sync as **every** `**day **hour **minute` or add a **cron expression** for a more granular sync frequency like `every monday at 9:00 am`. | | Create / Update user profile | Enable it if you also want to sync users on every sync. You can also update user properties by mapping columns to the relevant user properties and use it in your template as `{{$user.}}` to send personalized broadcasts. | | Column header | This is the column name from your query output used to map user property. | | is channel? | enable it if the column value is used to update communication channel such as `email`, `sms` or `whatsapp` in user profile. | | User property | add the key in which your user property should be stored. It can be different from the column name. In case of channel, it will be a dropdown with the list of available channels that you can update. | | Manage Existing property | - **Override** will override the value corresponding to the property key on every sync - **Don't Override** will set the property value for missing property keys. Existing property value will not be overridden. This is especially useful for properties which are set one time. E.g., `signup_date`, `first_item_purchased` - **Append** will create an array in the property key and will keep on appending the value to that array on every sync. **User channels are always append operation**. | After defining your sync setting, click on `Next` and add a relevant commit description for later reference. You'll be able to see all your previous edits in the version history. Your sync is now setup and will execute at the interval defined in your sync frequency. ## Enable / disable sync Your sync will be auto enabled the first you commit the changes. You can enable / disable sync with the switch next to edit button. When you disable a sync, we stop running the associated query. We don’t delete the sync, so you can re-enable it later. When you enable a sync, we’ll resume the sync. The next sync interval will send all data that has changed since the last sync. `{{last_sync_time}}` variable will also store the timestamp will also store the timestamp of your last successful sync. So, when you enable it, it will sync all the data that has changed since the last sync. ## Sync logs You can see sync logs on [lists -> logs ](https://app.suprsend.com/en/staging/subscribers/subscriber-list/logs/)tab. This page have sync history of all list syncs. So, filter logs on source type to see logs for database sync. Each row in the log represents a sync with relevant information related to the sync like when did the sync start, who updated the sync and the status of the sync. There are 5 types of sync status: * `Created` - Logs sync has just started. * `In progress` - Users have started syncing in the list. You'll also see the `%progress` in this case. * `Success` - Users are successfully synced in the list. You'll see `Total` users returned in the query, `Processed` users and `Added` users. In most cases, `added` user will be equivalent to `total` users but if it less than total users, it would mean one of 2 things: * Some users were already present in the list and that's why skipped. * Users were not present in SuprSend subscriber list. You can enable `Create / Update user profile` in your sync settings to avoid this scenario. * `Ignored` - These are the syncs which were skipped sync the previous sync was still in progress when the next sync interval occurs. * `Failed` - Sync couldn't be completed due to some errors while running the query, query output doesn't have `distinct_id` column or there was no data returned in your query output. If you see any error which could be internal to SuprSend system, please reach out to our [support](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email). For failed logs, You can hover on the status or click on the sync to see errors. You can also filter to `Show only error logs`. ## Version control All the changes are first saved in the draft version and are only made live once you commit the changes. This allows you to confidently make changes to your query, without affecting any running sync in production. You can also see previous version edits by clicking on the previous versions. *** # Manage Lists Source: https://docs.suprsend.com/docs/lists Create and manage subscriber lists for bulk notifications and campaigns. List is a group of subscribers or recipients to whom bulk messages can be sent. e.g., you can create a list of users who signed up in the last 30 days. You can now manage subscriber lists in SuprSend and send campaigns or [broadcasts](/docs/broadcast) to pre-created lists of users without the need to add each user individually when making the API call. By default, SuprSend creates a list named `all_users`, which automatically includes all users available in your workspace. You can use this list to send campaigns like newsletters and product updates. ## Creating lists You can create lists using one of the following methods: This is the most flexible way of updating users in a list. This way, you can keep the list updated in real-time. You can use [API](/reference/overview) or update it using backend SDK SuprSend supports integration with third-party platforms to sync user and event data. You can also sync user cohorts from these platforms into lists and run campaigns on those user segments from SuprSend. We currently have integration support for below connectors: Need support for another connector? [Please Let us know](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email) You can also update List directly from the SuprSend dashboard. This way, marketing or product teams can send broadcasts directly from the dashboard without any tech involvement. Recommended for cases when you want to send a one-off campaign and only need the list temporarily. This will only sync existing users in SuprSend database. It doesn't create new user profiles. [Refer docs](/docs/users#creating-user-profile-on-suprsend) to create user profiles You'll find the option to create the list and update users on the List Tab. * **To create a list**, click on the `+ New List` button and add the relevant information. `List ID` is the unique identifier for the list. `Name` and `Description` are just for reference so that you know what this list represents. You can also modify list `Name` and `Description` later by clicking on `Modify List` option available in the right side Kebab menu on a list. * Click on the `Update Users` option available in the right side Kebab menu on a list. It will open a dialog like the one shown below Follow below steps to update users in the list - * Choose `Add` to add more users to an existing list of users. e.g., if you are adding today's sign ups in the new sign ups list every day * Opt for `Replace` to replace the current list with a completely new set of users. This is handy in situations where you need to update the list with all users who fit a criteria (like users subscribed to a topic), without needing to track individual additions. Upload a CSV file containing the distinct\_id of the users. Make sure that the column is labeled as `distinct_id` ![](https://files.readme.io/bfd7943-ezgif.com-gif-maker_7.gif) Click on `Update Users` button. You can check the update progress in `Import logs`. You can automate your list update using data directly by writing SQL query on your database. This allows your data, product and growth teams to completely own product and engagement notifications without any development effort. You can also use it to sync user profiles and send personalized broadcasts to users using their profile properties. See how to setup database sync [here](/docs/list-sync-via-database). ## Configure list events You can configure events when a user enters or exits a list. e.g. - you have a list of users **"Active on the platform in last 7 days"**. Whenever a user exists this list, you might want to send a notification to re-engage the users. You can do so using list events. There are 2 types of list events that you can configure * **User Entry**- This will trigger a bulk event `$USER_ENTERED_LIST - ` for all new users added to the list whenever a list is updated. * **User Exit**- This will trigger a bulk event `$USER_EXITED_LIST - ` for all new users removed from the list whenever a list is updated. List events is a list property and can be updated using `Modify List` option on [SuprSend dashboard -> List page](https://app.suprsend.com/en/staging/lists) or using the [list API](/reference/create-list). Event tracking is set as false by default. ## Sending notification to list users * You can either setup individual workflows for list users based on [list events](/docs/lists#configure-list-events). e.g., if you want to notify job seekers who were active on the platform in the last 7 days but left today. * The most recommended way to send notification to list users is via [Broadcast](/docs/broadcast). You can schedule broadcast on this list from the dashboard by clicking on "**Run Broadcast**" option or trigger it programmatically via API.We recommend sending broadcasts using promotional category so you don't end up overloading transactional and system queues. Learn more about how to choose the right notification category [here](/docs/notification-category). Avoid using individual workflows on a consecutive list update of more than 1000 users. It can add a delay in your notification queue. *** # Lists Source: https://docs.suprsend.com/docs/lists-go Manage subscriber lists with Go SDK: create or update lists, and modify users in the list. The Lists SDK methods lets you create / manage list of subscribers. You can then send [broadcast](/docs/broadcast) to all the users in the list or create a workflow that triggers when a new user enters / exits list. ## Create / Update List You can use `suprClient.SubscriberLists.Create` method to create a new list ```go Request theme={"system"} package main import ( "context" "log" suprsend "github.com/suprsend/suprsend-go" ) func main() { // Initialize SDK opts := []suprsend.ClientOption{ // suprsend.WithDebug(true), } suprClient, err := suprsend.NewClient("__api_key__", "__api_secret__", opts...) if err != nil { log.Println(err) } ctx := context.Background() // ================= Create subscriber list subscriberListCreated, err := suprClient.SubscriberLists.Create(ctx, &suprsend.SubscriberListCreateInput{ ListId: "prepaid-users", // max length 64 characters ListName: "Users With Prepaid Vouchers", ListDescription: "Users With Prepaid Vouchers above $250", }) if err != nil { log.Fatalln(err) } log.Println(subscriberListCreated) } ``` ```go Response theme={"system"} { "list_id":"prepaid-users", "list_name":"Users With Prepaid Vouchers", "list_description":"Users With Prepaid Vouchers above $250", "list_type":"static_list", "subscribers_count":0, "source":"None", "is_readonly":false, "status":"active", "track_user_entry":false, "track_user_exit":false, "requested_for_delete":false, "created_at":"2025-03-14T13:42:45.641000Z", "updated_at":"2025-03-14T13:42:45.641000Z", "drafts":"None" } ``` Guidelines on defining the list\_id * `list_id` is case-insensitive. Suprsend first converts list\_id to lowercase before storing it or doing any sort of comparison on it. * `list_id `can be of max 64 characters. * It can contain characters \[a-z0-9\_-] that is alphanumeric characters, \_(underscore) and -(hyphen). ## Get list data You can get the latest information of a list using `suprClient.SubscriberLists.Get` method. ```go Request theme={"system"} subscriberListData, err := suprClient.SubscriberLists.Get(ctx, "_list_id_") ``` ```go Response theme={"system"} { "list_id":"_list_id_", "list_name":"_list_name_", "list_description":"_some sample description for the list_", "list_type":"static_list", "subscribers_count":0, "source":"None", "is_readonly":false, "status":"active", "track_user_entry":false, "track_user_exit":false, "requested_for_delete":false, "created_at":"2025-03-14T13:42:45.641000Z", "updated_at":"2025-03-14T13:42:45.641000Z", "drafts":"None" } ``` ## Get all lists To get the data of all the lists created in your workspace, use `suprClient.SubscriberLists.GetAll`  method ```go Request theme={"system"} allSubscriberList, err := suprClient.SubscriberLists.GetAll(ctx, &suprsend.SubscriberListAllOptions) // default limit 20 allSubscriberList, err := suprClient.SubscriberLists.GetAll(ctx, &suprsend.SubscriberListAllOptions{Limit: 10, Offset: 0}) // max limit 1000 ``` ```go Response theme={"system"} { "meta": { "count": 14, "limit": 20, "offset": 0 }, "results": [ { "list_id": "newsletter_subscribers", "list_name": "Newsletter Subscribers", "list_description": "all users who opted in to receive product updates", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": false, "track_user_exit": false, "requested_for_delete": false, "created_at": "2025-01-14T13:42:45.641000Z", "updated_at": "2025-01-14T13:42:45.641000Z", "drafts": "None" }, { "list_id": "abandoned_application", "list_name": "Signups abandoned application", "list_description": "Users who didn’t finish application", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": true, "track_user_exit": true, "requested_for_delete": false, "created_at": "2025-02-14T13:42:45.641000Z", "updated_at": "2025-02-14T13:42:45.641000Z", "drafts": "None" } ... ] } ``` ## Add subscribers to the list Use `suprClient.SubscriberLists.Add` to add list subscribers. There is no limit to the number of subscribers that you can add to a list. ```go Request theme={"system"} addDistinctIds := []string{"distinct_id_1", "distinct_id_2"} addResponse, err := suprClient.SubscriberLists.Add(ctx, "users-with-prepaid-vouchers-1", addDistinctIds) if err != nil { log.Fatalln(err) } log.Println(addResponse) ``` ```go Response theme={"system"} { "success":true } ``` ## Remove subscribers from the list ```go Request theme={"system"} removeDistinctIds := []string{"distinct_id_1", "distinct_id_2"} removeResponse, err := suprClient.SubscriberLists.Remove(ctx, "users-with-prepaid-vouchers-1", removeDistinctIds) if err != nil { log.Fatalln(err) } log.Println(removeResponse) ``` ```go Response theme={"system"} { "success":true } ``` ## Delete list ```go Request theme={"system"} deleteListResp, err := suprClient.SubscriberLists.Delete(ctx, "users-with-prepaid-vouchers-1") if err != nil { log.Fatalln(err) } log.Println("delete list resp: ", deleteListResp) ``` ## Replace users from the list In case you want to refresh list with a new set of users completely, you can replace users by creating a draft version of the list and updating users in it. This method will create a draft version of the list where you can add the new set of users to replace users. ```go Request theme={"system"} // start sync newVersion, err := suprClient.SubscriberLists.StartSync(ctx, "users-with-prepaid-vouchers-1") if err != nil { log.Fatalln(err) } log.Println("start sync resp: ", newVersion) versionId := newVersion.VersionId ``` You can use this method to add subscribers to List draft version created in Step 1. You'll get `version_id` in start sync response. ```go Request theme={"system"} // ================= Add users to a draft list (with versionId) addDistinctIds := []string{"id-399999", "id-399998"} addResponse, err := suprClient.SubscriberLists.AddToVersion(ctx, "users-with-prepaid-vouchers-1", versionId, addDistinctIds) if err != nil { log.Fatalln(err) } log.Println("add to version resp:", addResponse) ``` You can use this method to remove subscribers from List draft version created in Step 1. You'll get `version_id` in start sync response. ```go Request theme={"system"} // ================= remove users from a list removeDistinctIds := []string{"distinct_id_1", "distinct_id_2"} removeResponse, err := suprClient.SubscriberLists.RemoveFromVersion(ctx, "users-with-prepaid-vouchers-1", versionId, removeDistinctIds) if err != nil { log.Fatalln(err) } log.Println("remove from version resp: ", removeResponse) ``` Once your subscribers are updated in the list, use this method to finish sync and make the draft version updated in above steps live. ```go Request theme={"system"} // finish sync finishSyncResp, err := suprClient.SubscriberLists.FinishSync(ctx, "users-with-prepaid-vouchers-1", versionId) if err != nil { log.Fatalln(err) } log.Println("finish sync resp: ", finishSyncResp) ``` ### Delete Draft list You can also delete draft list if it's created by mistake. ```go Request theme={"system"} deleteVersionResp, err := suprClient.SubscriberLists.DeleteVersion(ctx, "users-with-prepaid-vouchers-1", tempListVersion.VersionId) if err != nil { log.Fatalln(err) } log.Println("delete version resp: ", deleteVersionResp) ``` # Lists Source: https://docs.suprsend.com/docs/lists-java Manage subscriber lists with Java SDK: create or update lists, and modify users in the list. The Lists SDK methods lets you create / manage list of subscribers. You can then send [broadcast](/docs/broadcast) to all the users in the list or create a workflow that triggers when a new user enters / exits list. ## Create / Update List You can use `suprClient.subscriberLists.create` method to create a new list ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendAPIException; import suprsend.SubscriberListBroadcast; import suprsend.SuprsendValidationError; public class Lists { public static void main(String[] args) throws Exception { createList(); } private static Subscriber createList() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); // Create List JSON payload JSONObject payload = new JSONObject().put("list_id", "_list_id_"); .put("list_name", "_list_name_ "); .put("list_description", "_some sample description for the list_"); try { JSONObject res = suprClient.subscriberLists.create(payload); System.out.println(res); } catch (SuprsendException e) { System.out.println(e); } } ``` ```java Response theme={"system"} { "list_id": "list-id", "list_name": "List Name", "updated_at": "2022-12-18T10:40:27.268417+00:00", "list_description": "List description" } ``` Guidelines on defining the list\_id * `list_id` is case-insensitive. Suprsend first converts list\_id to lowercase before storing it or doing any sort of comparison on it. * `list_id `can be of max 64 characters. * It can contain characters \[a-z0-9\_-] that is alphanumeric characters, \_(underscore) and -(hyphen). ## Get list data You can get the latest information of a list using  `suprClient.subscriberLists.get`  method. ```java Request theme={"system"} ... String listId = "_list_id_"; JSONObject res = suprClient.subscriberLists.get(listId); System.out.println(res); ``` ```java Response theme={"system"} { "list_id": "list-id", "list_name": "List Name", "updated_at": "2022-12-18T10:40:27.268417+00:00", "list_description": "List description" } ``` ## Get all lists To get the data of all the lists created in your workspace, use `suprClient.subscriberLists.getAll()`  method ```java Request theme={"system"} ... // To get the list of 20 subscriber lists JSONObject res = suprClient.subscriberLists.getAll(); // Get list with offset = 10 and limit = 20 // JSONObject res = suprClient.subscriberLists.getAll(20,10); System.out.println(res); ``` ```java Response theme={"system"} { "meta": { "limit": 20, "offset": 0, "count": 1 }, "results": [ { "list_id": "list-id1", "list_name": "List Name1", "updated_at": "2022-12-18T10:40:27.268417+00:00", "list_description": "List description1" }, { "list_id": "list-id2", "list_name": "List Name2", "updated_at": "2022-12-19T10:40:27.268417+00:00", "list_description": "List description2" } ] } ``` ## Add Subscribers to the list Use `suprClient.subscriberLists.add()` to add list subscribers. There is no limit to the number of subscribers that you can add to a list. ```java Request theme={"system"} //Add one or more distinct ids in the list String distinctId1 = "id-1"; String distinctId2 = "id-2"; String distinctId3 = "id-3"; ArrayList distinctIds = new ArrayList<>(Arrays.asList(distinctId1,distinctId2,distinctId3)); try { String listId = "l-001"; JSONObject res = suprClient.subscriberLists.add(listId, distinctIds); System.out.println(res); } ``` ```java Response theme={"system"} { "success":true } ``` ## Remove Subscribers from the list You can remove subscribers from the list using `suprClient.subscriberLists.remove()` ```java Request theme={"system"} //Add one or more distinct ids in the list String distinctId1 = "id-1"; String distinctId2 = "id-2"; ArrayList distinctIds = new ArrayList<>(Arrays.asList(distinctId1,distinctId2,distinctId3)); try { String listId = "l-001"; JSONObject res = suprClient.subscriberLists.remove(listId, distinctIds); System.out.println(res); } ``` ```java Response theme={"system"} { "success":true } ``` ## Delete list You can delete a subscriber list using the `supr_client.subscriberLists.delete()` method. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendAPIException; import suprsend.SubscriberListBroadcast; import suprsend.SuprsendValidationError; public class Lists { public static void main(String[] args) throws Exception { deleteList(); } private static void deleteList() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); try { String listId = "_list_id_"; JSONObject res = suprsendClient.subscriberLists.delete(listId); System.out.println(res); } catch (SuprsendException e) { System.out.println(e); } } } ``` ```java Response theme={"system"} { "success": true } ``` ## Replace users in the List In case you want to refresh a list with a new set of users completely, you can replace users by creating a draft version of the list and updating users in it. This method will create a draft version of the list where you can add the new set of users to replace users. ```java Request theme={"system"} JSONObject data = suprClient.subscriberLists.startSync("_list_id_"); ``` Add subscribers to the draft version created in Step-1. You'll get `version_id` in the Start Sync response. ```java Request theme={"system"} JSONObject data = suprClient.subscriberLists.addToVersion("_list_id_", "01HHCTXXXXXXXXXXX", Arrays.asList("_user_id_1", "_user_id_2")); ``` Remove subscribers from the draft version created in Step-1. ```java Request theme={"system"} JSONObject data = suprClient.subscriberLists.removeFromVersion("_list_id_", "01HHCTXXXXXXXXXXX", Arrays.asList("_user_id_1", "_user_id_2")); ``` Finalize the sync and make the draft version live. ```java Request theme={"system"} JSONObject data = suprClient.subscriberLists.finishSync("_list_id_", "01HHCTXXXXXXXXXXX"); ``` ### Delete Draft list You can also delete a draft list if it was created by mistake. ```java Request theme={"system"} JSONObject data = suprClient.subscriberLists.deleteVersion("_list_id_", "01HHCTXXXXXXXXXXX"); ``` # Logs Source: https://docs.suprsend.com/docs/logging Learn how to track, debug, & audit notification logs on SuprSend. SuprSend provides real-time notification logs of sent notifications for quick debugging and auditing. Navigate to the [Logs tab](https://app.suprsend.com/en/production/logs/) on the SuprSend dashboard to access notification logs. ## Data Structure of Logs Every workflow request generates a common entry in the log, allowing developers to gain a holistic view of notifications sent across all channels. You can expand the log to track the entire notification flow of each channel, from request to user delivery and engagement (open, seen, and click). **📘 Set Callback URL in vendor account:** To track notification failure, delivery, seen and click response in your logs, configure SuprSend tracking URL `https://hub.suprsend.com/webhook/*` in your vendor dashboard. This enables SuprSend to get delivery and failure response from your vendor and show the same in dashboard logs. You'll find details of the callback URL in individual [vendor integration](/docs/vendors) documentation. You can also configure your [outbound webhook](/docs/webhook) to get the normalized vendor response back to your application. Notification log conveys all necessary information in a summarized view for easy debugging: | Field | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Workflow | Workflow name. You can click on it to see workflow configuration and summarized analytics on the workflow details page. | | Distinct ID | `distinct_id` of the [user](/docs/users) receiving the message. Clicking on it provides direct access to the subscriber details page, where you can review user channels and profile properties. This will come in handy to debug notification failures caused by missing channel information on a user. | | Delivery Type | `Send to All Channels` -will send notification on all available channels in user profile defined in workflow call.`Smart Delivery` -sequential sending of notifications on available channels one after the other with a delay where delivery stops if the success metric is achieved. [Read more](/docs/smart-delivery) | | User channel | Trigger summary of all channels. (1) `Sent` count- notifications successfully sent by SuprSend to end vendor. This would also mean successful notification delivery to end user if callback URL tracking is set in vendor account. (2) `failed` count- SuprSend failed to send the notifications or if we got delivery failure error from vendor. (3) `in progress` count- Notifications in progress during [Smart Delivery](/docs/smart-delivery) | | Status | `Success` if at least one of the notifications is sent in workflow.`Failed` if all notifications fail in the workflow. | ## Notification Status You can see notification status in real-time as they are being sent by expanding the log. These are all the stages we track: | Stage | Status | Description | | ----- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | Requested | Notification request is accepted by SuprSend. | | 2 | Processed | SuprSend started processing the request. | | 3 | Triggered | Notification request is passed from SuprSend to channel vendor. | | 3 | Trigger Failed | SuprSend encountered error in forwarding the notification request to the vendor. To understand the cause of the failure, simply hover over the 'i' icon within the failed log chip. Failures can be categorized into two types: 1. SuprSend was unable to process the request due to issues like template rendering failure or the user missing the relevant channel information. 2. SuprSend attempted to call vendor API, but vendor didn't accept the request. This can result from errors in the notification payload, such as template mismatches in WhatsApp, invalid vendor credentials, or incorrect channel values like an invalid mobile number or email. | | 3 | Failure by vendor | Vendor accepted the request but failed to deliver the notification to end user. To understand the cause of the failure, simply hover over the 'i' icon within the failed log chip. These errors originate from vendor's end, and for detailed insights into the reason for failure, refer the error guides of individual vendor. | | 4 | Delivered | Notification is successfully delivered to the end user. | | 5 | Seen | Seen is marked by different actions on each channel:**Email**: `opened`**Whatsapp**: `read`**Inbox**: `read (**on click of Mark all as read**) or clicked`**push(android, iOS, web)**: `clicked`**chat(Slack, Microsoft teams)**: *seen is not tracked for chat channels* | | 6 | Dismissed | Dismiss is marked when user clicks on clear all and removes push notification from the tray. Tracked for **androidpush** channel only. | | 7 | Interacted | Interacted is marked when someone clicks on the notification | ### Notification Statuses Tracked Per Channel **Requested, Processed, Triggered** is internal to SuprSend and tracked for all channels. Here is a list of all statuses tracked across channels from delivery onwards: | Channel | Failure by vendor / Trigger Failed | Delivered | Seen | Dismissed | Interacted (Clicked) | | ----------- | ---------------------------------- | --------- | ---- | --------- | -------------------- | | Email | yes | yes | yes | - | yes | | SMS | yes | yes | - | - | - | | Whatsapp | yes | yes | yes | - | - | | Androidpush | yes | yes | yes | yes | yes | | iOSpush | yes | yes | yes | - | yes | | Webpush | yes | yes | yes | - | yes | | Inbox | yes | yes | - | - | yes | | Slack | yes | yes | - | - | - | | MS Teams | yes | yes | - | - | - | **👍 Set Callback URL in vendor account:** To track notification status post-trigger, like trigger failure, delivery, seen and click response in your logs, configure SuprSend tracking URL `https://hub.suprsend.com/webhook/*` in your vendor portal. You'll find details of the callback URL in respective [vendor integration documentation](/docs/vendors). ## Getting Logs and Notification Status Back to your Database You can either configure [outbound webhook](/docs/outbound-webhook) to get instant alert of all notification statuses in a URL endpoint. You can use it for logging on a third party platform and internal alerting of failure cases. Setup [S3 sync](/docs/amazon_s3) for internal analytics or showing notification status to your customers within your product. With this, you can get a consolidated entry of each notification sent and their statues in your database. You can directly query on this data using Athena or import it to data warehouses like Redshift, Snowflake, ClickHouse etc. for analysis. \*\* Trouble debugging? Reach out to Support \*\* You can click on `**i**` icon at the end of logs to see more details related to workflow execution like `brand_id`, `idempotency_key`, `execution ID`. If you have trouble debugging the error, just copy the **execution ID** and share with us either through in-product chat or slack community. We'll be happy to help you debug the issue. ## Filtering Logs Pinpoint the required log faster with filters. You can narrow down your logs based on specific criteria such as user `distinct_id`, `worklow name`, `brand_id`, `idempotency_key`, `status` and `date-time`. To filter logs, click on filter icon in the top right side options on logs page. Select `Failure logs` to just filter records with errors. ### Date Time Filter Date Time filter allows you to filter logs for a given time period. By default, logs for last 30 minutes is shown. You can change the filter to see logs for longer time span or select custom filter to see logs older than 2 days. > Please note that logs retention depends on pricing plan. Check [pricing page](https://www.suprsend.com/pricing) to see log retention timeframe applicable for your plan. ## Subscriber Logs You can also get easy reference of workflow logs on subscriber details page. This can be used for user level auditing and compliance checks, allowing you to reference all notifications sent to a user within a specified time period. You can also access subscriber specific logs directly on logs page by filtering on `distinct_id`. *** # Mailgun Source: https://docs.suprsend.com/docs/mailgun Guide to connect your Mailgun account with SuprSend to send email notifications. This section is a step by step guide to add Mailgun as your email service provider. You can your existing Mailgun account to integrate, or create a new account from [here](https://signup.mailgun.com/new/signup) ## Mailgun integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Email -> Mailgun from the list of Vendors. This will open vendor details page as shown below: 1752 | Form Field | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Private API Key | You will get this Private API Key from your Mailgun account. SuprSend uses this API Key to send email on your behalf via your registered Mailgun account. Checkout the [documentation](/docs/mailgun#how-to-get-api-key-from-mailgun-account) on how to get your Mailgun API Keys | | Webhook Signing Key | You will get this key from your Mailgun account. SuprSend uses this API Key to sign the delivery events received via webhook. Check the [documentation](/docs/mailgun#how-to-get-api-key-from-mailgun-account) on how to get your Mailgun API Keys | | Is Sandbox environment | Select this if you are using Mailgun sandbox domain for sending emails | | Domain Prefix / Sandbox Domain | **Sandbox Domain**- Add sandbox domain name if you are using mailgun sandbox domain for sending emails **Domain Prefix**- Prefix of the subdomain names added in mailgun account. e.g. - if your subdomain name is *mg.mydomain.com*, the domain prefix will be *mg* Leave it blank if your root domain is added as the domain in mailgun account like *mydomain.com* Mailgun domain names can be referred from [here](https://app.mailgun.com/app/sending/domains) | | Data center region | For domain region = EU, select "Europe region" else select "Outside Europe" | | From Name | Default 'From Name' that email will go from. You can override this in the individual template. *e.g. SuprSend* | | From Address | Default 'From Email ID' that email will go from. You can override this in the individual template. *e.g. \[[support@suprsend.com](mailto:support@suprsend.com)]\(mailto:[support@suprsend.com](mailto:support@suprsend.com))* | | Reply Address | Default 'Reply To Email id' on which replies are received. You can override this in the individual template. *e.g. \[[support@suprsend.com](mailto:support@suprsend.com)]\(mailto:[support@suprsend.com](mailto:support@suprsend.com))* | | Price per notification | This is the amount you pay per email notification to Mailgun. It helps us to calculate, estimate and optimise your cost spent on notifications. | ## How to get API Key from Mailgun account Once logged in to your Mailgun account, navigate to [API Security](https://app.mailgun.com/settings/api_security) from your profile. Private API Key can be seen only at the time of creation. You can either use any of your existing keys or **"Add new key"**. **🚧 User role to read API Keys:** Users with "developer" or "admin" role can only read the API Keys. Please check with relevant folks in your team if you are not able to see the API Keys in your account ## Setting callback URL in Mailgun account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Go to **`Sending->Webhooks->Add webhook`** Add below URL for all event types (`Delivered Messages`, `Clicks`, `Opens`, `Spam Complaints`, `Permanent Failure`, `Temporary Failure`) > URL: [`https://hub.suprsend.com/webhook/mailgun/`](https://hub.suprsend.com/webhook/mailgun/) You can add "Unsubscribe" link to your email by enabling **Unsubscribes** in *Sending -> Domain settings (Tracking section)* ## How to add Unsubscription link in email It's recommended to allow recipients to unsubscribe from emails. You can either: 1. **Use your vendor's unsubscribe link** by enabling **Unsubscribes** in *Sending → Domain settings (Tracking section)*. This opts users out of all emails from that vendor. 2. **Use SuprSend’s** [**hosted preference page** ](/docs/user-preferences#hosted-preference-page)for more granular control, allowing users to manage preferences per category while also reducing unnecessary vendor API calls for opt-outs. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don’t give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don’t have them. *** # Mailjet Source: https://docs.suprsend.com/docs/mailjet Guide to connect your Mailjet account with SuprSend to send email notifications. This section is a step by step guide to setup Mailjet as your email service provider You can use your existing Mailjet account to integrate, or create a new account from [here](https://app.mailjet.com/signup?lang=en_US) ## Mailjet integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Email -> Mailjet from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | API Key | You will get from the API Key management section in your Mailjet account settings. This is a mandatory field. SuprSend will use this key to route emails through your Mailjet account. Check the [documentation](/docs/postmark#how-to-get-a-server-token-from-postmark) on how to get your API and Secret Key. | | Secret Key | Secret generate along with your API Key. You will get this from API Key management section in your Mailjet account settings. This is a mandatory field. SuprSend will use this token to route emails through your Mailjet account. Check [documentation](/docs/postmark#how-to-get-a-server-token-from-postmark) on how to get your API and Secret Key. | | From Name | Default 'From Name' that email will go from. You can override this in the individual template. \*e.g. \[[support@suprsend.com](mailto:support@suprsend.com)]]\\ | | From Email | Default 'From Name' that email will go from. You can override this in the individual template. *e.g. [support@suprsend.com](mailto:support@suprsend.com)* From email domain should be verified by Mailjet. | | Reply Email | Default 'Reply To Email id' on which replies are received. You can override this in the individual template.*e.g. [support@suprsend.com](mailto:support@suprsend.com)* | | Price per notification | This is the amount you pay per email notification to Mailjet. It helps us to calculate, estimate and optimise your cost spent on notifications. We use this cost to set the order of channels in smart routing where routing rule is `optimize on cost`. | ## How to get API and Secret key from Mailjet On the API Key Management page, you'll get the option to create API Key. Create one if you haven't already or else you can add the existing API Key and Secret in SuprSend vendor form. ## Configuring Open/Click tracking for emails on Mailjet SuprSend uses webhooks to update notification statuses on delivery, user actions like open/click or the failure scenarios. This allows SuprSend to track notification status in logs and to power analytics. To allow SuprSend to receive these events via webhook, add SuprSend's webhook URL in your Mailjet account. SuprSend's webhook URL: [`https://hub.suprsend.com/webhook/mailjet/`](https://hub.suprsend.com/webhook/mailjet/) On webhook page, add SuprSend's tracking URL `` and apply this URL to all events. ![](https://mintlify.s3-us-west-1.amazonaws.com/suprsend/images/docs/604d0d5-image.png) ## How to add Unsubscription link in email You can directly add unsubscription link in your email using [`$hosted_preference_url`](/docs/user-preferences#hosted-preference-page) variable in your template. This link directs users to the [SuprSend preference page](/docs/user-preferences). With SuprSend preference setting, you have the flexibility to create multiple notification categories based on the type of notifications. This allows users to opt-out of specific notification categories, providing them with granular control over their preferences compared to other email services where they can only opt-out from all marketing communications at once. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don’t give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don’t have them. *** # Errors Source: https://docs.suprsend.com/docs/management-api-errors Common error codes and troubleshooting for the SuprSend Management API. SuprSend uses standard HTTP response codes to indicate the success or failure of your API requests. This page covers common error codes and how to handle them. ### Success Codes | Code | Description | | ----- | ------------------------------------------------------ | | `200` | OK - Request succeeded | | `201` | Created - Resource was successfully created | | `204` | No Content - Request succeeded but no content returned | ### Error Codes | Code | Description | | ----- | -------------------------------------------------- | | `400` | Bad Request - Invalid request syntax or parameters | | `401` | Unauthorized - Authentication required or failed | | `404` | Not Found - Resource not found | | `5xx` | Internal Server Error - Unexpected server error | ## Error Response Format All error responses follow a consistent format: ```json theme={"system"} { "code": 400, "error_code": "validation_error", "type": "ValidationError", "message": "Invalid request parameters", "detail": "The 'name' field is required" } ``` | Field | Type | Description | | ------------ | ------- | ----------------------------------- | | `code` | integer | HTTP status code | | `error_code` | string | Machine-readable error identifier | | `type` | string | Error type classification | | `message` | string | Human-readable error message | | `detail` | string | Additional error details (optional) | ## Common Error Codes and their solutions #### 401 Unauthorized - `authentication_failed` **Causes**: * Missing or invalid service token * Expired service token * Using environment API key instead of service token **Correct Syntax to authorizate Management API**: ```bash theme={"system"} # Ensure you're using a valid service token curl -H "Authorization: ServiceToken YOUR_SERVICE_TOKEN" \ https://management-api.suprsend.com/v1/workflows ``` #### 400 Bad Request - `validation_error` **Common Causes**: * Missing required fields * Invalid field formats * Invalid enum values **Example**: ```json theme={"system"} { "code": 400, "error_code": "validation_error", "type": "ValidationError", "message": "Invalid request parameters", "detail": "The 'name' field is required and cannot be empty" } ``` #### 404 Not Found **Common Causes**: * Given entity in the request is not found **Example**: ```json theme={"system"} { "code": 404, "error_code": "not_found", "type": "NotFound", "message": "Workflow not found", "detail": "Workflow with slug 'welcome-sequence' does not exist" } ``` # Overview Source: https://docs.suprsend.com/docs/management-api-overview Quick overview of SuprSend Management API. **Note:** The SuprSend management API only provides access to the resources managed in **the SuprSend dashboard**, such as workflows, templates, schemas, translations. APIs to manage all other data such as users, trigger workflows, manage tenants, preferences, etc. are available in the [API Reference](/reference/overview). Management APIs can be used to manage and push, pull SuprSend assets across workspaces. ## Authentication The management API authenticates with a ServiceToken authentication. You can get service token by going to your account settings -> Service Tokens tab. ```curl theme={"system"} Authorization: ServiceToken YOUR_SERVICE_TOKEN ``` # Messagebird (SMS) Source: https://docs.suprsend.com/docs/messagebird-sms-errors Possible SMS delivery errors reported by Messagebird and their resolutions. In this guide, we'll cover all the errors returned by the vendor when SuprSend initiates a send call to the vendor. In case of vendor error, you'll see `Trigger failed` or `Failure by Vendor` status in the third step of the logs. Hover over the failed chip to access the specific error message. ## Message error guide MessageBird uses standard HTTP status codes to indicate success or failure of API requests. * HTTP response codes in the 2xx range indicate the request was successfully processed. * HTTP response codes in the 4xx range indicate that there was a client error - e.g. authentication error, not enough balance or wrong/missing parameters. Don't worry, the HTTP response body includes a JSON-formatted response that tells you exactly what went wrong. If you're stuck, feel free to [contact support](https://messagebird.com/en/help), we're happy to help you out * HTTP response codes in the 5xx range indicate a server error. It's considered safe to retry the same request, preferably with a delay *** # Microsoft Teams Source: https://docs.suprsend.com/docs/microsoft-teams Guide to integrate Microsoft Teams App for sending notification to user DM or channels in any workspace. This is a step-by-step guide to integrating and sending notifications to users on Microsoft Teams. ## Pre-Requisites To send notifications on Microsoft Teams, you'll need to create a Teams bot. This bot will be responsible for sending notifications to users / channels in their Teams workspace. You'll also need a Microsoft 365 developer account and permission to add apps to your teams. You can ask your admin to assign you relevant permission, or you can sign up for a free Microsoft 365 developer program. * [Sign up for a Microsoft 365 developer program](https://developer.microsoft.com/en-us/microsoft-365/dev-program/) * [Create a Teams App](docs/microsoft-teams#create-teams-app) ## Create Teams app You'll have to create a [Teams App](/docs/microsoft-teams#step-1-create-teams-app) to send notifications via SuprSend. User will have to add this App in their Teams workspace and channel to start getting notifications through the App. You can learn more about Apps [here](https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/teams-developer-portal). First, [create a Microsoft 365 Developer Account](https://developer.microsoft.com/en-us/microsoft-365/dev-program/). Sign in If you have an existing developer account. You'll need permission to add Apps in your account. Ask your admin to assign you relevant permissions if you are using an existing account. Now, navigate to the Apps section and click on **+New App**. Give your App a suitable name and Add. For your test app, you can use something like "SuprSend Test App" for app name You'll be redirected to a new window where you'll have to add a few information related to your application such as company name, description, website link, URLs to register the App. Now select App feature from your left panel to click on Bot. If you have an existing bot, select it from the list or click on Create a new bot On the [bot page](https://dev.teams.microsoft.com/bots), click on **+New Bot** and add bot name. Add a relevant bot name and **Add**. Once the bot is created, you'll land on bot configuration page. Leave the endpoint blank, select "**Microsoft teams**" in the channel and create **client secret** for your bot. Save your bot secret somewhere safe. You'll need this in SuprSend vendor integration form. Now login to [Azure portal](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) and select All Apps. Here, you'll see the recently created bot. Click on it and copy the **App (client) ID**. You'll need to fill this information in SuprSend vendor integration form. After navigating, select `Accounts in any organizational directory`and save. Now that your bot is created, go back to your developer portal -> Apps and select your last created App. Select App features -> bot. In the bot configuration, add your recently created bot. Choose **Only send notifications** in "what can your bot do?" Add **personal**, **team** and **group chat** in bot scopes and click on Save. In your App permissions, add below permission. | Section | Permissions | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Team Permissions -> Application | `Channel.Create.Group`, `Channel.Delete.Group`, `ChannelMessage.Read.Group`, `ChannelMessage.Send.Group`, `TeamsActivity.Send.Group` | | Chat/Meeting Permissions -> Application | `ChatMessage.Read.Chat`, `ChatMessage.Send.Chat` | | User Permissions -> Application | `TeamsActivity.Send.User` | Publish to org if you want your App to be accessible only inside your organization teams. Publish to store if you want to make it publically available in the teams App store. Your admin will have to approve the App for it to go live. For admin approval, go to [admin portal](https://admin.teams.microsoft.com/policies/manage-apps) and publish it again. Your App might take some time to go live once it is published. Add client ID that you copied from azure portal into your App Basic Information: Now go to [https://teams.microsoft.com/](https://teams.microsoft.com/) and **ADD** the app . First, from left panel select Apps with a + sign and then In the section "Built for this organization", there will be an option to Add the app. ## Add Teams integration on SuprSend Go to SuprSend dashboard -> [Microsoft teams Vendor Settings](https://app.suprsend.com/en/staging/vendors/ms_teams/ms_teams-ms_teams?brand_id=default) page. Add `Application (client) ID` and `client secret` (in Password field) generated while [creating bot in the above step](/docs/microsoft-teams#creating-a-new-bot). ## Update teams channel in user or object profile The information required in the user profile is dependent on the type of message you are sending. To send a direct message to a user, add `tenant_id` , `service_url` of the tenant, and `conversation_id` or `user_id` corresponding to user personal chat. You can pass this information in a json format using `user.add_ms_teams` method as below: ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) # using conversation_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) # using user_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) response = user.save() print(response) ``` ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile // using conversation_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) //using user_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) const response = user.save() response.then((res) => console.log("response", res)); ``` **Getting tenant ID, user ID, conversation ID:** You'll get **tenant ID** from your developer azure portal. You'll get **conversation\_id** from the URL of the chat. You can get **user\_id** by following the steps to [fetch the roster or user profile](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/get-teams-context?tabs=json#fetch-the-roster-or-user-profile). You'll need authentication token to fetch user profile. You can get this token from SuprSend vendor page after saving the form. To send a message to a public or private channel, you’ll need to pass `tenant_id` , `service_url` of the tenant, and `conversation_id` of the teams chat channel. Please note that you'll have to add App in your teams channel to send notification otherwise it will fail. To add App to your teams channel, click on `+` icon to add a tab. In **Add a tab** modal, click on **"More Apps"**. You'll see your App in your organization. Click on the App and **Add to the team**. We recommend creating a separate user for identifying channels and not mix it in user profile. You can create a teams channel user with `distinct_id` as `channel_` so it's easy for you to remember and pass in your workflow calls whenever you want to send message on a teams channel. ```python python theme={"system"} distinct_id = "channel_general" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) response = user.save() print(response) ``` ```javascript node.js theme={"system"} const distinct_id = "channel_general" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) const response = user.save() response.then((res) => console.log("response", res)); ``` To send a direct message using incoming webhook, just pass the webhook URL in user profile. You can pass this information in a json format using `user.add_ms_teams` method as below: ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }) response = user.save() print(response) ``` ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }) const response = user.save() response.then((res) => console.log("response", res)); ``` Avoid using incoming webhook configuration in the case of a multi-tenant architecture where same set of users may belong to multiple tenants and you want to send notifications via [tenant vendor](/docs/tenant-vendor). The incoming webhook is not tied exclusively to the app configuration used for sending notifications and will invariably be dispatched to all incoming webhooks included in the user profile, regardless of the tenant's vendor configuration. **Get Incoming webhook for a teams channel:** To get incoming webhook, select **connectors** from channel -> more options. Configure **Incoming webhook** in connectors. Click on create and copy the webhook URL ### Order of precedence Microsoft teams profile should only contain `incoming_webhook->url`, or the bot configurations like `tenant_id`, `conversation_id` and `serive_url` . If there are multiple keys in `user.add_ms_teams()` call, the order of precedence is as follows: `incoming_webhook->url` > `conversation_id` > `user_id`. E.g., if your add\_ms\_teams argument is as follows: ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" }, "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx", "user_id":"29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) response = user.save() print(response) ``` ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" }, "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx", "user_id":"29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) const response = user.save() response.then((res) => console.log("response", res)); ``` The profile will be saved as `{"incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX"}}` ## Create Teams template Refer [teams template documentation](/docs/ms-teams-template) for step-by-step guide on how to create, preview and publish the template. ## Tenancy in Microsoft teams In scenarios where you're sending notifications on behalf of your enterprise clients, each with their own team applications for communicating with end-users, you can define distinct app configurations at the tenant level. In SuprSend, we presently identify tenants as tenants. To support this, simply include the app configuration for the tenant on the vendor details page. This will override the default tenant vendor at the time of sending notification. This configuration needs to be set up once, and SuprSend will seamlessly direct all notifications associated with that tenant through the vendor configuration specified for the tenant. > In case you don't have vendor defined for a tenant, notifications will automatically fallback to the default vendor credentials. Refer Steps to configure tenant level vendor [here](/docs/tenant-vendor) *** # Migrate from MagicBell to SuprSend Source: https://docs.suprsend.com/docs/migrate-from-magicbell-to-suprsend Migration guide to move your existing MagicBell notifications to SuprSend. SuprSend's API first approach simplifies migration of users, templates, workflows, preferences and Inbox implementation from MagicBell to SuprSend. This guide provides a structured migration plan with steps to facilitate a smooth transition. ## Mapping MagicBell's concepts with SuprSend concepts This section outlines how MagicBell's concepts align with SuprSend's features: ### 1. Project and workspace setup MagicBell's Project maps to SuprSend's Workspace. By default, when you sign up on SuprSend, you will have Staging & Production workspace setup, which can be linked to your respective projects in MagicBell. If you need more workspaces, like `Pre-Production`, reach out to our support team at [support@suprsend.com](mailto:support@suprsend.com). ### 2. Configuring channel providers as vendors In MagicBell, downstream providers are set up as Channel Providers. In SuprSend, these are referred as Vendors and can be configured via vendor tab from right navigation panel on SuprSend dashboard. Check full list of supported Channel types and providers [here](/docs/vendors). ### 3. Migrate users to subscribers MagicBell's Users translate to SuprSend's Subscribers. MagicBell supports user identification via email or external ID, while SuprSend uses `distinct_id` as the only identifier as there can be multiple emails attached to a user or it can be changed with time. If you are using email identifier in MagicBell, pass the same in `distinct_id` and also set email channel separately using `$email` key. Use SuprSend's API or SDK to [create or update subscriber profiles](/docs/users#creating-user-profile-on-suprsend) with the information from your MagicBell user base. You can also identify recipients inline in [workflow trigger](/docs/trigger-workflow#identifying-recipients-inline) without having to migrate all users at once. ### 4. Migrating Notification Inbox Inbox Implementation in SuprSend is similar to MagicBell. Though, we take security quite seriously and HMAC authentication is a must for subscriber authentication in SuprSend. You can quickly implement it using customizable [drop-in react components](/docs/react-in-app-feed) or have your custom implementation with [headless library](/docs/react-sdk-headless-feed). ### 5. Notification templates [Templates](/docs/templates) in MagicBell are designed at category level. In SuprSend, Templates are independent resources and defined in workflow step. Categories are assigned in context to workflows (to apply preference settings). This way, you can start with a basic setup and keep refining your preference categories and workflow logic over time, without having to make any changes in your set resources. You can create templates on the SuprSend dashboard using an intuitive WYSIWYG editor, allowing you to design even complex notification use cases with ease. ### 6. Setup broadcasts as workflows Broadcasts in MagicBell translate to [Workflows](/docs/design-workflow) in SuprSend. While MagicBell supports only single-step triggers, you can design multi-step workflows in SuprSend with advanced options —like delays, batching, conditional branches, throttling and timezone awareness. You can design Workflows on SuprSend dashboard and just keep the trigger in your code. This way, workflow logic is decoupled from code and can be easily modified on the fly. SuprSend also has a [Broadcast](/docs/broadcast) capability, but it’s specifically used for one-time campaigns sent to a targeted list of users. ### 7. Smart delivery In SuprSend, [Smart Channel Routing](/docs/smart-delivery) (akin to MagicBell’s Smart Delivery) is embedded within the workflow rather than defining it a category level. This gives you flexibility to customize trigger sequence at notification level rather than tying it to the category. ### 8. Preference categories In MagicBell, users can opt-out from category and channel level. SuprSend supports more granular [preference](/docs/user-preferences) control down to channel, category, channel within a category and notification frequency (immediately, daily, weekly). Further, in MagicBell, `{{unsubscribe_url}}` in email notifications is powered by topics and unsubscription happens at topic level. To unsubscribe from multiple topics, user will have to go to the email linked to that topic and unsubscribe from it. In SuprSend, the same categories shown in preference centre, are visible on unsubscription page as well, giving users granular unsubscription control. Also, you don't have to define topics and categories separately to power in-app preference page vs hosted unsubscription page. MagicBell organizes most resources at the category level, whereas in SuprSend, categories are linked to workflows, enabling flexible configuration of resources within workflows rather than limiting configurations to categories. ### 9. Topics and subscriptions Topics and Subscriptions are managed with [lists](/docs/lists) in SuprSend in terms of sending notification to a group of users by just defining the topic. Other topic functionalities like threading, batching can be achieved within workflow. Topic unsubscription can be handled by preference categories and available to users via [hosted preference page](/docs/user-preferences#hosted-preference-page). Notification can be sent to lists via [broadcast](/docs/broadcast). List can be updated programmatically via API, synced from third party like Mixpanel, via csv upload, or in an automated manner using database connector. Check all list update methods [here](/docs/list-sync-via-database#setting-up-list-update). ### 10. Additional SuprSend concepts (not available in MagicBell) * **Tenants**: If your application supports multi-tenancy, you can use tenants in SuprSend to trigger [per-tenant notifications](/docs/tenant-workflows), set [per-tenant preferences](/docs/tenant-preference), render per-tenant Inbox feed and route notifications via [tenant vendors](/docs/tenant-vendor). * **Objects**: Use objects to trigger notification to non-user entities (like departments, teams, and projects) and their subscribers. Objects are really powerful at handling hierarchical subscriptions and to send notifications to group channels like group email, slack channel and group Inbox feed. * **Tracking and Monitoring**: SuprSend provides extensive logging capabilities where you can dig deep into each workflow execution and track analytics to gauge overall notification performance. Along with this, you also get audit logs to keep track of user activities on SuprSend dashboard. * **Third party integrations for data-in and out**: In addition to first-party integrations with notification delivery platforms, we also offer connections with customer data platforms (CDPs) ([Segment](/docs/segment), [Mixpanel](/docs/mixpanel)) and [S3 connector](/docs/amazon_s3) to sync notification data back into your warehouse. * **Vendor Fallback**: Configure [vendor and channel fallbacks](/docs/vendor-fallback) in SuprSend for reliable delivery and broader audience reach. ## Migrating data from MagicBell to SuprSend Now that you have a clear understanding of how MagicBell concepts translate to SuprSend, you can start migrating your data. With SuprSend's [APIs and bulk endpoints](/reference), you can easily migrate your notifications into SuprSend. We recommend moving your data in the following order for a smooth transition: Start your setup in the staging workspace for a thorough testing of notifications before making them live for your production users. Configure vendors for the channels of your choice on [SuprSend dashboard -> vendor page](https://app.suprsend.com/en/staging/vendors). You can also start with a single channel (e.g. email) and add other channels later. Adding channels doesn't require any changes in your codebase. All you have to do is update template of that channel and use multi-channel delivery node in your workflows to ensure that it picks the newly set channel for delivery. If you are using MagicBell's preference centre, map the same categories in [SuprSend dashboard -> Developers -> Preference categories](https://app.suprsend.com/en/staging/developers/preference-categories) section. Once configured, publish these categories for use in workflows. Design workflows that mirror your MagicBell broadcast logic and create template for each of these triggers on SuprSend dashboard. If you have a large number of templates, our [support](mailto:support@suprsend.com) team is available to assist you with template migration. You can migrate your users all at once using [user APIs or SDK](/docs/users#creating-user-profile-on-suprsend) or just pass [recipient information in workflow trigger](/docs/trigger-workflow#identifying-recipients-inline) to gradually sync users in SuprSend with each notification trigger. Mapping MagicBell User profile in SuprSend involves: 1. **Mapping user identifiers**: If email is used as an identifier in MagicBell, pass it as `distinct_id` in SuprSend and also set email channel separately using `$email` key. External id can directly be set as `distinct_id` in SuprSend. 2. **Mapping User channels and properties**: * Channels are defined with `$` keys (`$email, $sms, $slack...`) in SuprSend and can support multiple channels in an array. * In MagicBell, fixed properties are passed at parent level whereas custom properties are passed in `custom_attributes` key . In SuprSend, you can pass both fixed and custom properties at parent level. with fixed properties starting with `$` Till this point, all the resources have been migrated to SuprSend. You can now setup [workflow triggers](/docs/trigger-workflow) in your backend code to invoke workflows created on SuprSend dashboard. If you are using Lists and broadcast, use [broadcast API](/docs/broadcast#triggering-broadcast) to trigger broadcast. Thoroughly test all notification use cases in staging workspace. Once tested, you can clone the resources to production workspace to make it live for your end users and gradually shift your notification traffic to ensure a seamless switchover from MagicBell to SuprSend. *** # Migrate in-house Notification System Source: https://docs.suprsend.com/docs/migrating-your-existing-notifications Migrate your in-house notification system to SuprSend with this step-by-step guide. This guide outlines the migration strategy for integrating SuprSend into your existing notification system. If you are setting up notifications from scratch, refer to the [Quick Start Guide](https://docs.suprsend.com/docs/quick-start-guide) instead. If you are looking for technical support, reach out to [support@suprsend.com](mailto:support@suprsend.com) and one of our solutions architect will design the entire migration plan befitting to your current notification architecture and business requirement. ## General migration approach There are three primary migration strategies for syncing your existing users and events: 1. **Do one time migration of all users and setup event stream**, then configure notifications on a per-use case basis. 2. [******Implement workflow API and identify recipients inline******](https://docs.suprsend.com/docs/trigger-workflow#triggering-workflow-via-api) to gradually sync users in SuprSend. 3. **Third-Party CDP Integration:** If you already track data in a CDP (e.g. [Segment](https://docs.suprsend.com/docs/segment)), you can integrate it directly with SuprSend and go live without needing to set up any additional data sync in your codebase. If you have an event-stream-based system or cannot identify users within workflow triggers (e.g. sending to a group via an object), do [one time migration of users](https://docs.suprsend.com/docs/python-create-user-profile#bulk-api-for-handling-multiple-user-profiles) since both cases need pre-creation of users for successful workflow trigger. For rest of the cases, you can go with either approaches. However, we recommend doing Bulk user migration unless you have some PII restrictions on sharing user data. User migration doesn't take more than 2 days of additional effort. ## General guidelines around notification architecture setup ### Migrating backend systems * **Call SuprSend APIs from a central place for easy tracking and maintenance**: Your backend systems can communicate with SuprSend using a microservice or monolith to centralize all your communication triggers at a central place.\ If you don't have these services in place, you can also call SuprSend APIs directly within the code. In this case, you should have a single function that calls SuprSend service and the same function is referred at the required places in the codebase. * **Introduce Messaging Queue for high notification volume**: If you have a respectable notification volume, introducing a messaging queue in between reduces reliability on the uptime of your internal systems and saves potential notification loss due to downtime of your backend machine or server. * **Implement retry mechanism for failure response**: It's a good practice to implement retry logic if SuprSend APIs doesn't return a 20x response. * **Sync existing data and assets**- There are 3 types of data entities in SuprSend - User Data (user, preferences), its modelling (Objects, tenants) and assets (template, workflow). * First, sync your [users](https://docs.suprsend.com/docs/users) and their [preferences](/docs/user-preferences). Do one time migration of existing data and setup integration to auto create new users on every profile change. Release it first so that data starts flowing into SuprSend.\ Skip this step if you are going with inline user identification in workflow API. * Setup additional data modelling for [objects](https://docs.suprsend.com/docs/objects) and [tenants](https://docs.suprsend.com/docs/tenants) alongside user sync. * Asset migration ([templates](https://docs.suprsend.com/docs/templates) and [workflows](https://docs.suprsend.com/docs/design-workflow)) can be done on per use case basis. You can design these directly on SuprSend console or use APIs (*currently in beta*). * **Setup notification trigger**. Integrate direct [workflow API](https://docs.suprsend.com/docs/trigger-workflow#triggering-workflow-via-api) or directly pass user [triggered events](https://docs.suprsend.com/docs/trigger-workflow#event-based-trigger) if you have an existing event stream. You can compare both approaches and see which one fits your use case better [here](https://docs.suprsend.com/docs/event-vs-api-trigger). Most companies opt for a hybrid approach—passing events for user-initiated actions while using APIs for system-generated alerts. ### Migrating frontend applications Frontend implementation can be planned in parallel but it should be released after your backend migration. * **Migrating frontend components (**[**Inbox**](https://docs.suprsend.com/docs/inbox-overview) or [**Preferences**](/docs/user-preferences)): It's a good practice to hide the frontend component behind a feature flag, allowing you to run it in parallel with your existing system. This enables a smooth transition, where you can switch off the current implementation and activate the new component as soon as the backend migration is complete. * **Migrating mobile push notifications:** Requires an app release with [SuprSend SDK integration](https://docs.suprsend.com/docs/android-firebase-fcm-push-integration). SuprSend SDK automatically registers user tokens on app launch, eliminating the need for manual push token migration. Since users will take time to upgrade, you can maintain dual routing—sending notifications via both your existing vendor integration (e.g. FCM, APNS, OneSignal) and SuprSend until near 100% users upgrade to the new app.\ During this transition, both SDKs will receive notifications, but only the notification from the existing vendor integration will be displayed. Once the majority of users have migrated, disable routing through the existing vendor, allowing SuprSend SDK to take control. ## Key migration steps User data migration is required for [event-driven workflows](https://docs.suprsend.com/docs/trigger-workflow#event-based-trigger), as recipient profiles must exist beforehand for successful event triggers. If you are identifying recipients inline within [workflow triggers](https://docs.suprsend.com/docs/trigger-workflow#triggering-workflow-via-api) or using a [CDP connection](https://docs.suprsend.com/docs/segment), you can skip this step. **1.1. Setup user sync:** * Users can be updated via [backend SDK](https://docs.suprsend.com/docs/python-create-user-profile), [frontend SDK](https://docs.suprsend.com/docs/js-events-and-user-methods#update-user-profile), or [direct API calls](https://docs.suprsend.com/reference/create-update-users). * If integrating with the frontend SDK for push or in-app notifications, users will be identified in the frontend application to authenticate them with their respective channel token. In such cases, you can call other profile updates (set out-of app channels (email, sms) or profile properties) through the frontend SDK as well. **1.2. Migration Process:** * First set up and release continuous integration to auto-populate user profiles as they are identified/modified in your database. * Perform a [one-time bulk migration](https://docs.suprsend.com/docs/python-create-user-profile#bulk-api-for-handling-multiple-user-profiles) of existing users. Since, profile updates are upsert functions, syncing the same profile again will not create duplicate users. **1.3. Required Data:** At a minimum, each user profile must include a unique, immutable identifier and channel details (e.g. email, phone number) for communication. Additional profile attributes, such as language and timezone, can be progressively synced alongside notification triggers. In addition to users, your system may have other entities that require notification modelling from the start. If your system only requires direct user notifications and you always know the recipient in your trigger, you can just migrate users and you are good to go. For other cases, SuprSend supports two key concepts for user modelling: Use [tenants](https://docs.suprsend.com/docs/tenants) when, * You are a SAAS platform where customer [notification preferences](https://docs.suprsend.com/docs/tenant-preference) can be controlled by your org or end customer admin. * Sending notifications on behalf of customers and need to white label notification or route via customer's vendor credentials (their email address) Use [objects](https://docs.suprsend.com/docs/objects) when, * You need to notify a group of users without having to identify individual recipients in the trigger. E.g., send notification to all followers or subscribers when a new content is posted on a channel or send an alert to associated users of a task on status change. * You need to send notification to non-user entities like company slack channel, email group or shared inbox feed. Eg. Send incident report or anomaly alerts on a company's Slack channel. * If your templates are stored in your database, they can be migrated using template APIs (*currently in beta, [contact support](mailto:support@suprsend.com) for migration assistance*). We use handlebars as the templating language. So, if you have your variables in a different language, convert it into handlebars before syncing. * If templates are part of your final trigger, you can create a template with all fields set as variable and pass the corresponding content dynamically in the trigger. However, to fully leverage the platform, we recommend a one-time bulk migration of templates. This will reduce engineering overhead as it the templates will be decoupled from the code and product / design team can manage template updates independently without engineering involvement. * For email templates, we recommend using our visual editor to design [template](https://docs.suprsend.com/docs/templates) directly on [SuprSend console](https://app.suprsend.com/en/staging/templates). While the initial setup may take some time, it significantly simplifies future template iterations. It also enables non-engineering teams to update templates independently, reducing dependency on developers. [Workflows](https://docs.suprsend.com/docs/workflows) contain notification logic, such as scheduling reminders (e.g. cron jobs) or determining when to trigger specific notifications based on conditions. Currently, this logic would be residing in your code and you would be calling vendor APIs to send the final notification.\\ We recommend building these logics on SuprSend. This decouples logic from your code and and all you have to do is simply trigger workflow, simplifying future iterations and reducing engineering overhead. Workflows can currently be designed on SuprSend console. We'll soon be exposing APIs for the same.\\ To streamline migration, **start by migrating 3-4 key notification** use cases in the first release. Gradually expand to cover all notification scenarios over time. * **Design Implementation**: You can integrate with the relevant SDK and replicate your existing Inbox UI using customizable [react components](https://docs.suprsend.com/docs/react-customize-inbox) or a [headless library](https://docs.suprsend.com/docs/headless-inbox). SuprSend's Inbox SDK gives you the flexibility to design any UI you want. * **Feature Flagging**: After achieving the design parity and implementing the Inbox integration, hide the new Inbox behind a feature flag and run it in parallel with your current implementation. * **Releasing without downtime**: As soon as your backend is released, remove the feature flag, switch off the current implementation and show SuprSend Inbox component. The notifications sent during parallel run of current and new SDK will be visible in SuprSend Inbox when switched. * Use [Preference API](/reference/overview) for a one-time migration and capture future preferences through SuprSend’s [hosted preference page](/docs/user-preferences#hosted-preference-page) or [embeddable UI](/docs/user-preferences#hosted-preference-page). * If you have an existing preference centre in your application, you can continue to use that. Just pass the updates via API back to SuprSend. Some companies also pass this data along with workflow trigger but we generally recommend syncing preferences in SuprSend, so you don't need to compute this everytime on trigger. * If preferences are managed directly by vendors (e.g. email opt-outs via suppression lists), SuprSend will automatically track these and mark the emails as inactive for a given period, generally 30 days, to ensure that we don't keep making vendor calls in case of hard bounce. In this case, You can avoid adding SuprSend preference centre in the first integration, but we recommend adding it later. Vendor unsubscription page doesn't give granular preference control to users and leads to user resorting to channel-level unsubscriptions. After data migration and setup, next is setup your [workflow trigger](https://docs.suprsend.com/docs/trigger-workflow). There are 2 ways to setup workflow triggers. You can compare them [here](https://docs.suprsend.com/docs/event-vs-api-trigger) and see which is the most suitable for you. ## Rollout plan Now, that you've understood how the migration will happen, you don't need to setup everything in the first phase and plan your release in multiple phases. Here are some of the phases and the timeline for the migration tasks in each phase. You can rollout all notifications in phase 1 or divide your migration in 2 phases, migrate 3-4 use cases at the start and then migrate rest of the use cases in phase 2. | **Release Phase** | **Migration task** | **Who needs to get involved** | **Timeline** | | :---------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :---------------------------------------------------------------- | | Phase 1 | - Setup User sync for new user updates - \[Optional] Setup data modelling for objects, tenant - Setup Event stream | Engineering | 4-6 days | | Phase 1 | Do an early release to start syncing user and event data in SuprSend | Engineering | 2-3 days | | Phase 1 | Vendor Integration | Engineering | \< 1 day | | Phase 1 | Create assets for initial set of notifications (workflow and template) | Product and Design | 4-5 days | | Phase 1 | Setup workflow trigger if different from event stream | Engineering | 2-3 days | | Phase 1 | Add SuprSend In-app Inbox in Frontend Application | Engineering | \< 1 day (with react components) 4-5 days (using headless hooks) | | Phase 1 | Create Preference categories on SuprSend platform and link them to the relevant workflows | Product, Engineering | \< 1 day | | Phase 1 | Add SuprSend Preference centre in Frontend Application | Engineering | 4-5 days | | Phase 1 | \[Optional]Set up push notification | Engineering | 1 day | | Phase 1 | Migrate Existing Users | Engineering | 4-5 days | | Phase 1 | Backend: Testing and Go-live | Product, Engineering | 7-10 days | | Phase 1 | Frontend: Release web and mobile applications | Engineering | 3 - 4 days | | Phase 1 | Migrate User Preferences | Engineering | \< 1 day | | Monitoring | Testing and Monitoring the changes on production | Engineering | 7 days | | Phase 2 | Adding more notification use cases | Product, Engineering | 2 - 3 hours per additional notification | # Migration guide from v1 Source: https://docs.suprsend.com/docs/migration-guide-from-v1 Guide for migrating from v1 and v2 to v3 of the SuprSend Web SDK. # Migrating to v4 from v3 This migration is pretty simple. * Pagination related meta data keys have been changed in InApp Feed. ```javascript v4 theme={"system"} pageInfo = { total: 0, pageSize: DEFAULT_PAGE_SIZE, hasMore: false, // this key is added in v4 }; ``` ```javascript v3 theme={"system"} pageInfo = { total: 0, currentPage: 0, // this key is removed in v4 totalPages: 0, // this key is removed in v4 pageSize: DEFAULT_PAGE_SIZE, }; ``` # Migrating to v3 from v2 This migration is pretty simple. This version also supports InApp Feed support. * SuprSend class export has been changed from default export to named export. ```javascript v3 theme={"system"} import { SuprSend } from "@suprsend/web-sdk"; ``` ```javascript v2 theme={"system"} import SuprSend from "@suprsend/web-sdk"; ``` # Migrating to v2 from v1 Migrating from v1 to v2 has breaking changes, as we have made some architectural level changes. Refer the v1 SDK [documentation](https://docs.suprsend.com/v1.2.1/docs/javascript-sdk) . Following are changes in detail: ### Authentication changes In v1, workspace key and workspace secret are used to authenticate requests made to SuprSend which is not so secure. In v2 we have changed authentication to use public API Key and Signed User Token. ```javascript v2 theme={"system"} const suprSendClient = new SuprSend(publicApiKey); suprSendClient.idenitfy(distinctId, userToken); ``` ```javascript v1 theme={"system"} suprsend.init(workspace_key, workspace_secret); ```
### Initializing SDK v1 used `init` method to initialize SDK and suprsend instance was provided by SDK itself. In v2 we have provided `SuprSend` class and its clients responsibility to export the class instance and use it in other places to call library methods. ```javascript v2 theme={"system"} export const suprSendClient = new SuprSend(publicApiKey); suprSendClient.track("test"); ``` ```Text v1 theme={"system"} suprsend.init(workspace_key, workspace_secret); suprsend.track("test") ```
### Synchronous methods In v1 all methods used to be asynchronous and have returned void. In background SDK used to batch requests and make API calls. In v2 we have made all requests synchronous so you could access response of API immediately depending of status of API call. Almost all methods including preference methods return response type of [API Response](https://docs.suprsend.com/docs/integrate-javascript-sdk#response-structure). ```javascript v2 theme={"system"} const trackResponse = await suprSendClient.track("test"); console.log(trackResponse.status); // success or error ``` ```javascript v1 theme={"system"} const resp = suprsend.track("test"); // resp will be null ```
### Renamed methods and arguments to camelCase In v1, all library methods and method parameters are in snake\_case which has been changed to camelCase in v2. ```javascript v2 theme={"system"} // examples suprSendClient.user.addEmail(); suprSendClient.user.preferences.getPreferences({ tenantId: "test" }); ``` ```javascript v1 theme={"system"} // examples suprsend.user.add_email(); suprsend.user.preferences.get_preferences({ tenant_id: "test" }); ```
### Removed `purchase_made` method In v2 `purchase_made`method has been removed. If you are using this method in v1 you can directly call track method with event type: `$purchase_made`. ```javascript v2 theme={"system"} suprSendClient.track("$purchase_made", { item: "ps5" }); ``` ```javascript v1 theme={"system"} suprsend.purchase_made({ item: "ps5" }); ```
### Removed `set_super_properties` In v2 `set_super_properties` method has been removed. If you are using this method in v1 you could directly pass all these super properties as individual event properties.
After migrating please test all library methods to see if they everything is working properly. If you face any issue in migration process please reach out to us on our [slack community](https://suprsendcommunity.slack.com/join/shared_invite/zt-1bs6rbxr8-zI6SyJQd2b~XMpM9uaT1Bw#/shared-invite/email) or drop an email to us on [support@suprsend.com](mailto:support@suprsend.com) # Mixpanel Source: https://docs.suprsend.com/docs/mixpanel Guide to integrate Mixpanel with SuprSend for syncing user cohorts & triggering notifications using subscriber lists. SuprSend Mixpanel integration enables you to export user cohorts from Mixpanel and create [subscriber lists](/docs/lists) on SuprSend. You can then trigger notification to this list using our [broadcast](/docs/broadcast) API. ## Integration To start syncing Mixpanel data, you need to first setup custom webhook integration in your Mixpanel dashboard. Go to [SuprSend dashboard -> Settings -> Connectors](https://app.suprsend.com/en/staging/connectors) page. Just add mapping key and click on enable. Mapping key is the unique identifier of user on SuprSend. Once enabled, you'll see webhook configuration details. Go to **`Mixpanel dashboard -> Integrations -> Custom Webhook`** section and click on **`Add connection`** Copy paste the webhook details from SuprSend integrations page to Mixpanel custom webhook settings and click on "Continue" SuprSend webhook is now setup in your Mixpanel account. You can now start exporting user cohorts. ## Setup list (User Cohorts) sync Cohort is a filtered list of users in Mixpanel. You can export cohort to create subscriber list in SuprSend. Follow below steps to export cohort: After Creating a cohort, add filter and give a suitable name to the cohort. This name will also be the name of the subscriber list on SuprSend. Once cohort is created, export the cohort to the SuprSend webhook that we just configured. You can setup 2 types of cohort exports - One time and Recurring. We recommend using recurring sync to keep Mixpanel and SuprSend data in sync. Click on Begin Sync to start cohort sync. Data is updated every 30 minutes from sync start time. Congratulations! Mixpanel integration is now complete. *** ## Check synced logs You can now track the status of cohort sync on Mixpanel [logs page](https://app.suprsend.com/en/demo/connectors/mixpanel/3/logs). Logs can be accessed from the connector page itself. # Mobile Push Source: https://docs.suprsend.com/docs/mobile-push-quick-start Quickly set up & send Mobile Push notifications using SuprSend SDK ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Integrate push channel in your application For `Android` applications, Integrate SuprSend SDK and FCM (service provider for sending android push notifications). | Language | Documentation | Example | | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | | **Native Android** | [Integrate Android SDK](/docs/integrate-android-sdk) and [Integrate FCM](/docs/android-firebase-fcm-push-integration) in native application | - | | **React Native (Android)** | [Integrate React Native SDK](/docs/react-native-android-integration) and [Integrate FCM](/docs/react-native-androidpush-integration) in react native application | [GitHub](https://github.com/suprsend/suprsend-rn-sdk/tree/main/example) | | **Flutter (Android)** | [Integrate Flutter SDK](/docs/flutter-android-integration) and [Integrate FCM](/docs/flutter-androidpush-integration) in flutter application | [GitHub](https://github.com/suprsend/suprsend-flutter-sdk/tree/main/example) | For `iOS` applications, Integrate SuprSend SDK and APNS (service provider for sending iOS Push notifications) | Language | Documentation | Example | | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | | **Native iOS** | [Integrate iOS SDK](https://docs.suprsend.com/docs/ios-integration) and [Integrate APNS](https://docs.suprsend.com/docs/ios-apns-push) in native application | - | | **React Native (iOS)** | [Integrate React Native SDK](/docs/react-native-android-integration) and [Integrate APNS](/docs/react-native-ios-push-integration) in react native application | [GitHub](https://github.com/suprsend/suprsend-rn-sdk/tree/main/example) | | **Flutter (iOS)** | [Integrate Flutter SDK](/docs/flutter-ios-integration) and [Integrate APNS](/docs/flutter-ios-push-integration) in flutter application | [GitHub](https://github.com/suprsend/suprsend-flutter-sdk/tree/main/example) | ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period of 30 days. 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
### Identify user to attach push token to their profile Call this method as soon as you know the identity of user, that is after login authentication. Pass the id that you use as internal user identifier (UUID, email or numeric code). You'll use this same id in [recipient field](/docs/mobile-push-quick-start#5-trigger-the-workflow) to trigger the notification. ```kotlin Kotlin (Native Android) theme={"system"} ssApi.identify(_distinct_id_) //Sample Values ssApi.identify("291XXXXX-62XX-4dXX-b2XX"); ssApi.identify("johndoe@suprsend.com"); ``` ```swift Swift (Native iOS) theme={"system"} SuprSend.shared.identify(identity: _distinct_id_) //Example SuprSend.shared.identify(identity: "291XXXXX-62XX-4dXX-b2XX"); SuprSend.shared.identify(identity: "johndoe@suprsend.com"); ``` ```javascript Javascript theme={"system"} suprsend.identify(_distinct_id_); //Sample Values suprsend.identify("291eaa13-62f5-4d52-b2dd"); suprsend.identify("johndoe@gmail.com"); ``` ```dart Dart (Flutter) theme={"system"} suprsend.identify(_distinct_id_); //Sample suprsend.identify("291eaa13-62f5-4d52-b2dd"); suprsend.identify(10005); suprsend.identify("johndoe@gmail.com"); ``` #### Call reset to clear user data on log out Don't forget to call reset on user logout. If not called, user id will not reset and multiple tokens and channels will get added to the `distinct_id` who logged in first on the device. ```kotlin Kotlin (Native Android) theme={"system"} ssApi.reset() ``` ```swift Swift (Native iOS) theme={"system"} SuprSend.shared.reset(unsubscribeNotification: true) ``` ```javascript Javascript theme={"system"} suprsend.reset(); ``` ```dart Dart (Flutter) theme={"system"} suprsend.reset(); ``` ### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger): You can trigger these events from your frontend application or from your backend systems, depending on the use case. (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery) to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * **Template** in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Push templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. Learn how to design [Android Push](/docs/android-push-template) and [iOS Push](/docs/ios-push-template) template here. ![](https://files.readme.io/e5b9db7-template_edit.gif) 3\. **Functional nodes (Optional)**- These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '`Text`' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**- End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id` within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or create user profile first with [user creation method](/docs/users#creating-user-profile-on-suprsend) for event based trigger. 2. **Data or Event Properties**- This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow)
**Sample payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys ](https://app.suprsend.com/en/sandbox/developers/api-keys). Push channel will be updated in user profile as soon as you identify the user in [step 4](/docs/mobile-push-quick-start#4-identify-user-to-attach-push-token-to-their-profile), so you can just pass the `distinct_id` and data in the trigger. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Microsoft Teams Source: https://docs.suprsend.com/docs/ms-teams-quick-start Quick set up guide to start sending notification on MS Teams chat via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Create MS Teams App To activate MS Teams Integration, you'll have to [create a Teams App](/docs/microsoft-teams#step-1-create-teams-app) and [add integration in SuprSend vendor page](/docs/microsoft-teams#step-2-add-teams-integration-on-suprsend). ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period of 30 days. 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger): You can trigger these events from your frontend application or from your backend systems, depending on the use case. (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery) to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * **Template** in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. You can choose to edit in markdown or JSONNET format. For simple text messages, use markdown (add variables in handlebars format as `{{var}}`) and for complex adaptive card designs, you can use [JSONNET](https://jsonnet.org/ref/language.html) editor. Variables can be added as `data.var` or `data["$batched_events"][0].var` for batched alerts in JSONNET editor.Ensure to publish the template before using it in a workflow. [Learn how to design MS Teams template here](/docs/ms-teams-template). ![](https://files.readme.io/e5b9db7-template_edit.gif) 3\. **Functional nodes (Optional)**- These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '`Text`' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**- End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id` within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile](/docs/users#creating-user-profile-on-suprsend) first for event based trigger. 2. **Data or Event Properties**- This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow)
**Sample payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys ](https://app.suprsend.com/en/sandbox/developers/api-keys). Here, we are defining MS teams channel inline using `conversation_id` and `tenant_id`. Refer this doc on how to get conversation\_id and tenant\_id for your teams account. Service URL will change depending on your region. Example service URL- ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }], "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$ms_teams", new JSONObject() .put("tenant_id", "c1981ab2-9aaf-xxxx-xxxx") .put("service_url", "https://smba.trafficmanager.net/amer") .put("conversation_id", "19:c1524d7c-a06f-456f-8abe-xxxx")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs Once triggered, you can monitor: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Microsoft teams Template Source: https://docs.suprsend.com/docs/ms-teams-template How to design simple MS Teams template using markdown editor or use JSONNET editor to replicate Microsoft's adaptive card design. ## Design Template You can design a simple text template with our default markdown editor. For advanced formatting and interactive options like buttons, images, and avatars, you can use Microsoft's [adaptive card design](https://adaptivecards.io/designer/) in JSONNET editor. Please note that the variable format for both editors will be different. We'll cover more about it in the next sections. ### Design simple text template using Markdown editor You can send simple text templates using the markdown editor in teams. Variables has to be added in handlebars format. Supported markdown syntax in Teams text messages: | Format | Syntax | | ----------------- | ------------------------------------------------------------------------------------------------------------------ | | Headings | `# Heading level 1`. Refer all [heading formats](https://www.markdownguide.org/basic-syntax/#headings) here | | line break | end a line with two or more spaces, and then type return (*only 1 continuous line break is supported*) | | **Bold** | `**bold text**` or `__bold text__` | | *Italic* | `_italic text_` or `*italic text*` | | ***Bold Italic*** | `***bold italic text***` or `___bold italic text___` | | >Blockquotes | `> block quote text` | | Lists | (1) First item (2) Second item Refer more [lists format here](https://www.markdownguide.org/basic-syntax/#lists-1) | | `code` | `At the command prompt, type `code`.` | | Links | `[Duck Duck Go](https://duckduckgo.com)` | ### Adding dynamic data in markdown editor We use `handlebarsjs` as the template variable language in the markdown editor. You can learn about `handlebarsjs` [here.](/docs/handlebars-helpers) If you are new to templates, we would recommend reading the [templates documentation](/docs/templates) first to understand the basics of templating with SuprSend. To add dynamic data, first add the variable data from your event and workflow request in the `Mock data`. Enter the variables in `JSON` format as shown in the screenshot below. This JSON should be the same as passed in your workflow or event request (it is a part of the `data` field for workflow and `properties` field for event). Now, you can use your added sample by adding variables in the template. Type `{{` and you'll start getting auto-suggestions of the variables added in your sample data below the text field. You can select the variable from the dropdown or directly type it in. As a general rule, all the variables have to be entered within double curly brackets: `{{variable_name}}`. For URLs and links, we recommend using triple curly braces `{{{url}}}`.This is to avoid escaping html entities in handlebars. You can also write transformations like date-time formatting, if-else statement inside your templates using [handlebar helpers](/docs/handlebars-helpers). ### Design adaptive card template using JSONNET editor You can switch to JSONNET editor for advanced formatting and interactive options like buttons, images, and avatars. Microsoft provides support for [adaptive card design](https://adaptivecards.io/designer/) to design such templates using a drag-and-drop editor. You can design your template in the adaptive card designer and copy the JSON payload from your designer into the Teams JSONNET editor. We have also created some sample templates for a quick start. You can choose one of the samples from the right side options on top of the editor to start editing. We have used some mock data in our examples. Copy-paste the mock data values in the global `Mock Data` button to see the preview. Please note that the variable format `${variable}` used in adaptive card is not supported in the JSONNET editor. You'll have to add the variable in JSONNET format as `data.variable` ![](https://files.readme.io/d7495e4-ezgif.com-optimize.gif "slack_editor.gif") #### Adding dynamic data in JSONNET editor To add dynamic data, first add the variable data from your event and workflow request in the `Mock data`. Enter the variables in \`JSON\` format as shown in the screenshot below. This JSON should be the same as passed in your workflow or event request (it is a part of the \`data\` field for workflow and \`properties\` field for event). Once the variables are declared, you can add them in the template as `data.`. Note that you will be able to enter a variable name even when you have not declared it inside the 'Mock data' button. However, the preview will not load without declaring the variables in `Mock Data`. Hence it's advised to always declare variables before loading the preview of the template. Below are some examples of how to enter variables in the JSONNET editor. We'll use the sample shown in the screenshot above as a reference point. To enter a nested variable, enter in the format `data.var1.var2.var3`. Eg. to refer to city in the example above, you need to enter `data.location.city` If you have any space in the variable name, enclose it in square bracket `data.event['first name']` or `data[$brand].brand_name` To refer to an array element, enter in format `data.var[*index*].var2}}`. Eg. to refer to `product_name` of the first element of the array `array`, enter `data.array[0].product_name` Enter in the below format to add a dynamic list of items. In the above example, variable `array` has a list of product items, with `product_name`, and `product_price` as array properties. Below is an example code to fetch array items in a text field list: ```json JSONNET theme={"system"} { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "body": [ { "text": product.name +" at "+ product.price, "type": "TextBlock", "wrap": true } for product in data.products ], "type": "AdaptiveCard", "version": "1.6" } ``` You'll need to add mock data for the variables added in your JSONNET template to view the parsed JSON template. JSON template will throw an error in case of missing mock data for a variable ## Preview message Click on the `Load Preview` button to load the message preview. > You will be able to see if your template is rendering properly with the sample mock data by loading the preview of your draft version. Make sure to see the preview before publishing the template to check if the parsed JSON is valid or not. ## Publish the template Once finalized, you can publish the template by clicking on `Publish template` button on the draft version. Your template is now ready to be triggered. At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will still be sent. Eg. if there is an error in rendering Teams template, but email template is successfully rendered, Teams notification will not be triggered, but email notification will be triggered by SuprSend. *** # Internationalization Source: https://docs.suprsend.com/docs/multi-lingual-template Guide on handling multiple languages in the template & publish language-specific versions. SuprSend allows you to create notifications in multiple languages in the same template. Once the languages are added, SuprSend will pick the preferred language from user's profile and send the message as per the user's language. You can set user preferred language using our [Python](/docs/python-create-user-profile#set-preferred-language) or [Node](/docs/node-create-user-profile#set-preferred-language) SDK English is the default fallback language. So users with no language preference set or preferred language for which content is not present in template will get the notification in English language. You can enable multi-lingual templates by simply following the below steps: In any template, English (en) language in enabled by default. To add other languages, click on `Language` button on the top right side of template editor. This will open language selection modal. Search the required language, add it and save the changes. ![](https://files.readme.io/f3b4017-ezgif.com-gif-maker_5.gif) You'll also see `Auto Translate` option on top of the language modal. Enable it if you want SuprSend to translate the English content in your preferred language. [See how auto translation works](/docs/how-auto-translation-works) Once the required languages are added, you'll see the enabled languages in the side panel - draft version. The languages are set at template group level, that is it will be enabled for all the channels at once. However, you can choose which language content to publish at the time of publishing the draft. Add the content for each of these languages. After making the changes, click on `Publish draft button`. Add a relevant version name and Click on `Next`. Select the languages that you want to Publish. English language will be published by default. If you see an error message below any language (Spanish in above image), that means there are some missing fields in the template form for that language. You'll not be able to publish the language with incomplete information. You can go back, add the missing fields for that language and publish it again or skip this language and publish. That's it. Published languages can now be seen in the live version. *** # Multi Tabs Source: https://docs.suprsend.com/docs/multi-tabs Learn how to set up stores to filter and display notifications in separate inbox tabs such as Read, Unread, and more. ## Define stores in SDK For supporting multiple tabs, list of stores need to be defined while initializing inbox SDK. If stores is ignored you can get all notifications. ```javascript IStore theme={"system"} interface IStore { storeId: string label?: string query?: { tags?: string | string[] categories?: string read?: boolean archived?: boolean } } ``` | Property | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | storeId | Unique identifier that identifies the store from list of stores. This can be any string. | | label | This is used to show name on Tab for that store. If not provided storeId will be shown on tab, if you are using inbox with SuprSend's UI. | | query | depending on use case you can design query for grouping inbox notifications in specific store/tab. Ignore this field if you want to get all notifications. | | tags | Pass string or array of string to filter notifications that only has any one of those tags. Ignore this field to get all notifications irrespective of tags. You can add tags while designing inbox template inside Advanced configuration section. After publishing it you can click on tag to copy it. | | categories | Filter notifications based on notification category. Ignore this field to get all notifications irrespective of category. | | read | Used to get all notifications which are in read state. Ignore this field to get all notifications irrespective of read status. | | archived | Used to get all notifications which are archived. Ignore this field to get all unarchived notifications. | ```javascript Example theme={"system"} stores = [{ storeId: "testing", label: "Test", query: { categories: "transactional" , read:true, tags:["profile", "user"]} }] ``` The above example gets notifications which belong to transactional category and in read state and has profile or user tag. *** # MySQL Source: https://docs.suprsend.com/docs/mysql Guide to set up MySQL database connection to auto sync subscriber lists. With this integration, you can directly send notifications on your 360 degree data sitting in your data warehouse. This enables your data teams or product managers to automate user syncing, setup recurring user cohort sync and create [subscriber lists](/docs/lists) on SuprSend. You can then trigger notification to this list using our [broadcast](/docs/broadcast) API. ## Getting started To start syncing data from your MySQL database, you need to add this integration on the connector page. 1. Go to [SuprSend dashboard -> Settings -> Connectors](https://app.suprsend.com/en/staging/connectors) page. Here, you'll see the list of available connectors. If you have already setup any connectors in the past, you'll see a list of existing connectors. Click on **"+New Connector"** button to add the connector. 2. Click on MySQL and add your database user’s credentials. All fields in the form are mandatory. | Field | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | A name to uniquely qualify the connection as you'll see it in the connector list on your sync task. You can add the name of database here for easy identification. | | Host | Host’s IP address or [DNS Name](https://docs.aws.amazon.com/redshift/latest/mgmt/jdbc20-obtain-url.html). | | Port | The port on which your SQL server is listening for connections. | | Database Name | Name of database you created for your cluster. | | Username | Database username created for suprsend sync. We recommend using a user with the minimal privileges here. This user only requires read permissions with access limited to the tables you want to sync from. | | Password | Database password created for suprsend sync. We recommend using a user with the minimal privileges here. This user only requires read permissions with access limited to the tables you want to sync from. | After adding the required information, click on "connect" to enable this connection. You'll see the error while connecting in case of incorrect credentials. Once your connection is setup, you'll see it in existing connector list and the connector dropdown on sync task. ## Best Practices Before you setup your database sync, you should take some measures to ensure the security of your customers’ data and limit performance impacts to your backend database. The following “best practice” suggestions can help you limit the potential for data exposure and minimize performance impacts: 1. **Sync your read-only replica instance**: Do not sync data directly from your main instance. Instead use a read-only data replica to minimize the load and avoid data loss on your main database. 2. **User connected here should have minimal privileges**: You should have a database user with minimal privileges. This person only requires read permissions with access limited to the tables you want to sync from. 3. **Sync only the data that you’ll need**: Limiting your query can improve performance, and minimize the potential to expose sensitive data. Select only the columns you need to either update user profile in SuprSend and to create list sync. 4. **Use \{\{last\_sync\_time}} to limit query results**: Make sure you use the `{{last_sync_time}}` variable in your recurring sync queries. It stores the timestamp of last successful sync in your list. Adding it in your where statement against datetime index can really speed up the query and limit the number of results returned in consecutive syncs. \{\{last\_sync\_time}} is stored in timestamp format. Use relevant cast expression to format it based on your column type. 5. **Limit your sync frequency**: Setup a sync frequency based on how frequently you want to send notifications on that list. If the previous sync is still in progress when the next interval occurs, we’ll skip the operation and catch up your data on the next interval. Skipped syncs show `Ignored` status in the logs. Frequently skipped operations may indicate that you’re syncing too often. You should monitor your first few syncs to ensure that you haven’t impacted your system’s performance. *** # Netcore Source: https://docs.suprsend.com/docs/netcore-whatsapp Guide to connect your Netcore account with SuprSend to send Whatsapp notifications. This section is a step-by-step guide to set Netcore as your WhatsApp service provider. ## Pre-Requisites You'll need a Netcore account to complete this tutorial. You can use your existing Netcore account to integrate, or [Create a Netcore account](https://netcorecloud.com/request-demo/) ## Netcore integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click WhatsApp -> Exotel from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | WhatsApp Mobile Number | Mobile number of your WhatsApp business account created on Netcore. SuprSend uses this information to send WhatsApp on your behalf via your registered WhatsApp number | | API Key | You will get this API Key from your Netcore account. SuprSend uses this API Key to send WhatsApp on your behalf via your registered Netcore account. | | Username | Username of your Netcore WhatsApp dashboard. SuprSend uses this information to upload templates on your behalf on Netcore dashboard for approval | | Password | Password of your Netcore WhatsApp dashboard. SuprSend uses this information to upload templates on your behalf on Netcore dashboard for approval | | Region | NON EU / EU. The region of your WhatsApp account on Netcore. Netcore treats EU and NON EU regions separately. | | Price per notification | This is the amount you pay per WhatsApp notification to Netcore. It helps us to calculate, estimate and optimise your cost spent on notifications. | ## How to get API Key and mobile number from Netcore On netcore, go to General Settings from left panel. And In integrate API section you'll find your API Key and WhatsApp Number. ## Configure callback URL for delivery status & incoming messages tracking One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. After saving the vendor configuration, SuprSend will generate 2 callback URL's: one to track delivery status updates of messages and another to track incoming messages. Copy the delivery report URL from SuprSend and configure it on netcore (**General settings -> event webhook**) Copy the inbound URL from SuprSend and configure it on netcore (**General Settings -> Incoming Webhook**) ## Must know: 1. Netcore DOES NOT support variables in header text (for now), please take care of this point while creating templates on SuprSend and on Netcore. 2. Netcore does not have template auto-approval process, hence, the templates need to be created both on SuprSend and on [Netcore platform](https://cpaas.netcorecloud.com/app/settings/template). 3. There can be slight delay in receiving incoming messages and delivery status update webhook response from Netcore. *** # Broadcast Source: https://docs.suprsend.com/docs/node-broadcast Trigger broadcast notifications to a list of users with NodeJS SDK. ## Pre-Requisites [Create a list of users](/docs/python-lists) ## Trigger broadcast You can trigger broadcast using `supr_client.subscriber_lists.broadcast()` method. ```javascript Request theme={"system"} const { Suprsend, SubscriberListBroadcast } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("workspace_key", "workspace_secret"); // prepare payload const broadcast_body = { list_id: "_list_id_", template: "_template_slug_", notification_category: "_", channels: [], delay: "_time_delay_", trigger_at: "_ISO_timestamp_"/"number in seconds", data: { key1: "value1", key2: "value2", }, }; const broadcast_instance = new SubscriberListBroadcast(broadcast_body, {tenant_id : "your_tenant_id", idempotency_key="__uniq_request_id__"}); // create broadcast instance const response = supr_client.subscriber_lists.broadcast(inst); // trigger broadcast response.then((res) => console.log("response", res)); ``` ```javascript Example theme={"system"} const { Suprsend, SubscriberListBroadcast } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("workspace_key", "workspace_secret"); const broadcast_body = { "list_id": "bulk_list", "template": "purchase-confirmation", "notification_category": "transactional", "channels": ["iospush","sms","inbox"], "delay": 30, "trigger_at": "2022-12-27T11:14:51.643Z", "data": { "amount": "$100", "product_name": "Medicines" "regions": ["USA", "UK"] } } const broadcast_instance = new SubscriberListBroadcast(broadcast_body); const response = supr_client.subscriber_lists.broadcast(inst); response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} { 'success': True, 'status': 'success', 'status_code': 202, 'message': 'OK' } ``` | Parameter | Description | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | list\_id | list of users that you want to send broadcast messages. | | template | It is the template slug which can be found in Templates tab in SuprSend dashboard. | | notification\_category | Preference Category to apply user preference settings while sending. Root categories- system / transactional / promotional | | channels (Optional) | User channels on which the broadcast messages to be sent. If not provided, it will trigger notifications on all available channels in user profile. Available channels: androidpush / iospush / inbox / email / whatsapp / sms Example:\["sms", "inbox"] | | delay (Optional) | Broadcast will be halted for the time mentioned in delay, and become active once the delay period is over. Example: 1d2h3m4s / 60 | | trigger\_at (Optional) | Trigger broadcast on a specific date-time. Example: "2021-08-27T20:14:51.643Z" | | data (Optional) | variable data defined in templates | ## Add file attachment (for email) To add one or more attachments to a notification (viz. Email), call `add_attachment()` on broadcast instance for each attachment file. Ensure that attachment url is valid and public, otherwise error will be raised. Since broadcast instance size can't be > 100 KB, local file paths can't be passed in event attachment. ```javascript Request theme={"system"} const broadcast_instance = new SubscriberListBroadcast(broadcast_body); broadcast_instance.add_attachment("/home/user/billing.pdf"); broadcast_instance.add_attachment("https://www.adobe.com/sample_file.pdf"); ``` 🚧 A single broadcast instance size (including attachment) must not exceed 100KB (100 x 1024 bytes). *** # Manage Users Source: https://docs.suprsend.com/docs/node-create-user-profile Create, update, & manage user profiles and communication channels using NodeJS SDK methods. ## How SuprSend identifies a User SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Create User To create a new user or to update an existing user, you’ll have to fetch user instance. Call `supr_client.user.get_instance` to instantiate user object. ```javascript Request theme={"system"} const user = supr_client.user.get_instance("_distinct_id_"); // create user instance // user methods mentioned in this docs can be attached to user instance if needed const response = user.save() // IMP: trigger request response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} { "success": boolean, "status": "success"/"fail", "status_code": http_status_code, "message": "response_message"/"error_message", } ``` ## Edit User To Edit user, you need to first fetch user instance, call all the update methods and save changes using `user.save()` method. ```javascript Request theme={"system"} // Fetch user instance const user = supr_client.users.get_edit_instance("_distinct_id_"); // Call user update methods user.set_timezone("America/Los_Angeles"); user.set("name", "John Doe"); // Save Changes const response = user.save(); // IMP: trigger request response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} { "success": true, "status": "success", "status_code": 200, "message": "User updated successfully" } ``` Here's a list of all edit methods: Use `user.add_*` method to add user channels. ```javascript Request theme={"system"} // add Email user.add_email("user@example.com") // add SMS user.add_sms("+15555555555") // add Whatsapp user.add_whatsapp("+15555555555") // add fcm push token user.add_androidpush("__android_push_fcm_token__") // add apns push token user.add_iospush("__iospush_token__") // add Slack using email user.add_slack({ email: "user@example.com", access_token: "xoxb-XXXXXXXX" }); // add Slack if slack member_id is known user.add_slack({ user_id: "U03XXXXXXXX", access_token: "xoxb-XXXXXXXX" }); // add Slack channel user.add_slack({ channel_id: "CXXXXXXXX", access_token: "xoxb-XXXXXXXX" }); // add Slack incoming webhook user.add_slack({ incoming_webhook: { url: "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }); // add MS teams user or channel using conversation_id user.add_ms_teams({ tenant_id: "c19xxxx2-9aaf-xxxx-xxxx", service_url: "https://smba.trafficmanager.net/amer", conversation_id: "19:c1524d7c-a06f-456f-8abe-xxxx" }); // add MS teams user using user_id user.add_ms_teams({ tenant_id: "c19xxxx2-9aaf-xxxx-xxxx", service_url: "https://smba.trafficmanager.net/amer", user_id: "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }); // add MS teams using incoming webhook user.add_ms_teams({ incoming_webhook: { url: "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }); ``` Use `user.remove_*` method(s) to remove user channels. These methods will detach provided value from the user profile in specified channel. ```javascript Request theme={"system"} // remove Email user.remove_email("user@example.com") // remove SMS user.remove_sms("+15555555555") // remove Whatsapp user.remove_whatsapp("+15555555555") // remove fcm push token user.remove_androidpush("__android_push_fcm_token__") // remove apns push token user.remove_iospush("__iospush_token__") // remove Slack email user.remove_slack({ email: "user@example.com", access_token: "xoxb-XXXXXXXX" }); // remove Slack if slack member_id is known user.remove_slack({ user_id: "U03XXXXXXXX", access_token: "xoxb-XXXXXXXX" }); // remove Slack channel user.remove_slack({ channel_id: "CXXXXXXXX", access_token: "xoxb-XXXXXXXX" }); // remove Slack incoming webhook user.remove_slack({ incoming_webhook: { url: "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }); // remove MS teams user or channel using conversation_id user.remove_ms_teams({ tenant_id: "c19xxxx2-9aaf-xxxx-xxxx", service_url: "https://smba.trafficmanager.net/amer", conversation_id: "19:c1524d7c-a06f-456f-8abe-xxxx" }); // remove MS teams user using user_id user.remove_ms_teams({ tenant_id: "c19xxxx2-9aaf-xxxx-xxxx", service_url: "https://smba.trafficmanager.net/amer", user_id: "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }); // remove MS teams using incoming webhook user.remove_ms_teams({ incoming_webhook: { url: "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }); ``` This method will delete/unset all values in specified channel for user (ex: remove all emails attached to user). ```javascript Request theme={"system"} user.unset("$email") user.unset(["$email", "$sms", "$whatsapp"]) // what value to pass to unset channels // for email: $email // for whatsapp: $whatsapp // for SMS: $sms // for androidpush tokens: $androidpush // for iospush tokens: $iospush // for webpush tokens: $webpush // for slack: $slack // for ms_teams: $ms_teams ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```javascript Request theme={"system"} user.set_preferred_language("en") ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```javascript Request theme={"system"} user.set_timezone("America/Los_Angeles") ``` Set is used to set the custom user property or properties. The given key and value will be assigned to the user, overwriting an existing property with the same key if present. ```javascript Request theme={"system"} user.set(key, value) user.set("name","John Doe") user.set({ key1: value1, key2: value2 }) user.set({"name": "John Doe","city": "San Francisco"}) ``` | Parameters | type | | ---------- | ------ | | key | string | | value | any | Works just like `user.set`, except it will not override already existing property values. This is useful for properties like first\_login\_date. ```javascript Request theme={"system"} user.set_once(key, value) user.set_once("first_login","2021-11-02") user.set_once({ key1: value1, key2: value2 }) user.set_once({"first_login": "2021-11-02","signup_date": "2021-11-02"}) ``` This method will append a value to the list for a given property. ```javascript Request theme={"system"} user.append(key, value) user.append("played_games", "game_1") user.append({ key1: value1, key2: value2 }) user.append({"played_games": "game_1", "liked_games": "game_2"}) ``` This method will remove a value from the list for a given property. ```javascript Request theme={"system"} user.remove(key, value) user.remove("played_games", "game_1") user.remove({ key1: value1, key2: value2 }) user.remove({"played_games": "game_1", "liked_games": "game_2"}) ``` Add the given number to an existing property on the user. If the user does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number for the value. ```javascript Request theme={"system"} user.increment(key, value) user.increment("login_count", 1) user.increment({ key1: value1, key2: value2 }) user.increment({"login_count" : 1, "order_count" : 1}) ``` | Parameters | type | | ---------- | --------------- | | key | string | | value | number (+ or -) | This will remove a property permanently from user properties. ```javascript Request theme={"system"} user.unset(key) user.unset("wishlist") user.unset([key1, key2]) user.unset(["wishlist", "cart"]) ``` | Parameters | type | | ---------- | ------ | | keys | string | After calling `add*/remove*/unset` methods, don't forget to call the `user.save()` request. the changes will be sent to SuprSend only after calling this method. ## Bulk API for updating multiple user profiles To update multiple user profiles use Bulk API. There isn't any limit on number-of-records that can be added to `bulk_users` instance. Use `append()` on `bulk_users` instance to add however-many-records to call in bulk. ```javascript Request theme={"system"} const { Suprsend } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("workspace_key", "workspace_secret"); const bulk_ins = supr_client.bulk_users.new_instance() // user1 instance const user1 = supr_client.user.get_instance("_distinct_id_1") user1.add_email("u1@example.com") user1.set_preferred_language("en") // user2 instance const user2 = supr_client.user.get_instance("_distinct_id_2") user2.remove_email("u2@example.com") //append users instance to bulk instance bulk_ins.append(user1) bulk_ins.append(user2) // OR bulk_ins.append(user1, user2) // trigger request const response = bulk_ins.save() response.then((res) => console.log("response", res)); ``` ```javascript Bulk Response theme={"system"} { status: "success/fail/partial", total: 10, success: 10, failure: 0, failed_records: [{"record": {...}, "error": "error_str", "code": ""}], warnings: [] } ``` ## Get user details ```javascript Request theme={"system"} const response = await supr_client.users.get(_distinct_id_); ``` ## Delete user ```javascript Request theme={"system"} const response = await supr_client.users.delete(_distinct_id_); ``` ## Get list of objects subscribed by user ```javascript Request theme={"system"} const response = await supr_client.users.get_objects_subscribed_to(_distinct_id_, {limit:20}); ``` You can pass optional query parameters - `limit`,`before`,`after`. ## Get lists subscribed by user ```javascript Request theme={"system"} const response = await supr_client.users.get_lists_subscribed_to(_distinct_id_, {limit:20}); ``` You can pass optional query parameters - `limit`,`before`,`after`. *** # Lists Source: https://docs.suprsend.com/docs/node-lists Manage subscriber lists with NodeJS SDK: create/update list, add/remove/replace users. The Lists SDK methods lets you create / manage list of subscribers. You can then send [broadcast](/docs/broadcast) to all the users in the list or create a workflow that triggers when a new user enters / exits list. ## Create / Update List You can use `supr_client.subscribers_list.create` method to create a new list ```javascript Request theme={"system"} const { Suprsend } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("workspace_key", "workspace_secret"); // create list API call const response = supr_client.subscriber_lists.create({ list_id: "_list_id_", list_name: "_list_name_", list_description: "_some sample descritpion for list_", }); data.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```javascript Response theme={"system"} { "list_id":"_list_id_", "list_name":"_list_name_", "list_description":"_some sample description for the list_", "list_type":"static_list", "subscribers_count":0, "source":"None", "is_readonly":false, "status":"active", "track_user_entry":false, "track_user_exit":false, "requested_for_delete":false, "created_at":"2025-03-14T13:42:45.641000Z", "updated_at":"2025-03-14T13:42:45.641000Z", "drafts":"None" } ``` Guidelines on defining the list\_id * `list_id` is case-insensitive. Suprsend first converts list\_id to lowercase before storing it or doing any sort of comparison on it. * `list_id `can be of max 64 characters. * It can contain characters \[a-z0-9\_-] that is alphanumeric characters, \_(underscore) and -(hyphen). ## Get list data You can get the latest information of a list using `supr_client.subscribers_list.get` method. ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.get("_list_id_"); ``` ```javascript Response theme={"system"} { "list_id": "list-id", "list_name": "List Name", "updated_at": "2022-12-18T10:40:27.268417+00:00", "list_description": "List description" } ``` ## Get list of lists To get the data of all the lists created in your workspace, use `supr_client.subscribers_list.get_all()`  method ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.get_all(); // default limit 20 const data = supr_client.subscriber_lists.get_all({limit:20, offset:0}); // max limit 1000 ``` ```javascript Response theme={"system"} { "meta": { "count": 14, "limit": 20, "offset": 0 }, "results": [ { "list_id": "newsletter_subscribers", "list_name": "Newsletter Subscribers", "list_description": "all users who opted in to receive product updates", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": false, "track_user_exit": false, "requested_for_delete": false, "created_at": "2025-01-14T13:42:45.641000Z", "updated_at": "2025-01-14T13:42:45.641000Z", "drafts": "None" }, { "list_id": "abandoned_application", "list_name": "Signups abandoned application", "list_description": "Users who didn’t finish application", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": true, "track_user_exit": true, "requested_for_delete": false, "created_at": "2025-02-14T13:42:45.641000Z", "updated_at": "2025-02-14T13:42:45.641000Z", "drafts": "None" } ... ] } ``` ## Add subscribers to list Use `supr_client.subscribers_list.add()` to add list subscribers. There is no limit to the number of subscribers that you can add to a list. ```javascript Request theme={"system"} const list_id = "_list_id_"; const subscribers = ["_distinct_id1_","_distinct_id2_", ..... ]; const data = supr_client.subscriber_lists.add(list_id, subscribers); ``` ```javascript Response theme={"system"} { "success":true } ``` ## Remove subscribers from list You can remove subscribers from the list using `supr_client.subscribers_list.remove()` ```javascript Request theme={"system"} const list_id = "_list_id_"; const subscribers = ["_distinct_id1_","_distinct_id2_", ..... ]; const data = supr_client.subscriber_lists.remove(list_id, subscribers); ``` ```javascript Response theme={"system"} { "success":true } ``` ## Delete list ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.delete("_list_id_"); ``` ## Replace users in the list In case you want to refresh list with a new set of users completely, you can replace users by creating a draft version of the list and updating users in it. This method will create a draft version of the list where you can add the new set of users to replace users. ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.start_sync("_list_id_"); ``` You can use this method to add subscribers to List draft version created in the Step-1. You'll get `version_id` in start sync response. ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.add_to_version("_list_id_", "01HHCTXXXXXXXXXXX", ["_user_id_1","user_id_2"]) ``` You can use this method to remove subscribers from List draft version created in Step-1. You'll get `version_id` in start sync response. ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.remove_from_version("_list_id_", "01HHCTXXXXXXXXXXX", ["_user_id_1","_user_id_2"]) ``` Once your subscribers are updated in the list, use this method to finish sync and make the draft version updated in above steps live. ```javascript Request theme={"system"} const data = supr_client.subscriber_lists.finish_sync("_list_id_", "01HHCTXXXXXXXXXXX") ``` *** ### Delete draft list You can also delete draft list if it's created by mistake. ```python Request theme={"system"} const data = supr_client.subscriber_lists.delete_version("_list_id_", "01HHCTXXXXXXXXXXX"); ``` # Send and Track Events Source: https://docs.suprsend.com/docs/node-send-event-data Learn how to send events to trigger workflows, with code snippets and examples. ## Send Event You can send event to Suprsend platform by using the `supr_client.track_event` method. When you call `supr_client.track_event`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. The actual processing/execution of event happens asynchronously. ```javascript Request theme={"system"} const { Suprsend, Event } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // dictionary containing variable/info about event, If none use {} const properties = { "key1":"value1", "key2":"value2" } const event = new Event(distinct_id, event_name, properties, {tenant_id : "_tenant_id", idempotency_key="__uniq_request_id__"}) // trigger request const response = supr_client.track_event(event) response.then((res) => console.log("response", res)); ``` ```javascript Sample theme={"system"} const { Event } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); const distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" const event_name = "product_purchased" const properties = { "first_name": "User", "spend_amount": "$10", "nested_key_example": { "nested_key1": "some_value_1", "nested_key2": { "nested_key3": "some_value_3", }, } } const event = new Event(distinct_id, event_name, properties) const response = supr_client.track_event(event) response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} { "success": boolean, "status": "success"/"fail", "status_code": http_status_code, "message": "response_message"/"error_message", } ``` | Parameter | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `distinct_id` | distinct\_id of subscriber performing the event. | | `event_name` | string identifier for the event like `product_purchased`. This should exactly match the one from created workflow. | | `properties` | dictionary representing event information. This can be used to pass variables to template. Property keys shouldn't start with `ss_` or `$`. | | `tenant_id` (Optional) | Tenant ID of the tenant. | | `idempotency_key` (Optional) | unique key in the request call for [idempotent requests](https://docs.suprsend.com/docs/node-send-event-data#idempotent-requests). | **Event naming guidelines:** Event Name or Property Name should not start with or , as we have reserved these symbols for our internal events and property names. ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an `idempotency_key`, it will skip processing requests with same `idempotency_key` for next 24 hours. You can use this key to track webhooks related to workflow notifications. To make an idempotent request, pass `idempotency_key` in the event instance. `idempotency_key` should be unique that you generate for each request. You may use any string up to 255 characters in length as an idempotency key. Ensure that you don’t add any space in start and end of the key as it will be trimmed. Here are some common approaches for assigning idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ## Trigger multiple events in bulk Use `.append()` on `bulk_events` instance to add however-many-records to call in bulk. ```javascript Request theme={"system"} const { Suprsend, Event } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // create bulk instance const bulk_ins = supr_client.bulk_events.new_instance(); // create event1 instance const event1 = new Event("distinct_id1", "event_name1", {"k1": "v1"}) // create event2 instance const event2 = new Event("distinct_id2", "event_name2", {"k2": "v2"}) // add event instance to bulk instance bulk_ins.append(event1); bulk_ins.append(event2); // OR bulk_ins.append(event1, event2); // trigger request const response = bulk_ins.trigger(); response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} { status: "success/fail/partial", total: 10, success: 10, failure: 0, failed_records: [{"record": {...}, "error": "error_str", "code": ""}], warnings: [] } ``` ## Add file attachment (for email) To add one or more attachments to a notification (viz. Email), call `add_attachment()` on event instance for each attachment file. Ensure that attachment url is valid and public, otherwise error will be raised. Since event API size can't be > 100 KB, local file paths can't be passed in event attachment. ```javascript javascript theme={"system"} const event = new Event(distinct_id, event_name, properties); event.add_attachment("/home/user/billing.pdf"); event.add_attachment("https://www.adobe.com/sample_file.pdf"); ``` A single event instance size (including attachment) must not exceed 100KB (100 x 1024 bytes). *** # Tenants Source: https://docs.suprsend.com/docs/node-tenants Learn how to create, update, fetch, & list tenants using NodeJS SDK. Tenants (previously named as brands) are used for white labeling notifications, personalizing template content or capturing admin preferences for another entity/organization. Tenants are workspace-level entities and by default, a tenant with `tenant_id="default"` (representing your organization) is created your workspace. Read more about tenants [here](/docs/tenants). ## Create / Update tenant This method will create a new tenant or update an existing tenant. ```javascript Request theme={"system"} const { Suprsend } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("workspace_key", "workspace_secret") const tenant_payload = {...} // prepare tenant datastructure payload // ---- Optional Parameters ------- const requestData = { secondary_color: "#00ff00", tertiary_color: "#0000ff", timezone: "America/New_York", // blocked channels will be skipped triggering notifications for this tenant. blocked_channels: ["email"], social_links: { website: "https://suprsend.com", facebook: "", linkedin: "", x: "", instagram: "", medium: "", discord: "", telegram: "", youtube: "", tiktok: "" }, // In-product Notification center link for capturing user preferences. embedded_preference_url: "", properties: { prop1: "value1", prop2: { prop3: ["value2"] } } }; // create or update tenant const response = supr_client.tenants.upsert(tenant_id, tenant_payload); response.then((res) => console.log("response", res)); ``` | Field | Description | | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | tenant\_id (mandatory) | max 64 characters and should contains alphanumeric characters(a-z, 0-9), hyphen (-) and underscode(\_). | | tenant\_name (mandatory) | name of the tenant. | | colors (primary, secondary, tertiary) | used while designing templates. If not provided `default` tenant colour will be picked. | | social\_links | social links to your tenant. Pass `""` if you want to remove existing link. | | properties | Custom properties associated with the tenant. Update operation is upsert (new properties are added to existing one's and if key is already present, value is overridden). | All properties of the tenant can be referred as `{{$tenant.prop}}` (handlebars) or `data["tenant"].prop` in JSONNET format. ## Get tenant ```javascript Request theme={"system"} const response = supr_client.tenants.get(tenant_id) ``` ```javascript Response theme={"system"} { "tenant_id":"tenant_id", "tenant_name":"ABC Company", "logo":"https://logo_url.png", "timezone":"America/New_York", "blocked_channels":[ "whatsapp", "email" ], "embedded_preference_url":"https://app.suprsend.com/preferences", "hosted_preference_domain":"preferences.suprsend.com", "primary_color":"#0751E8", "secondary_color":"#e308e3", "tertiary_color":"#1EE9F1", "social_links":{ "website":"https://www.suprsend.com/", "facebook":"", "linkedin":"", "x":"", "instagram":"", "medium":"", "discord":"", "telegram":"", "youtube":"", "tiktok":"" }, "properties":{ "address":"5678 Market Street Suite 300 San Francisco, CA 94103 USA" }, "updated_at":"2025-02-27T10:21:15.235666Z" } ``` ## List tenants By default, `limit=20`. The maximum value for `limit` is `1000`. ```javascript Request theme={"system"} const response = supr_client.tenants.list(); // default limit 20 const response = supr_client.tenants.list({limit:20, offset:0}); // max limit 1000 ``` ```javascript Response theme={"system"} { "meta":{ "count":28, "limit":20, "offset":0 }, "results":[ { "tenant_id":"tenant_01", "tenant_name":"ABC Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" }, { "tenant_id":"tenant_02", "tenant_name":"CDE Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" } ... ] } ``` ## Add tenant in workflow ```javascript Request theme={"system"} const wf = new Workflow(workflow_body, {tenant_id : "_tenant_id_"}) ``` ## Add tenant in event ```javascript Request theme={"system"} const event = new Event(distinct_id, event_name, properties, {tenant_id : "_tenant_id_"}) ``` *** # Trigger Workflow from API Source: https://docs.suprsend.com/docs/node-trigger-workflow-from-api Learn how to trigger workflows using direct workflow API, with code snippets and examples. It is a unified API to trigger workflow and doesn't require user creation before hand to trigger notification. Recommended for platforms transitioning their existing notifications to SuprSend. If you are using our frontend SDKs to configure notifications and passing events and user properties from third-party data platforms like Segment, then [event-based trigger](/docs/node-send-event-data) would be a better choice. 📘 Available in SDK version >= 1.10.0 ## Payload Schema Once your request is accepted, you can check the status of your request in the [SuprSend Logs](https://app.suprsend.com/en/production/logs/api?last_n_minutes=1440). ```javascript Sample Payload theme={"system"} const { Suprsend, WorkflowTriggerRequest } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true }, "recipients": [ // notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true }, // notify object { "object_type": "teams", "id": "finance", "$skip_create": true } ], "data": { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234" } }; // Create workflow instance const w1 = new WorkflowTriggerRequest(body, { tenant_id: "tenant_id1", idempotency_key: "_unique_identifier_of_the_request_" }); // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```javascript Response theme={"system"} { success: true, status: 'success', status_code: 202, message: '{"status":"success"}' } ``` To prevent automatic creation of an actor, or recipient (user/object) in SuprSend (the case where they already exist in your system), you can use the `"$skip_create": true` flag. This can be applied inside the actor, individual user recipient objects, or object recipient objects. | Property | Type | Description | | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`workflow`** | `string` | Slug of the designed workflow on the SuprSend dashboard. You'll get the slug from workflow settings. | | **`recipients`** | `array of string / array of objects` | List of users who need to be notified. You can add up to 100 recipients in a workflow trigger. You can either pass recipients as an array of `distinct_ID` (if the user is pre-synced in the SuprSend database) or [define recipient information inline](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#identifying-recipients-inline). | | **`actor`** *(Optional)* | `string / object` | Includes `distinct_id` and properties of the user who performed the action. You can use it for [cross-user notifications](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#sending-cross-user-notifications) where you need to include actor properties in the notification template. Actor properties can be added as `$actor.`. | | **`data`** | `object` | Variable data required to render dynamic template content or workflow properties such as dynamic delay or channel override in the send node. | | **[`tenant_id`](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#multi-tenant-notifications)** *(Optional)* | `string` | Trigger workflow for a specific tenant/brand. | | **[`idempotency_key`](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#idempotent-requests)** *(Optional)* | `string` | Unique identifier of the request. We'll be returning `idempotency_key` in our [outbound webhook response](https://docs.suprsend.com/docs/outbound-webhook). You can use it to map notification statuses and replies in your system. | | **`recipients[].$preferred_language`** | `string` | To set the recipient's preferred language. This is to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer to all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | | **`recipients[].$timezone`** | `string` | To set the recipient's timezone. Used to send notifications in the user's local timezone. You can pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | ### Sending notification to Multiple Users Recipients in workflow call is an array of `distinct_ids` or recipient objects. You can pass up to 100 recipients in a single workflow trigger. SuprSend will internally convert it into multiple workflow triggers, one for each recipient in the array. ```json json theme={"system"} "recipients": ["distinct_id1","distinct_id2"] ---- OR ------ "recipients": [ { "distinct_id": "id1", "$email":["id1@example.com"], "name":"recipient_1" }, { "distinct_id": "id1", "$email":["id2@example.com"], "name":"recipient_2" } ] ``` **Use lists to broadcast to a large list of users:** We recommend you to use [lists](/docs/node-lists) and [broadcast](/docs/node-broadcast) to send notifications to a user list larger than 1000 users. This approach allows bulk processing, resulting in significantly faster delivery compared to individual workflow calls. Sending individual workflows to a large set of users may introduce delays in your notification queue. ### Identifying Recipients Inline One of the benefits of using direct workflow trigger is that you can identify recipients inline. You can include recipient channel information, their channel preferences, and their user properties along with the workflow trigger. Upon triggering the workflow, the recipient will be automatically created in the SuprSend database in the background. This facilitates dynamic synchronization of your user data within SuprSend and eliminates the need for any migration efforts on your end to start sending notifications from SuprSend. You can also use recipient properties in your template as `$recipient.`. This is how the complete recipient object with look like ```json json theme={"system"} "recipients": [{ "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], // email communication channel "$sms":["+15555555555"], // sms communication channel "$channels":["email","inbox"], "$preferred_language":"en", "$timezone": "America/New_York", "custom_prop1":"value_1", // custom property "custom_prop2": "value_2", // custom property }] ``` | Property | Type | Description | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`distinct_id`** | `string` | Unique identifier of the user to be notified. | | **[`communication channels`](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#add-user-communication-channel)** (`$email`, `$sms`, etc.) | `array of string / objects` | The communication channel info provided will be updated in the user profile in the background. For this workflow, only the specified channel values for this recipient will be used for sending notifications instead of all channel values present in the user profile. | | **`$channels`** | `array of string` | By default, notifications will be sent to all channels defined in the workflow delivery nodes. However, if a user has a specific channel preference for a notification (e.g. they only want to receive payment reminders via email), you can include that preference in the workflow payload. This ensures notifications are sent only to the channels specified here. Supported channels: `email, sms, whatsapp, androidpush, iospush, slack, webpush, ms_teams`.You can always use our [in-built preference APIs](/docs/user-preferences) to maintain user notification preferences. Preferences defined within SuprSend will automatically apply with workflow triggers. | | **`$preferred_language`** | `string` | Sets the recipient's preferred language to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer to all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | | **`$timezone`** | `string` | Sets the recipient's timezone to send notifications in the user’s local timezone. You can pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | **`*`** | `object` | You can pass other user properties to render dynamic template content. These properties will also be set in the user profile and can be used in the template as `$recipient.`. | #### Add user communication channel ```json json theme={"system"} "$email":["user@example.com"], "$whatsapp":["+15555555555"], "$sms":["+15555555555"], "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], // slack using email "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }] // slack using member_id "$slack": [{ "user_id": "U/WXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack channel "$slack": [{ "channel": "CXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack incoming webhook "$slack": [{ "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }] // MS teams user or channel using conversation_id "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }] // MS teams user using user_id "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }] // MS teams incoming webhook "$ms_teams": [{ "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }] ``` ### Sending cross-user notifications In scenarios where you need to notify a group of users based on another user's action, such as sending a notification to the document owner when someone comments on it, you can specify the actor in your workflow call. This allows you to use actor's name or other properties in your notification template. Actor properties can be included in the template as `$actor.`. ```json text theme={"system"} //handlebar template Hi {{$recipient.name}}, {{$actor.name}} added {{length comments}} new comments on the {{doc_name}}. // rendered content with Sample Data Hi recipient_1, actor_1 added 2 new comments on the annual IT report. ``` ```javascript Sample Workflow Body theme={"system"} { "workflow": "new_comment", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name":"actor_1" }, "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "doc_name": "annual IT report", "date": "2024-01-01", "comments":["change the date","rest looks good"] } } ``` ### Sending notification to anonymous user You can send notifications to anonymous users by passing `"is_transient": true` in your recipient object. This approach is recommended for scenarios where you need to send notifications to unregistered users without creating them in the SuprSend platform. The same way, you can pass ` "is_transient": true` in your actor object to use actor properties in template without creating user profile. ```javascript Request theme={"system"} const workflow_payload = { "workflow": "_workflow_slug_", "actor": { "is_transient": true, // for anonymous actor "name":"actor_1" }, "recipients": [ { "is_transient": true, // for anonymous recipient "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ``` ### Multi-tenant notifications For cases where you want to send notifications to your enterprise customers end users, pass the `tenant_id` in your workflow instance. You can use this to dynamically manage [tenant level notification customizations](/docs/tenants). This includes the ability to customize template design or content and route notifications via tenant vendors. ```javascript Request theme={"system"} const workflow = new WorkflowTriggerRequest(workflow_body, {tenant_id: "tenant_id1"}) ``` ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. Idempotency key should be uniquely generated for each request (max 255 characters allowed). Spaces in start and end of the key will be trimmed. Here are some common approaches for generating idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ```javascript javascript theme={"system"} const workflow = new WorkflowTriggerRequest(workflow_body, {idempotency_key: "_unique_request_identifier"}) ``` ## Bulk API for triggering multiple workflows Bulk API allows you to send multiple workflow requests in a single call. Use `.append()` and `workflows.bulk_trigger_instance()` to add however-many-records to call in bulk. ```javascript Request theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // workflow1 instance const wf1 = new WorkflowTriggerRequest(wf_body1, {tenant_id: "tenant_id1", idempotency_key: "_unique_identifier_of_the_request_"}) // workflow2 instance const wf2 = new WorkflowTriggerRequest(body2) // create bulk instance const bulk_ins = supr_client.workflows.bulk_trigger_instance() // add workflows instances to bulk instance bulk_ins.append(wf1,wf2) // trigger workflows const response = bulk_ins.trigger(); response.then(res => console.log("response", res)); ``` ```javascript Response theme={"system"} { status: "success/fail/partial", total: 10, success: 10, failure: 0, failed_records: [{"record": {...}, "error": "error_str", "code": ""}], warnings: [] } ``` ## Add attachment (in email) To add one or more attachments to a notification (viz. Email), call `wf_instance.add_attachment()` for each file with local path or remote attachment url. Ensure that file path is valid, and public(for remote url) otherwise it will raise error. ```javascript Request theme={"system"} const wf_instance = new WorkflowTriggerRequest(wf_body); wf_instance.add_attachment("/home/user/billing.pdf"); wf_instance.add_attachment("https://www.adobe.com/sample_file.pdf"); ``` A single workflow instance size (including attachment) must not exceed 800KB (800 x 1024 bytes). *** ## Dynamic workflow trigger You can trigger workflow from node SDK by using `supr_client.trigger_workflow` method. Import *Workflow class* before calling this method. \ Once your request is accepted, you can check the status of your request in the [ SuprSend Logs](https://app.suprsend.com/en/staging/logs) section. ```javascript Request theme={"system"} const { Workflow } = require("@suprsend/node-sdk") // Prepare Workflow body const workflow_body = { "name": "workflow_name", "template": "template_slug", "notification_category": "notification_category", //notification category transactional/promotional/system "delay": "time_delay", // time delay after which the first notification will be sent "trigger_at": "date string in ISO 8601", // to trigger scheduled notifications "users": [ { "distinct_id": "distinct_id", // unique identifier of the user // if $channels is present, communication will be triggered on mentioned channels only. // "$channels": ["email"], // User communication channel can be added as [optional]: // "$email":["user@example.com"], // "$whatsapp":["+15555555555"], // "$sms":["+15555555555"], // "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], // "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], // "$slack": { //"email": "e@example.com", //"access_token": "xoxb-XXXXXXXX" //} --- slack using email // "$slack": { // "user_id": "U/WXXXXXXXX", // "access_token": "xoxb-XXXXXX" //} --- slack using member_id //"$slack": { //"channel": "CXXXXXXXX", //"access_token": "xoxb-XXXXXX" //} --- slack channel //"$slack": { //"incoming_webhook": { //"url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" //} //} --- slack incoming webhook // "$ms_teams": { //"tenant_id": "c1981ab2-9aaf-xxxx-xxxx", //"service_url": "https://smba.trafficmanager.net/amer", //"conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" //} --- MS teams user or channel using conversation_id // "$ms_teams": { //"tenant_id": "c1981ab2-9aaf-xxxx-xxxx", //"service_url": "https://smba.trafficmanager.net/amer", //"user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" //} --- MS teams user using user_id // "$ms_teams": { // "incoming_webhook": { // "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" // } //} --- MS teams incoming webhook } ], // delivery instruction [optional]. how should notifications be sent, and whats the success metric "delivery": { "smart": , "success": "success_metric", "time_to_live": "TTL duration", // will be applicable for smart = TRUE "mandatory_channels": [] // list of mandatory channels e.g ["email"], will be applicable for smart = TRUE }, // data can be any json "data": { "key":"value", "nested_key": { "nested_key1": "some_value_1", "nested_key2": { "nested_key3": "some_value_3", }, } } } const wf = new Workflow(workflow_body, {tenant_id="default", idempotency_key="__uniq_request_id__"}) // Trigger workflow const response = supr_client.trigger_workflow(wf); // returns promise response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} // Response structure { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "Accepted", } { "success": false, // error will be present in message "status": "fail", "status": 400, // http status code "message": "error message", } ``` For configuring a workflow from backend, you can pass following properties in your method | Parameter | Description | Format | Obligation | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | **`name`** | It is the unique name of the workflow. You can see workflow-related analytics on the workflow page (how many notifications were sent, delivered, clicked, or interacted). The workflow name should be easily identifiable for your reference at a later stage. | *text* | *Mandatory* | | **`template`** | It is the unique slug name of the template created on the SuprSend platform. You can get this slug name by clicking on the clipboard icon next to the Template name on the SuprSend templates page. It is the same for all channels. | **`slug name`** | *Mandatory* | | **`notification_category`** | You can understand more about them in the [Notification Category](https://docs.suprsend.com/docs/notification-category) documentation. | `system` / `transactional` / `promotional` | *Mandatory* | | **`delay`** | Workflow will be halted for the time mentioned in delay and become active once the delay period is over. | `XXdXXhXXmXXs` or if it's a number (n), then delay is in seconds (n). | *Optional* | | **`trigger_at`** | Trigger workflow on a specific date-time. | Date string in ISO 8601 format e.g. `"2021-08-27T20:14:51.643Z"` | *Optional* | | **`users`** | Array object of target users. At least 1 user is mandatory.`distinct_id` for each user is mandatory. Channel information is non-mandatory (If you have already sent channel information via backend or Frontend SDK (on app or website)).Notification will be sent only to the channels defined in workflow if channel information is added. | `json"users": [ { "distinct_id": "value", "$channels": [], "channel_information_dict" #(optional) }]` | *Mandatory* | | **`delivery`** | Delivery instructions for the workflow. You can set [Smart Delivery](/docs/smart-delivery#smart-channel-routing) preference by setting `"smart":true`.By default, delivery instruction will be: `json"delivery": { "smart": false, "success": "seen"}` | `jsondelivery: { "smart": True/False, "success": "delivered/seen/interaction/", "time_to_live": "", "mandatory_channels": [] # list of mandatory channels e.g "notification",}` | *Optional* | | **`data`** | JSON object. To replace the variables in the template. Templates use [`handlebars.js`](https://handlebarsjs.com/guide/) language. | `json"data": { "key": { "key": "value", "key": "value" }}` | *Optional* | | **`brand_id`** | `Brand_id` of the tenant to trigger notifications on behalf of your tenants. | *string* | *Optional* | | **`idempotency_key`** | Unique key in the request call for [idempotent requests](https://docs.suprsend.com/docs/node-trigger-workflow-from-api#idempotent-requests). | *string* | *Optional* | For setting `$sms` and `$whatsapp`, `+` is mandatory to send along with phone number. Eg: +1 for US #### Bulk API for triggering multiple workflows Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.append()` on `bulk_workflows` instance to add however-many-records to call in bulk. ```javascript Request theme={"system"} const { Workflow } = require("@suprsend/node-sdk") const bulk_ins = supr_client.bulk_workflows.new_instance() // one or more workflow instances const workflow1 = new Workflow({...}) // body must be a proper workflow request json/dict const workflow2 = new Workflow({...}) // body must be a proper workflow request json/dict // --- use .append on bulk instance to add one or more records bulk_ins.append(workflow1) bulk_ins.append(workflow2) // OR bulk_ins.append(workflow1, workflow2) // ------- const response = bulk_ins.trigger() response.then((res) => console.log("response", res)); ``` ```javascript Response theme={"system"} // Response structure { status: "success", total: 10, // number of records sent in bulk success: 10, // number of records succeeded failure: 0, // number of records failed failed_records: [], warnings: [] } { status: "fail", total: 10, // number of records sent in bulk success: 0, // number of records succeeded failure: 10, // number of records failed failed_records: [{"record": {...}, "error": "error_str", "code": 500}], warnings: [] } { status: "partial", total: 10, // number of records sent in bulk success: 6, // number of records succeeded failure: 4, // number of records failed failed_records: [{"record": {...}, "error": "error_str", "code": 500}], warnings: [] } ``` *** # Notification Category Source: https://docs.suprsend.com/docs/notification-category Learn what notification categories mean and how to set it up for capturing user-preferences. Notification Categories are the category your workflow belongs to. It is used for following purposes: * **Defining notification latency**: We maintain separate notification queues for different notification category to ensure that marketing and promotional notifications doesn't delay system or time-sensitive notifications. * [Setting category-level preferences](/docs/user-preferences) **per-user and tenant**: You can create sub-categories for different type of notifications and give your users option to set their notification preferences at category level. * **Setting category-level rules or limits**: e.g., you can set a rule that all notifications in promotional category should only be sent between 10 AM - 5 PM or the max notifications in the category should not exceed 100k per day. ## Top-level categories SuprSend has 3 broad notification categories - **System** , **Transactional** and **Promotional**. Notifications rules and latency settings are defined at these top level categories. You can create further [sub-categories](docs/notification-category#creating-sub-categories-for-preference-management) in each of these categories to power [user preferences](/docs/user-preferences). ### System System notifications are the ones that are essential for your users to use your website and can't be unsubscribed by the users. These notifications are considered most time-sensitive and given the highest priority in notification send queues. Some examples are - OTP, Forgot Password and Verification emails. ### Transactional Transactional notifications are the ones that are sent in response to some user transaction or action on your platform. By default, users are subscribed to transactional notifications and not allowed to unsubscribe as they have initiated them at the first place. However, some of the transactional notifications are not so critical and can be unsubscribed as per user's wish (e.g. Post likes/ comments). You can set which transactional notifications are critical and should not be allowed to unsubscribe by setting the default preference as `cant unsubscribe`. These notifications are considered second in priority in notification queues after system notifications. Some examples are - Delivery updates, Payment Confirmation, booking confirmation, Post shared / liked, Balance alerts, account update alert, and reminders. As a company, you should make sure that you use strategies like [batching](/docs/batch) & [channel routing](/docs/smart-delivery), so that users do not feel bombarded with transactional notifications. Also, you can cleverly use transactional communications to up-sell, cross-sell, collect feedback, or bring your user back to your platform. Channel providers do not block transactional communications generally, and consider your users to be implicitly agreeing to these notifications, hence transactional communications should be used judiciously. 📘 Sending notifications that are not related to user transaction but are very personal to user's profile and interests, like recommended products, cart abandoned, etc could be inferred as transactional, depending on indicators like whether user is subscribed to receive them, or the way content is designed. ### Promotional Promotional notifications are the ones that are sent out to users to increase sales, promote products, or bring users back to your platform without user doing any interaction in the first place. These communications are generally sent in bulk, consuming a significant portion of the communication pipeline. That's why they are given the least priority in notification queue. You should always send your bulk messages and broadcasts in promotional category so that it doesn't impact the latency of your system and transactional notifications. Some examples are: Newsletters, announcements, product updates, upcoming sale or event, deals and discounts, or price drop alerts. Some channel providers often require explicit users permission to send these communications. Other channel providers could block or tag these communication as promotional marketing. ## Creating sub-categories for preference management You can create notification categories from [Developers -> Notification categories](https://app.suprsend.com/en/staging/developers/preference-categories) page. All sub-categories are created in the 3 [top level categories](/docs/notification-category#top-level-categories) mentioned above. Notification rules and latency priority are inherited from the top level category. [Preference](/docs/user-preferences) settings are defined at each sub-category level. Top level categories are not shown on the preference page. Every category can have sections and sub-categories. User preferences are controlled at the sub-category level. * **Sub-category** is what end users will see on their preference page. * **Sections** are used to group related sub-categories on the preference page, facilitating easier navigation and organization on the preference page. For instance, in the below example, `Quote Approval` and `Forecast` are sub-categories and `Quotes` is the section in which these sub-categories are grouped. ### Updating section You can update section in any category by clicking on the `+Section` button in each category Add name and description for the category and `Save Changes`. You'll see the updated section inside the category and edit it later by clicking on the `Edit` button against that section. ### Updating sub-category Sub-Category default preferences vary based on the category that you are adding in. * System category default preference is set to `Can't Unsubscribe` with `All` user channels set as mandatory. Users will not be allowed to update preferences in this category as system notifications are critical notifications and should always be sent to the user. * Transactional category default preference is set to `Can't Unsubscribe`. We recommend selecting at least one mandatory channel for the transactional category since users should receive notifications on at least one channel corresponding to a transaction. However, you are free to set any default preference in this category. * Promotional category default preference is set to `ON`. This means that the user will receive notification in this category by default. You can't set this category to `Can't Unsubscribe` as users should always be able to opt-out of marketing-related notifications. You can update sub-category in any category by clicking on the `+Sub-Category` button in that category. | Field | Obligation | Description | | ------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | *Mandatory* | Name of the sub-category to be shown on the user preference page | | Section | *Optional* | Section in which this sub-category will be grouped into | | Description | *Optional* | Description of the sub-category to be shown on the user preference page | | Default Preference | *Mandatory* | Default preference settings for the sub-category. Notifications will be sent to the user based on this preference until the user overrides it - `On` means the user will get notifications on all channels in this sub-category until user opts out from this category or any channel in this category - `Off` means the user will not get notification on all channels in this sub-category until user opts into this category or any channel in this category - `Can't Unsubscribe` means the user can't completely opt-out from this category. Notifications will always be sent on mandatory channels in this category. | | Mandatory Channels | *Optional* | Mandatory channels from which user `Can't Unsubscribe`. The default selection is `All` but you can change it to `Selected Channels only` to make specific channels mandatory. | Click on "Save Changes" to save the sub-category. You'll see the updated sub-category inside the section and edit it later by clicking on the pencil icon against that category. **Avoid Changing sub-category name:** Changing category name updates the category slug. So, make sure to not change sub-category name once you have configured it in your workflows or if it is absolutely necessary to change the name, change it in all backend and front workflows otherwise the workflows will start failing. ### Preview changes Click on **Preview** to see a reference view of ready-made preference center for your end user. Please note that this view is just for reference of how the categories and sections will be placed in end user's view. Actual view will be based on the preference page design in your application. ### Publish changes All the changes done are saved in draft version. You can publish the changes to make it live for your end users. You'll be able to add the updated categories in workflow call only after publishing the changes. Click on `Publish Changes` button to publish the changes. ### Clone categories to production workspace You can test preferences in your staging application and use `Clone` button to clone the changes to production workspace. ## Triggering notification for a sub-category You can add the sub-category slug in `notification_category` field in your backend workflow call or select the category from dropdown in workflows created through SuprSend dashboard. You can copy sub-category slug by clicking on sub-category name on the category settings page *** # Object Subscriptions Source: https://docs.suprsend.com/docs/object-subscriptions Learn how to use subscriptions to notify a list of recipients associated with an object. Subscriptions are an extension to [Objects](/docs/objects) and used to link [recipient/user](/docs/users) with the object. You can use subscriptions to: * Maintain relationship for a group of users and reuse them in all triggers, without having to compute recipient list in individual workflow triggers. * Notify all users subscribed to a topic or event when it occurs, with users able to subscribe and unsubscribe as needed. Eg. Notify all users subscribed to an event when the event starts. * SaaS applications managing notifications for end-users where recipient relationships are coming from an system and event triggers or notification calls are coming from an external systems like CRM or SAP, and the trigger doesn't have information of the users who are subscriber to that trigger. Objects can have one or more subscribers, and triggering a workflow on an object will automatically propagate notifications to all subscribers, without you needing to maintain this subscription in your database. ## Managing subscriptions ### Add subscription You can add subscribers by passing an array of existing users or identifying them inline, which will automatically create user profiles in the background. The subscription is an upsert operation, meaning it adds new subscribers to the object but overwrites existing subscriber properties if passed with updated values. You can subscribe up to 100 subscribers to an object in one go, with no cap on the total number of subscribers an object can have. [Create/Update Subscription API ->](/reference/add-object-subscription) ```curl Adding existing users theme={"system"} curl --request POST \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": ["id1","id2"], "properties": {} }' ``` ```javascript NodeJs theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.create_subscriptions("_object_type_", "_object_id_", { recipients: ["id1","id2"], properties: {} }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": ["id1","id2"], "properties": {} } obj = supr_client.objects.create_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` ```curl Identifying subscribers inline theme={"system"} curl --request POST \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": [ { "distinct_id": "id1", "$email": ["abc@example.com"], "name": "recipient_name" }, { "object_type": "teams", "id": "devs" } ], "properties": {} }' ``` ```javascript NodeJs theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.create_subscriptions("_object_type_", "_object_id_", { recipients: [ { "distinct_id": "id1", "$email": ["abc@example.com"], "name": "recipient_name" }, { "object_type": "teams", "id": "devs" } ], properties: {} }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": [ { "distinct_id": "id1", "$email": ["abc@example.com"], "name": "recipient_name" }, { "object_type": "teams", "id": "devs" } ], "properties": {} } obj = supr_client.objects.create_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` ### Subscription properties Subscription properties define the specifics of the relationship between a subscriber and an object. During a workflow execution, these properties can be accessed under the `recipient.subscription` namespace as `recipient.subscription.property_key`. Properties can be assigned when creating a subscription, and since creating a subscription is an upsert operation, any properties passed will overwrite existing values in subsequent calls for the same user-object relationship. ### Adding nested object hierarchies It's possible to model nested subscription hierarchies by associating child objects as subscribers of a parent object. This allows you to create structures like "matches" having many "players" which have their own "followers"(users). ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": [ { "object_type": "players", "id": "player1", "name":"Player 1" } ] }' ``` ```javascript NodeJs theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.create_subscriptions("_object_type_", "_object_id_", { recipients: [ { "object_type": "players", "id": "player1", "name": "Player 1" } ] }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": [ { "object_type": "players", "id": "player1", "name": "Player 1" } ] } obj = supr_client.objects.create_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` Once you've established a nested hierarchy like the above, it's also possible to notify all child subscribers from a parent object. In the example above, that means we could notify all followers of a player by setting the recipient of the trigger to be match. 📘 **Note**: currently we only support subscriptions at a maximum depth of 2, meaning you can model a hierarchy such as parent -> child -> user but no deeper. If you need to support a deeper nesting, please [get in touch](mailto:support@suprsend.com). ### Object subscription fan out in trigger In nested object hierarchies, a workflow trigger fans out up to two levels by default. This means notifications are sent to Object channels (e.g. team email, Slack), Object subscribers and Subscribers of child objects. You can however change the fanout hierarchy by passing `$object_subscriptions_query -> depth` in the recipient JSON. * `depth: 0`  would mean sending notification to object channels and not its subscribers. * `depth: 1`  would mean sending notification to object channels and its subscribers. * `depth: 2`  (maximum) → Expands up to two levels of subscription. ` teams -> team members>`. If you need to support a deeper nesting, kindly [get in touch](mailto:support@suprsend.com) with our team. ```json theme={"system"} "recipients": [ { "object_type": "teams", "id":"finance", //optional parameter to define subscription fan-out depth in workflows "$object_subscriptions_query": { "depth": 0 } } ] ``` ### Remove subscription To remove one or more subscribers from an object, you can pass a list of subscriber distinct\_ids or object\_type and id for child objects. Similar to subscription addition, you can remove up to 100 subscribers in one go. [Delete Subscription API ->](/reference/delete-object-subscription) ```curl Remove Users theme={"system"} curl --request DELETE \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": ["id1","id2","id3"] }' ``` ```javascript NodeJs theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.delete_subscriptions("_object_type_", "_object_id_", { recipients: ["id1","id2","id3"] }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": ["id1","id2","id3"] } obj = supr_client.objects.delete_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` ```curl Remove Child Objects theme={"system"} curl --request DELETE \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": [ { "object_type": "players", "id": "player1" }, { "object_type": "players", "id": "player2" } ] }' ``` ```javascript NodeJs theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.delete_subscriptions("_object_type_", "_object_id_", { recipients: [ { "object_type": "players", "id": "player1" }, { "object_type": "players", "id": "player2" } ] }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": [ { "object_type": "players", "id": "player1" }, { "object_type": "players", "id": "player2" } ] } obj = supr_client.objects.delete_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` ### Fetch subscriptions You can fetch a cursor pointer based paginated list of object subscriptions. [Get object subscription API ->](/reference/list-object-subscriptions) ```json json theme={"system"} { "meta": { "count": 20, "limit": 10, "has_prev": false, "before": null, "has_next": true, "after": "01JAWC72RCY8FRE06SABJSQ0ZP" }, "results": [ { "properties": {...}, "user": null, "object": { "id": "devs", "object_type": "teams", "subscriptions_count": 0, "updated_at": "2024-10-23T10:14:25.082642+00:00" }, "created_at": "2024-10-23T10:14:25.083588+00:00", "updated_at": "2024-10-23T10:14:25.083588+00:00" }, { "properties": { "role": "manager" }, "user": { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "updated_at": "2023-12-26T23:35:17.97078+00:00" }, "object": null, "created_at": "2024-10-23T10:13:51.900851+00:00", "updated_at": "2024-10-23T10:13:51.900851+00:00" }, ... ] } ``` ## Trigger notification to object subscribers When you trigger notification on an object. It automatically fans out and send notifications to all its subscribers. If a child object is subscribed to the parent object, the notification will propagate up to two levels down the hierarchy, notifying both the direct subscribers of the parent and the subscribers of the child object. You can just pass object type and id as recipient in workflow trigger to send notification to object subscribers. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product" } ], "data": { "key": { "k1": "v1", "k2": "v2" } } }' ``` ```javascript NodeJs theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") wf_trigger_req = new WorkflowTriggerRequest( body = { "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product" } ], "data": { "key": { "k1": "v1", "k2": "v2" } } } ) res = supr_client.workflows.trigger(wf_trigger_req); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Workflow, WorkflowTriggerRequest, Suprsend supr_client = Suprsend("", "", debug=True) w = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product" } ], "data": { "key": { "k1": "v1", "k2": "v2" } } } ) res = supr_client.workflows.trigger(w) print(res) ``` 📘 **Deduplication of recipients** Recipients are deduplicated when executing a notification fan out, which will ensure your notification workflow is executed only once for each unique recipient. *** # Manage Objects Source: https://docs.suprsend.com/docs/objects How to model and manage non-user entities (teams/projects) with Objects. Objects in SuprSend represent non-user entities such as organizations, teams, roles, and projects. You can leverage objects to: * **Send Group Notifications**: Direct notifications to group channels like team emails, Slack channels, or a shared Inbox feed. * **Notify Object Subscribers**: Send notifications to all users subscribed to an object without passing individual users in the trigger. For instance, if you need to send invoicing alerts to all finance team members, you can create a “Finance” object and add team members as subscribers. Triggering the notification for the finance object then automatically sends it to all subscribed finance team members. Each object can have unique channels, preferences, and properties, which are used to send notification to object as a recipient. Every notification trigger is sent to individually to object + its subscribers. Objects are advanced way of modelling user triggers in SuprSend. If you are new to SuprSend, we recommend you to try out basic workflow trigger with [user](/docs/users) first before jumping into object implementation. ### How object trigger works? When you trigger notification on an object, It fans out and send notifications to object and all its subscribers. The object acts as an independent recipient of the notification, so if it has two subscribers, the notification will be sent to: * The object as a recipient (channels, properties and preferences defined at object are used in this workflow). * Its 2 subscribers. Importantly, object settings (properties, channels, and preferences) apply when sending to object as a recipient; these settings are not applied when sending to individual object subscribers. ### Creating objects in SuprSend Each object in SuprSend is organized by a `type`, grouping similar objects together. Objects are uniquely identified by an `{id, object_type}` pair in all API calls. Follow these conventions when defining object types and IDs: * **Object Type**: Use plural names where possible to reflect a collection of similar objects, such as teams, projects, or customers. * **Object ID**: Choose a stable, unique identifier, ideally the primary key from your system. This ensures easy reference, as object IDs are immutable once created. Objects largely follows the same schema and profile update methods as user. You can create objects in a separate API call or inline within workflow trigger. [Object creation API ->](/reference/create-update-objects) This is an ideal approach for updating objects especially if you are using object to send notification to its subscribers. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/v1/object/:object_type/:id/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{ "name": "__Object_name__", "prop1": "value1", "nested_props": { "prop2": "value2", }, "$email": ["object_group@example.com"], "$timezone": "UTC", "$preferred_language": "en" }' ``` ```javascript Node theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") // upsert object const object_properties = { "name": "__Object_name__", "prop1": "value1", "nested_props": { "prop2": "value2", }, "$email": ["object_group@example.com"], "$timezone": "UTC", "$preferred_language": "en" } res = supr_client.objects.upsert("__object_type__", "__object_id__", object_properties); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") properties = { "name": "__Object_name__", "prop1": "value1", "nested_props": { "prop2": "value2", }, "$email": ["object_group@example.com"], "$timezone": "UTC", "$preferred_language": "en" } obj = supr_client.objects.upsert("_object_type_", "__object_id__", properties) print(obj) ``` Objects are generally defined inline if you are sending notification to object as a recipient and need to add its channels or properties in the trigger. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 }, { "object_type": "teams", "id": "devs", "total_members": 20 } ], "data": { "key": { "k1": "v1", "k2": "v2" } } } ' ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") wf_trigger_req = new WorkflowTriggerRequest( body = { "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 }, { "object_type": "teams", "id": "devs", "total_members": 20 } ], "data": { "key": { "k1": "v1", "k2": "v2" } } } ) res = supr_client.workflows.trigger(wf_trigger_req); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Workflow, WorkflowTriggerRequest, Suprsend supr_client = suprsend.Suprsend("", "", debug=True) w = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 }, { "object_type": "teams", "id": "devs", "total_members": 20 } ], "data": { "key": { "k1": "v1", "k2": "v2" } } } ) res = supr_client.workflows.trigger(w) print(res) ``` ### ## Object subscribers You can add users or other objects as object subscribers. When a workflow is triggered on the object, SuprSend will automatically fan out and run a workflow for each subscriber associated with that object. [Learn more about subscription here](/docs/object-subscriptions). **Nested object hierarchies**: Object subscriptions support hierarchical structures, allowing one object to subscribe to another. e.g., if you create a `customer` object with associated `team` objects subscriber to `customer` object and `team members` are subscribed to their respective `team` objects. Now, when you trigger workflow on the customer object as recipient, it will propagate through this hierarchy and all teams and their team members will be notified. SuprSend allows inline creation of users or objects when subscribing them to an object, so there's no need to pre-create them in the system. You can add subscription properties to define the relationship of subscriber to the object and can be referenced as variable in template or workflow under `recipient.subscription` namespace. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/v1/object/:object_type/:object_id/subscription/ \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "role": "CEO" }, { "object_type": "teams", "id": "product" }, { "object_type": "teams", "id": "devs" } ], "properties": { "team_location":"Austin, Texas" } }' ``` ```javascript Node theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") res = supr_client.objects.create_subscriptions("_object_type_", "_object_id_", { recipients: [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "role": "CEO" }, { "object_type": "teams", "id": "product" }, { "object_type": "teams", "id": "devs" } ], properties: { "team_location":"Austin, Texas" } }); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```python Python theme={"system"} from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("", ">") subscription_payload = { "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "role": "CEO" }, { "object_type": "teams", "id": "product" }, { "object_type": "teams", "id": "devs" } ], "properties": { "team_location": "Austin, Texas" } } obj = supr_client.objects.create_subscriptions("_object_type_", "__object_id__", subscription_payload) print(obj) ``` ## Trigger workflow You can trigger workflow on the created object by passing object\_type and id as recipient in [API based trigger](/docs/trigger-workflow#triggering-workflow-via-api). Event trigger doesn't support triggering on objects. ```curl curl theme={"system"} curl --location 'https://hub.suprsend.com/trigger/' \ --header 'Authorization: Bearer ' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{ "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 } ], "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1" }, "data": { "key": { "k1": "v1", "k2": "v2" } } }' ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("__workspace_uid__", "__worksapce_secret__") wf_trigger_req = new WorkflowTriggerRequest( body = { "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 } ], "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1" }, "data": { "key": { "k1": "v1", "k2": "v2" } } } ) ``` ```python Python theme={"system"} from suprsend import Workflow, WorkflowTriggerRequest, Suprsend supr_client = Suprsend("", "", debug=True) w = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "object_type": "teams", "id": "product", "total_members": 4 } ], "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1" }, "data": { "key": { "k1": "v1", "k2": "v2" } } } ) res = supr_client.workflows.trigger(w) print(res) ``` ## Update object profile and preferences You can update channels, properties, and preferences for an object in the same way as for a user. These updates apply when sending notifications to the object itself as the recipient. Object profiles can be modified in the SuprSend UI for testing purposes, or programmatically through the object [upsert API](/reference/create-update-objects) or [edit operations API](/reference/edit-object-profile). Updating object preferences follows the same approach as updating user preferences: in the API call, you pass the identifier as `:object_type/:id` instead of `:distinct_id`. [Refer all object APIs](/reference/create-update-objects) *** # Objects Source: https://docs.suprsend.com/docs/objects-node-sdk Create, update, & manage objects and their subscriptions using NodeJS SDK. Objects in SuprSend represent non-user entities such as organizations, teams , roles, and projects. Understand more about objects from our [objects documentation](/docs/objects) ## Upsert (create/update) an object Object updating is an upsert function, meaning it would always override existing key values on further updates. Object id and type is mandatory to create object. You can optionally pass object properties (to use in template or workflow condition) or channel information (send notification on object channels) in the payload. ```javascript Request theme={"system"} const {Suprsend} = require("@suprsend/node-sdk"); // Initialize SDK const supr_client = new Suprsend("Workspace_Key", "Workspace_Secret") res = supr_client.objects.upsert("object_type", "object_id"); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```javascript Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "key1": "val1" }, "created_at":"2025-01-20T19:21:42.030343+00:00", "updated_at":"2025-02-14T14:06:05.928675+00:00", "$inbox":[ { "value":"H4iGeGSHyXXXXXXSeuBb_PJGXXXgWu_LsXXXXXyiw", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ], "$email":[ { "value":"abc@example.com", "status":"active", "perma_status":"active" } ] } ``` ## Edit an object There are 2 ways in which you can edit an object data. * Build edit payload yourself * Use helper methods provided by SDK (Recommended) ### 1. Build edit payload yourself Use this to modify an object, typically for removing channels or unsetting properties. The payload will follow the same structure as the [Object Edit](https://docs.suprsend.com/reference/edit-object-profile) API. ```javascript Request theme={"system"} //for all supported operations, refer API documentation const payload = {operations: [{$unset: ["name"]}, {$remove: {"$email": "abc@example.com"}}]} const response = await supr_client.objects.edit(object_type, object_id, payload}; ``` ```javascript Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "name":"John Doe" }, "created_at":"2025-03-14T15:31:59.151061+00:00", "updated_at":"2025-03-14T15:31:59.182616+00:00", "$inbox":[ { "value":"75Ev_m8yln0N_i1iXXXXXXr2eNOE_4ROXXXXXX1TgTc", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` ### 2. Edit using helper methods \[Recommended] It is possible to use the SDK's helper methods to perform edit operations on an object. For this, first create object instance then call any of the helper methods mentioned below and finally save the changes. ```javascript Request theme={"system"} // create instance const object_instance = supr_client.objects.get_instance(object_type, object_id); // call any helper methods object_instance.set(key, value); object_instance.add_email("johndoe@gmail.com"); // save the changes const response = await supr_client.objects.edit(object_instance); ``` ```javascript Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "$preferred_language":"en", "$timezone":"America/Los_Angeles", "company_name":"ABC company" }, "created_at":"2025-03-04T08:34:58.061696+00:00", "updated_at":"2025-03-14T14:40:21.467156+00:00", "$inbox":[ { "value":"dHvPs6pmUXXXXXXTAsfM2yroXXXX2CXZOtjXXXXXX8", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` Here's a list of all edit helper methods: Use `object_ins.add_*` method(s) to add user channels in a profile ```javascript Request theme={"system"} // add Email object_instance.add_email("object_ins@example.com"); // add Email object_instance.add_sms("+15555555555"); // add Whatsapp object_instance.add_whatsapp("+15555555555"); // add fcm push token object_instance.add_androidpush("__android_push_fcm_token__"); // add apns push token object_instance.add_iospush("__iospush_token__"); // add Slack using email object_instance.add_slack( { "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }); // add Slack if slack member_id is known object_instance.add_slack( { "user_id": "U03XXXXXXXX", "access_token": "xoxb-XXXXXXXX" }); // add Slack channel object_instance.add_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-XXXXXXXX" }); // add Slack incoming webhook object_instance.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }); // add MS teams user or channel using conversation_id object_instance.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }); // add MS teams user using user_id object_instance.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "object_ins_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }); // add MS teams using incoming webhook object_instance.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }); ``` Use `object_ins.remove_*` method(s) to remove channels from an object profile ```javascript Request theme={"system"} // remove Email object_instance.remove_email("object_ins@example.com"); // remove SMS object_instance.remove_sms("+15555555555"); // remove Whatsapp object_instance.remove_whatsapp("+15555555555"); // remove fcm push token object_instance.remove_androidpush("__android_push_fcm_token__"); // - by default, token is assumed to be fcm-token // remove apns push token object_instance.remove_iospush("__iospush_token__"); // remove Slack email object_instance.remove_slack( { "email": "object_ins@example.com", "access_token": "xoxb-XXXXXXXX" }); // remove Slack if slack member_id is known object_instance.remove_slack( { "object_ins_id": "U03XXXXXXXX", "access_token": "xoxb-XXXXXXXX" }); // remove Slack channel object_instance.remove_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-XXXXXXXX" }); // remove Slack incoming webhook object_instance.remove_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }); // add MS teams user or channel using conversation_id object_instance.remove_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }); // add MS teams user using user_id object_instance.remove_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "object_ins_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }); // add MS teams using incoming webhook object_instance.remove_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }); ``` If you need to delete/unset all emails (or any other channel) of an object, you can call `object_ins.unset()` method on the object instance. The method accepts the channel key/s (a single key or list of keys) ```javascript Request theme={"system"} object_instance.unset("$email"); object_instance.unset(["$email", "$sms", "$whatsapp"]); // available channel keys - //["$email","$whatsapp","$sms","$androidpush","$iospush","$webpush","$slack","$ms_teams"] // --- multiple channels can also be deleted in one call by passing argument as a list object_ins.unset(["$email", "$sms", "$whatsapp"]); res = object_ins.save(); res.then((res) => console.log(res)).catch((err) => console.log(err)); ``` ```javascript Request theme={"system"} object_instance.set_preferred_language("en"); ``` ```javascript Request theme={"system"} object_instance.set_timezone("America/Belize"); ``` Set is used to set the custom property or properties. The given name and value will be assigned to the object, overwriting an existing property with the same name if present. It can take key as first param, value as second param for setting single object property or object for setting multiple object properties. ```javascript Request theme={"system"} //For setting single property object_instance.set("key", "value"); //For multiple Properties object_instance.set({ "prop1": "val1", "prop2": ["one", "two", "three"], "number": 20 }); ``` | Parameters | Type | Description | | :--------- | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **arg1** | string/dictionary | *Mandatory* This param will be string in case where only single property needs to be created/updated. It will be a dictionary in cases where complex objects need to be set in object properties, like multiple properties, arrays, nested properties etc. Should not start with `$` or `ss_` | | **arg2** | any | *Optional* This will be value that will be attached to key property. Not required in cases where first param is a dictionary. | When you create a key, please ensure that the Key Name does not start with or , as we have reserved these symbols for our internal events and property names. Works just like `object_ins.set`, except it will not overwrite existing property values. This is useful for properties like *First login date* ```javascript Request theme={"system"} // For setting once a single property: object_instance.set_once(key, value); object_instance.set_once("first_login", "2021-11-02"); // For setting once multiple properties object_instance.set_once(properties); object_instance.set_once({"first_login" : "2021-11-02", "DOB" : "1991-10-02"}); ``` Add the given amount to an existing property on the object. If the object does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number for the value. ```javascript Request theme={"system"} // For incrementing a single property: object_instance.increment(key, value); object_instance.increment("login_count", 1); // For incrementing multiple properties: object_instance.increment(property_obj); object_instance.increment({"login_count" : 1, "remaining_credits" : -1}); ``` This method will append a value to the list for a given property. ```javascript Request theme={"system"} // For appending a single property: object_instance.append.append(key, value); object_instance.append.append("wishlist", "iphone12"); // For appending multiple properties: object_instance.append.append(properties); object_instance.append.append({"wishlist" : "iphone12", "cart" : "Apple airpods"}); ``` This method will remove a value from the list for a given property. ```javascript Request theme={"system"} // For removing a single property: object_instance.remove(key, value); object_instance.remove("wishlist", "iphone12"); // For removing multiple properties: object_instance.remove(properties); object_instance.remove({"wishlist" : "iphone12", "wishlist": "Apple airpods"}); ``` This will remove a property permanently from properties. ```javascript Request theme={"system"} // For unsetting a single property: object_instance.unset(key); object_instance.unset("wishlist"); // For unsetting multiple properties: object_instance.unset(property_list); object_instance.unset(["wishlist", "cart"]); ``` | Parameters | Type | Description | | :----------------- | :------------- | :---------------------------------------------------------------- | | **key** | string | This property provided will be deleted from object properties | | **property\_list** | array\[string] | If list is given all properties included in list will be removed. | After calling `add_*/remove_*/unset` methods, don't forget to call `object_ins.save()`. On call of save(), SDK sends the request to SuprSend platform to update the Object Profile. ## List objects List objects for an `object_type`. You can also pass listing options in the payload which includes `limit`,`before`,`after` ```javascript Request theme={"system"} const response = await supr_client.objects.list(object_type,{"limit":10, "after:"cursor"}); ``` ## Get object details ```javascript Request theme={"system"} const response = await supr_client.objects.get(object_type, object_id); ``` ## Create subscriptions ```javascript Request theme={"system"} const payload = { "recipients": ["recipient1", "recipient2"], "properties": {"role":"manager"} } const response = await supr_client.objects.create_subscriptions(object_type, object_id, payload); ``` ## List subscriptions ```javascript Request theme={"system"} const response = await supr_client.objects.get_subscriptions(object_type, object_id,{"limit":10}) ``` ## Remove object subscription ```javascript Request theme={"system"} const payload = {"recipients": ["recipient1", "recipient2"]} const response = await supr_client.objects.delete_subscriptions(object_type, object_id, payload) ``` ## Get list of objects subscribed by object An object can subscribe to other objects. Use this method to get the list of all objects that the current object has subscribed to. ```javascript Request theme={"system"} const response = await supr_client.objects.get_objects_subscribed_to(object_type, object_id); ``` ## Delete object ```javascript Request theme={"system"} const response = await supr_client.objects.delete(object_type, object_id); ``` ### Bulk Delete object ```javascript Request theme={"system"} const response = await supr_client.objects.bulk_delete(object_type, {object_ids: ["object_id1", "object_id2"]}); ``` # Webhook Source: https://docs.suprsend.com/docs/outbound-webhook Learn how to configure outbound webhooks to get real-time update of errors and notification status changes in SuprSend. You can use webhook to get real-time notification updates in your application. SuprSend processes data received from your end-vendors in a standard format, so you don't have to adapt your system for different vendor data structures. ## How it works? SuprSend sends a JSON payload to a specified endpoint (URL) as soon as a notification update is received, e.g. when a message is sent, delivered, seen or replied by user. You can configure which events you want to receive on a particular endpoint, allowing you to filter out unnecessary data and only receive the information relevant to your application. Once the data is received, a POST request is sent to the endpoint you've provided. You can then use it to store information or trigger specific actions in your app. ## Set up your Webhook Endpoint Create an endpoint in your application specifically to receive webhook requests from SuprSend. The endpoint should be an HTTP or HTTPS URL. Once you have created the endpoint, configure it in SuprSend dashboard to receive notification updates. You can use one endpoint per event type or you can send multiple types of events to the same endpoint * Navigate to the Developers section on SuprSend dashboard and go to Webhooks. On the webhooks page, click on "Enable Webhook" * Once you have enabled webhook, you'll see a screen like below. Click on "Add Endpoint" * This will open endpoint settings form. Add the endpoint URL that you have created in the first step and give it a description for your reference. Select the event type that you want to receive on this endpoint for each channel. * Here's how an example configuration of enabling status updates for email notification looks like: Form Fields and their descriptions: | Field | Obligation | Description | | -------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **URL** | *mandatory* | http or https endpoint where you want to receive the webhook requests from SuprSend. Must support POST method and content-type should be application/json | | **Description** | *optional* | add the reference of what information will be tracked in the given endpoint | | **Event type** | *mandatory* | What all events should be tracked in this endpoint at each channel level. In case of WhatsApp, inbound\_message = all requests related to user responses and status = notification status such as sent, delivered, seen etc. | | **Advanced Configuration -> Rate Limit** | *optional* | Number of webhook requests that you want to receive per second on the given endpoint. After the limit is reached, requests will get throttled so to keep a consistent rate under the limit. | | **Advanced Configuration -> Custom Headers** | *optional* | You'll be able to add custom headers after saving the webhook endpoint. `Content-type = application/json` is set by default. You can use this field to set authentication token in SuprSend request | ## Webhook payload structure This is an example of the webhook payload structure. The payload contains normalized data from SuprSend as well as the vendor response received. The payload structure will be similar for all channels except for vendor response. Vendor response will contain the exact vendor response received from the vendors and will vary based on the structure of vendor response. ```json Whatsapp status theme={"system"} { "channel": "whatsapp", "channel_value": "+15555555555", "distinct_id": "joe@example.com", "event_type": "status", "reference": { "idempotency_key": "44220630-d9ae-49f2-918e-0b2f6018cecf", "supr_execution_id": "01GNEQZ65HRWZP5H0JY0TQA9VG", "supr_ngid": "01GNEQZ6F2DE7BF0AFTXWZE7AE", "supr_nid": "01GNEQZ6F4JZF1BAK9CDXYD4T4", "vendor_ack_id": "320xxxx7xxxxxxxxxK-xxxxHxxxx8xxxxxxxx-xxxxYxxxx1xxxxxxxxxxxxx", "workspace_name": "staging", "workspace_uid": "lap5XXXXXXXXXXX" }, "suprsend_status": { "status": "delivered", "timestamp": 1672310923000 }, "template": "purchase-made", "vendor_name": "gupshup", "vendor_response": { "gupshup": { "event_type": "status", "response": [ { "cause": "SUCCESS", "channel": "WHATSAPP", "destAddr": "15555555555", "errorCode": "000", "eventTs": 1672310923000, "eventType": "DELIVERED", "externalId": "320xxxx7xxxxxxxxxK-xxxxHxxxx8xxxxxxxx-xxxxYxxxx1xxxxxxxxxxxxx", "extra": "", "srcAddr": "TESTSM" } ] } }, "version": "1.0", "workflow_name": "Purchase Made" } ``` ```json Whatsapp inbound message theme={"system"} { "channel": "whatsapp", "channel_value": "+15555555555", "distinct_id": "joe@example.com", "event_type": "inbound_message", "reference": { "idempotency_key": "a29a7a15-7a02-400b-8257-2220750b11b3", "supr_execution_id": "01H08212H17MMW152F370TM4FT", "supr_ngid": "01H08212QBEN2Q2E0XHNPK0NBF", "supr_nid": "01H08212QETNQ7J71B42V3XVKD", "vendor_ack_id": "4902186086570803293-kfWdrPL1nFqs7OUihiBn-01H08212QETNQ7J71B42V3XVKD", "workspace_name": "staging", "workspace_uid": "lap5XXXXXXXXXXX" }, "suprsend_inbound_message": { "context": { "from": "+14444444444", "id": "gBEGkZMmKUh5AglyguZOxeyE6Xc" }, "from": "+15555555555", "message_type": "quoted", "source_message_id": "lap5XXXXXXXXXXX-01H08212QETNQ7J71B42V3XVKD", "source_message_sent_via_suprsend": true, "text": { "body": "Hi" }, "timestamp": 1683898377000, "to": "+14444444444", "type": "text" }, "template": "purchase-done", "vendor_name": "gupshup", "vendor_response": { "gupshup": { "context": "{\"from\":\"+14444444444\",\"id\":\"gBEGkZMmKUh5AglyguZOxeyE6Xc\"}", "messageId": "lap5XXXXXXXXXXX-01H08212QETNQ7J71B42V3XVKD", "mobile": "+15555555555", "name": "Joe", "replyId": "4902186086570803293", "text": "Hi", "timestamp": "1683898377000", "type": "text", "waNumber": "+14444444444" } }, "version": "1.0", "workflow_name": "Purchase Done" } ``` *** # Override Recipient Source: https://docs.suprsend.com/docs/override-recipient-list How to override default recipient in trigger nodes to notify specific users/ groups based on event properties? The "recipient" refers to either a single user or a group of users who will be receiving a notification. By default, the user who initiates the event (specified by the `distinct_id` parameter in the track event call) will be considered the recipient. However, there may be situations where you do not want the notification to be sent to the user who initiates the action. For instance, when John shares a document with Olivia for review and Olivia adds comments to the document, you would want to inform John that he has received new comments on the shared document. To handle such cases, you can override the default recipient with another identifier specified in the event property. e.g., if this is the event call, you can pass `.recipient` in the recipient field to send the notification to `joe@example.com` ```python python theme={"system"} distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" # Unique id of user in your application event_name = "product_purchased" # name of the event you're tracking # Properties: a dict representing event-attributes properties = { "first_name": "User", "spend_amount": "$10", "recipient":"joe@example.com" } event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties) ``` You can add recipient as a [JQ-expression](https://stedolan.github.io/jq/manual/#TypesandValues). Below are some examples of how to enter recipients in the workflow: 1. To enter an array of recipients, something like `"recipients": \["recipient1","recipient2","recipient3"\]`, enter in the format `.recipients\[\]`. It would send the notification to all the recipients in the array. 2. If the recipient is a nested event property key like shown below, enter it in the format `.recipient.id`. ``` properties = { "recipient": { "name": "Steve", "id": "0exxx9g12-xxxx-31b5-8752-xxxcb7860fb07" } } ``` 1. In the below array object example, if you want to send notifications to all the admins of an organization, enter the format `.organization.admins\[\].user_id`. ``` properties = { "organization":{ "id":"1234", "admins": [ { "user_id":"0exxx9g12-xxxx-31b5-8752-xxxcb7860fb07", "name":"Olivia" }, { "user_id":"1gxxx5a32-xxxx-73b3-9854-xxxqw7891fb07", "name":"Steve" }], "members": [ { "user_id":"4txxx9a18-xxxx-71f5-0876-xxxmn7908fb07", "name":"Amy" }, { "user_id":"5yxxx0n23-xxxx-90f5-0746-xxxmn9081fb91", "name":"Jane" } ], "location":"San Francisco" }} ``` *** # Pinnacle Source: https://docs.suprsend.com/docs/pinnacle Guide to connect your Pinnacle account with SuprSend to send SMS notifications. ## Pre-Requisites 1. You will need a Pinnacle account to complete this tutorial. You can use your existing Pinnacle account to integrate, or [Create a Pinnacle account](https://console.pinnacle.in/). 2. [Create an Enterprise DLT account](https://dltconnect.airtel.in/signup/) This is to register at the Airtel DLT portal. You can choose any vendor that best suits you. ## Pinnacle integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click SMS -> Pinnacle from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | API Key | API Key of your Pinnacle account, this can either be obtained by getting in touch with pinnacle support team or by generating it from their dashboard. | | Username | Username of your Pinnacle SMS account that is also used to login into Pinnacle | | Password | Password of your Pinnacle account. SuprSend uses this information to send SMS on your behalf via your registered Pinnacle account. | | Price per notification | This is the amount you pay per SMS notification to Pinnacle. It helps us to calculate, estimate and optimise your cost spent on notifications. | | DLT Integration -> 'Mobile Operator' | Mobile Operator of your enterprise DLT account | | DLT Integration -> 'Headers' | 6 digit/character sender id registered for your entity ( *You can get the header details from your DLT portal*) *e.g. SPRSND* Also, you can add multiple headers in the list by just typing the header name and clicking on enter | | DLT Integration -> 'User Name' | User Name of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Password' | Password of your DLT platform login. SuprSend uses this info to register template on your behalf through your registered DLT platform. | | DLT Integration -> 'Entity ID' | Entity Registration ID linked to your DLT account. You can get the Registration Id from your DLT account homepage. SuprSend uses this info to send messages on your behalf through your registered DLT platform. | ## Setting callback URL in Pinnacle account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Send a mail to Pinnacle team to enable below webhook URL to your account: URL: [`https://hub.suprsend.com/webhook/pinnacle/sms/`](https://hub.suprsend.com/webhook/pinnacle/sms/) Request method- POST Alternatively, this can also be achieved by logging into your pinnacle account through the dashboard as shown below. ## How to register headers through Airtel DLT platform To register header on Airtel DLT platform, you can refer the section: [Sender ID/Mask/Header Registration- DLT Platform](https://enterprise.smsgupshup.com/DLT/senderidRegistration) *** # Postgres Source: https://docs.suprsend.com/docs/postgres Guide to set up Postgres database connection to auto sync subscriber lists. With this integration, you can directly send notifications on your 360 degree data sitting in your data warehouse. This enables your data teams or product managers to automate user syncing, setup recurring user cohort sync and create [subscriber lists](/docs/lists) on SuprSend. You can then trigger notification to this list using [broadcast](/docs/broadcast) API. ## Getting Started To start syncing data from your postgres database, you need to add postgres integration on the connector page. 1. Go to [SuprSend dashboard -> Settings -> Connectors](https://app.suprsend.com/en/staging/connectors) page. Here, you'll see the list of available connectors. If you have already setup any connectors in the past, you'll see a list of existing connectors. Click on **"+New Connector"** button to add the connector. 2. Click on Postgres and add your database user’s credentials. All fields in the form are mandatory. | Field | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | A name to uniquely qualify the connection as you'll see it in the connector list on your sync task. You can add the name of database here for easy identification. | | Host | Host’s IP address or [DNS Name](https://docs.aws.amazon.com/redshift/latest/mgmt/jdbc20-obtain-url.html). | | Port | The port on which your Amazon Postgres server is listening for connections. | | Database Name | Name of database you created for your cluster. | | Username | Database username created for suprsend sync. We recommend using a user with the minimal privileges here. This user only requires read permissions with access limited to the tables you want to sync from. | | Password | Database password created for suprsend sync. We recommend using a user with the minimal privileges here. This user only requires read permissions with access limited to the tables you want to sync from. | | SSL Mode | We support both SSL and non-SSL connections. | After adding the required information, click on "connect" to enable this connection. You'll see the error while connecting in case of incorrect credentials. Once your connection is setup, you'll see it in existing connector list and the connector dropdown on sync task. ## Best Practices Before you setup your database sync, you should take some measures to ensure the security of your customers’ data and limit performance impacts to your backend database. The following “best practice” suggestions can help you limit the potential for data exposure and minimize performance impacts: 1. **Sync your read-only replica instance**: Do not sync data directly from your main instance. Instead use a read-only data replica to minimize the load and avoid data loss on your main database. 2. **User connected here should have minimal privileges**: You should have a database user with minimal privileges. This person only requires read permissions with access limited to the tables you want to sync from. 3. **Sync only the data that you’ll need**: Limiting your query can improve performance, and minimize the potential to expose sensitive data. Select only the columns you need to either update user profile in SuprSend and to create list sync. 4. **Use \{\{last\_sync\_time}} to limit query results**: Make sure you use the `{{last_sync_time}}` variable in your recurring sync queries. It stores the timestamp of last successful sync in your list. Adding it in your where statement against datetime index can really speed up the query and limit the number of results returned in consecutive syncs. \{\{last\_sync\_time}} is stored in timestamp format. Use relevant cast expression to format it based on your column type. 5. **Limit your sync frequency**: Setup a sync frequency based on how frequently you want to send notifications on that list. If the previous sync is still in progress when the next interval occurs, we’ll skip the operation and catch up your data on the next interval. Skipped syncs show `Ignored` status in the logs. Frequently skipped operations may indicate that you’re syncing too often. You should monitor your first few syncs to ensure that you haven’t impacted your system’s performance. *** # Postmark Source: https://docs.suprsend.com/docs/postmark Guide to connect your Postmark account with SuprSend to send email notifications. This section is a step by step guide to select Postmark as your email service provider You can use your existing Postmark account to integrate, or create a new account from [here](https://account.postmarkapp.com/sign_up) ## Postmark integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Email -> Postmark from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Server Token | You will get this server token from your Postmark account. This is a mandatory field. SuprSend will use this token to send mails from your Postmark account. Check the [documentation](/docs/postmark#how-to-get-a-server-token-from-postmark) on how to get your Server Token. | | Account Token | Account token is used by postmark for account level API access. These tokens cannot be used to access individual server data. It's a non-mandatory field to send emails. | | From Name | Default 'From Name' that email will go from. You can override this in the individual template. *e.g. SuprSend* | | From Email | From email for Postmark has to be a verified email by postmark . Check [documentation](/docs/postmark#how-to-verify-email-and-whitelist-domain-on-postmark) on how to verify your email or enable domain whitelisting on Postmark. | | Reply Email | Default 'Reply To Email id' on which replies are received. You can override this in the individual template. *e.g. [support@suprsend.com](mailto:support@suprsend.com)* | | Price per notification | This is the amount you pay per email notification to Postmark. It helps us to calculate, estimate and optimise your cost spent on notifications. | | | | ## How to get a server token from Postmark 1. Login to your Postmark account 2. Go to [servers](https://account.postmarkapp.com/servers) section and select your email server 3. Go to section API Tokens 4. Copy the already generated API token or generate a new token. ## How to verify email and whitelist domain on Postmark Email signature verification is an important step without which Postmark does not allow to send emails from the email added in Postmark account. You can add multiple emails by verifying them individually on Postmark or verify all the emails with your email domain. To do this, follow the steps below: 1. Go to Postmark -> [Sender Signatures](https://account.postmarkapp.com/signature_domains) section 2. Add your email, Postmark will send an email for verifying the added email address, complete the verification 3. Alternatively you can also whitelist all the email addresses in your email domain by adding a DKIM DNS record. ## Configuring Open/Click tracking for emails on Postmark SuprSend uses webhooks to update notification statuses on delivery, user actions like open/click or the failure scenarios. This allows SuprSend to power your notification analytics. To allow SuprSend to receive these events via webhook, add SuprSend's webhook URL in your Postmark account. * Go to Postmark -> servers -> select your server -> select your stream -> webhooks -> add webhook * On **Add webhook**, settings page, add [`https://hub.suprsend.com/webhook/postmark/`](https://hub.suprsend.com/webhook/postmark/) in webhook URL section and enable all events on Postmark. ## How to add Unsubscription link in email You can directly add unsubscription link in your email using [`$hosted_preference_url`](/docs/user-preferences#hosted-preference-page) variable in your template. This link directs users to the [SuprSend preference page](/docs/user-preferences). With SuprSend preference setting, you have the flexibility to create multiple notification categories based on the type of notifications. This allows users to opt-out of specific notification categories, providing them with granular control over their preferences compared to other email services where they can only opt-out from all marketing communications at once. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don’t give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don’t have them. *** # Preferences Source: https://docs.suprsend.com/docs/preference-react-sdk Step-by-Step Guide to add SuprSend notification preference centre in react-based websites. ## Integration steps Integrate [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) as it is needed for creating SuprSend Client and authenticating user. Call [useSuprSendClient](https://docs.suprsend.com/docs/react-1#usesuprsendclient) hook in your react component code to get SuprSend client instance which has all preferences methods. Please refer these sections to [understand](https://docs.suprsend.com/docs/js-preferences#understanding-preferences-structure) and [implement](https://docs.suprsend.com/docs/js-preferences#integration) preferences methods in your react application, as integration steps are same for both the web-sdk and react-sdk. ## Example ```javascript Example.jsx theme={"system"} import { useState, useEffect } from "react"; import Switch from "react-switch"; import { SuprSendProvider, ChannelLevelPreferenceOptions, PreferenceOptions, useSuprSendClient, useAuthenticateUser, } from "@suprsend/react"; // -------------- Category Level Preferences -------------- // const handleCategoryPreferenceChange = async ({ data, subcategory, setPreferenceData, suprSendClient, }) => { const resp = await suprSendClient.user.preferences.updateCategoryPreference( subcategory.category, data ? PreferenceOptions.OPT_IN : PreferenceOptions.OPT_OUT ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; const handleChannelPreferenceInCategoryChange = async ({ channel, subcategory, setPreferenceData, suprSendClient, }) => { if (!channel.is_editable) return; const resp = await suprSendClient.user.preferences.updateChannelPreferenceInCategory( channel.channel, channel.preference === PreferenceOptions.OPT_IN ? PreferenceOptions.OPT_OUT : PreferenceOptions.OPT_IN, subcategory.category ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; function NotificationCategoryPreferences({ preferenceData, setPreferenceData, }) { const suprSendClient = useSuprSendClient(); if (!preferenceData.sections) { return null; } return preferenceData.sections?.map((section, index) => { return (
{section?.name && (

{section.name}

{section.description}

)} {section?.subcategories?.map((subcategory, index) => { return (

{subcategory.name}

{subcategory.description}

{ handleCategoryPreferenceChange({ data, subcategory, setPreferenceData, suprSendClient, }); }} uncheckedIcon={false} checkedIcon={false} height={20} width={40} onColor="#2563EB" checked={subcategory.preference === PreferenceOptions.OPT_IN} />
{subcategory?.channels.map((channel, index) => { return ( { handleChannelPreferenceInCategoryChange({ channel, subcategory, setPreferenceData, suprSendClient, }); }} /> ); })}
); })}
); }); } // -------------- Channel Level Preferences -------------- // const handleOverallChannelPreferenceChange = async ({ channel, status, setPreferenceData, suprSendClient, }) => { const resp = await suprSendClient.user.preferences.updateOverallChannelPreference( channel.channel, status ); if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }; function ChannelLevelPreferernceItem({ channel, setPreferenceData }) { const suprSendClient = useSuprSendClient(); const [isActive, setIsActive] = useState(false); return (
setIsActive(!isActive)} >

{channel.channel}

{channel.is_restricted ? "Allow required notifications only" : "Allow all notifications"}

{isActive && (

{channel.channel} Preferences

{ handleOverallChannelPreferenceChange({ channel, status: ChannelLevelPreferenceOptions.ALL, setPreferenceData, suprSendClient, }); }} />

Allow All Notifications, except the ones that I have turned off

{ handleOverallChannelPreferenceChange({ channel, status: ChannelLevelPreferenceOptions.REQUIRED, setPreferenceData, suprSendClient, }); }} />

Allow only important notifications related to account and security settings

)}
); } function ChannelLevelPreferences({ preferenceData, setPreferenceData }) { return (

What notifications to allow for channel?

{preferenceData.channel_preferences ? (
{preferenceData.channel_preferences?.map((channel, index) => { return ( ); })}
) : (

No Data

)}
); } // -------------- Main component -------------- // export default function App() { return ( ); } function Preferences() { const suprSendClient = useSuprSendClient(); const { authenticatedUser } = useAuthenticateUser(); const [preferenceData, setPreferenceData] = useState(); useEffect(() => { if (!authenticatedUser) return; suprSendClient.user.preferences.getPreferences().then((resp) => { if (resp.status === "error") { console.log(resp.error.message); } else { setPreferenceData({ ...resp.body }); } }); // listen for update in preferences data suprSendClient.emitter.on("preferences_updated", (preferenceData) => { setPreferenceData({ ...preferenceData.body }); }); // listen for errors suprSendClient.emitter.on("preferences_error", (response) => { console.log("ERROR:", response?.error?.message); }); }, [authenticatedUser]); if (!preferenceData) return

Loading...

; return (

Notification Preferences

); } // -------------- Custom Checkbox Component -------------- // function Checkbox({ title, value, onClick, disabled }) { const selected = value === PreferenceOptions.OPT_IN; return (

{title}

); } function Circle({ selected, disabled }) { const bgColor = selected ? disabled ? "#BDCFF8" : "#2463EB" : disabled ? "#D0CFCF" : "#FFF"; return (
); } ``` ## # Angular Source: https://docs.suprsend.com/docs/preferences-angular Integration guide to add notification preference centre in Angular website. \*\*Preferences changes in `@suprsend/web-sdk` version >=2.0.0 In `@suprsend/web-sdk@2.0.0` we have revamped SDK. The whole structure of preferences have been changed from SDK side. Please upgrade to newer versions. Old version of documentation for preference is available [here](https://docs.suprsend.com/v1.2.1/docs/angular). ## Pre-Requisites * [Integrate JavaScript SDK](https://docs.suprsend.com/docs/integrate-javascript-sdk) * [Identify user on login](https://docs.suprsend.com/docs/integrate-javascript-sdk#2-authenticate-user) and [reset on logout](https://docs.suprsend.com/docs/integrate-javascript-sdk#3-reset-user) to ensure that preference changes are tagged to the correct user * [Configure notification categories](/docs/user-preferences#create-notification-category) on SuprSend dashboard * [Understanding of the preference data structure and methods](https://docs.suprsend.com/docs/js-preferences) ## Example implementation GitHub Repository for this example implementation can be found here: ("[https://github.com/suprsend/angular-example](https://github.com/suprsend/angular-example)") ## Integration There will be 3 components in example implementation: * preference * channel-level-preferences * category-level-preferences ### Preference component setup **preference** will be the main component and inside it, there will be a property (preferencesData) that contains full user preference data. Whenever there's an update we will update the latest preference data in the preferencesData so that component to rerender. We already created [SuprSend service ](https://github.com/suprsend/angular-example/blob/master/src/app/suprsend.service.ts)to access SuprSend client everywhere in the application which is used to call methods. Call [getPreferences](https://docs.suprsend.com/docs/js-preferences#get-preferences-data) method to get the preferences data for the already identified user. This method is used to get full user preferences data from the SuprSend. This method should be called first before any update methods. Calling this method will make an API call and returns a preference response, which you can store in your instance property **preferenceData** if there is no error. After that we configure 2 [event listeners](https://docs.suprsend.com/docs/js-preferences#event-listeners) `preferences_updated` and `preferences_error` to listen to updates in preference data and errors. Our Main component **preference** has 2 child components one for Category-level preference section and other for Overall Channel-level preference section. ```javascript preference.component.ts theme={"system"} import { SuprsendService } from '../suprsend.service'; @Component({...}) export class PreferenceComponent implements OnInit { preferencesData: any = null; constructor(private router: Router, private ssService: SuprsendService) {} ngOnInit(): void { // before getting preferences make sure to call identify method this.ssService.ssClient.user.preferences.getPreferences().then((resp) => { if (resp.status === 'error') { console.log(resp.error); } else { this.preferencesData = resp.body; } }); // listen for update in preferences data this.ssService.ssClient.emitter.on( 'preferences_updated', (preferenceData) => { this.preferencesData = { ...preferenceData.body } } ); // listen for errors this.ssService.ssClient.emitter.on('preferences_error', (error) => { console.log('ERROR:', error); }); } } ``` ```typescript preference.component.html theme={"system"}

Loading...

Notification Preferences

``` ```css preference.component.css theme={"system"} .main-div { margin: 24px; } .main-header { margin-bottom: 24px; } p, h3 { margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } ``` ### Category level Preference section In category-level preferences, you'll have to fetch the data from 3 parts: * **[Section](https://docs.suprsend.com/docs/js-preferences#11-sections)**-to show sections like "Product Updates" in below example * **[Category](https://docs.suprsend.com/docs/js-preferences#12-categories-sections---sub-categories)**-to show categories and their overall status like "Refunds" in below example * **[CategoryChannel](https://docs.suprsend.com/docs/js-preferences#13-category-channels-sections---sub-categories---channels)**-to show communication channels inside the category and their status Below are the steps to render category preference UI: 1. Loop through the property ***preferenceData.sections*** for showing sections, show sub-categories inside each section, and show subcategory's channels inside each sub-category. 2. Add a ***switch*** button next to each sub-category for opting in and out of the category. Add ***checkbox*** components in sub-category channels for opting in and out of category-channel. You can use any third-party npm package to import these components or design your own component. 3. To update category preference on the click of the switch button, call **[updateCategoryPreference](https://docs.suprsend.com/docs/js-preferences#update-category-preference)** method and if no error is received in response, update the latest data in the instance property. For preference state `opt-in` set the switch state as **on** and **off** for the `opt-out` state. 4. To update category-channel preference on the click of checkbox next to each channel, call the **[updateChannelPreferenceInCategory](https://docs.suprsend.com/docs/js-preferences#update-channel-preference-in-category)** method. Update the latest data in the instance property if no error is received in response. For preference state `opt-in` set the checkbox state as **checked** and **unchecked** for the `opt-out` state. ```typescript category-level-preferences.component.ts theme={"system"} import { Component, Input } from '@angular/core'; import { PreferenceOptions } from '@suprsend/web-sdk'; import { SuprsendService } from '../suprsend.service'; @Component({ selector: 'app-category-level-preferences', templateUrl: './category-level-preferences.component.html', styleUrls: ['./category-level-preferences.component.css'], }) export class CategoryLevelPreferencesComponent { @Input() public preferencesData: any; constructor(private ssService: SuprsendService) {} async handleCategoryPreferenceChange(e: boolean, subcategory: string) { const resp = await this.ssService.ssClient.user.preferences.updateCategoryPreference( subcategory, e ? PreferenceOptions.OPT_IN : PreferenceOptions.OPT_OUT ); if (resp.error) { console.log(resp.error); } else { this.preferencesData = { ...resp.body }; } } async handleChannelPreferenceInCategoryChange( channel: any, category: string ) { if (!channel.is_editable) return; const resp = await this.ssService.ssClient.user.preferences.updateChannelPreferenceInCategory( channel.channel, channel.preference === PreferenceOptions.OPT_IN ? PreferenceOptions.OPT_OUT : PreferenceOptions.OPT_IN, category ); if (resp.status === 'error') { console.log(resp.error); } else { this.preferencesData = { ...resp.body }; } } } ``` ```typescript category-level-preferences.component.html theme={"system"}

{{ section.name }}

{{ section.description }}

{{ subcategory.name }}

{{ subcategory.description }}

```
```css category-level-preferences.component.css theme={"system"} .cat-container { margin-bottom: 24px; } .section-name-container { background-color: #fafbfb; padding-top: 12px; padding-bottom: 12px; margin-bottom: 18px; } .section-name-text { font-size: 18px; font-weight: 500; color: #3d3d3d; } .section-description-text { color: #6c727f; } p, label { margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .subcategory-channel-container { display: flex; gap: 10; margin-top: 12px; } .subcategory-name { font-size: 16px; font-weight: 600; color: #3d3d3d; } .subcategory-top-div { display: flex; justify-content: space-between; align-items: center; } .subcategory-container { border-bottom: 1px solid #d9d9d9; padding-bottom: 12px; margin-top: 18px; } .subcategory-description { color: #6c727f; font-size: 14px; margin-top: 6px; } .category-channel-checkbox { margin-right: 20px; } .category-channel-label { margin-left: 4px; cursor: pointer; } ``` ### Channel level Preference section Below are the steps to render channel preference UI: 1. Loop through the property **preferenceData.channel\_preferences** for showing channels and for every channel item we will show an option to select preference using radio buttons. 2. Add a radio button next, against channel level options for switching from `all` to `required` preference in channel. 3. To update channel preference on the click of the radio button, call **[updateOverallChannelPreference](https://docs.suprsend.com/docs/js-preferences#update-overall-channel-preference)** method, and if no error is received in response, update the latest data in the state. ```typescript channel-level-preferences.component.ts theme={"system"} import { Component, Input } from '@angular/core'; import { ChannelLevelPreferenceOptions } from '@suprsend/web-sdk'; import { SuprsendService } from '../suprsend.service'; @Component({ selector: 'app-channel-level-preferences', templateUrl: './channel-level-preferences.component.html', styleUrls: ['./channel-level-preferences.component.css'], }) export class ChannelLevelPreferencesComponent { @Input() public preferencesData: any; constructor(private ssService: SuprsendService) {} async handleChange(channel: string, preference: string) { const preferenceStatus = preference === 'ALL' ? ChannelLevelPreferenceOptions.ALL : ChannelLevelPreferenceOptions.REQUIRED; const resp = await this.ssService.ssClient.user.preferences.updateOverallChannelPreference( channel, preferenceStatus ); if (resp.status === 'error') { console.log(resp.error); } else { this.preferencesData = { ...resp.body }; } } } ``` ```typescript channel-level-preferences.component.html theme={"system"}

What notifications to allow for channel?

{{ channel.channel }}

Allow required notifications only

Allow all notifications

{{ channel.channel }} Preferences

Allow All Notifications, except the ones that I have turned off

Allow only important notifications related to account and security settings

``` ```Text channel-level-preferences.component.css theme={"system"} .channel-header-div { background-color: #fafbfb; padding-top: 12px; padding-bottom: 12px; margin-bottom: 18px; } .channel-header-p { font-size: 18px; font-weight: 500; color: #3d3d3d; } p, label { margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .channel-container { border: 1px solid #d9d9d9; border-radius: 5px; padding: 12px 24px; margin-bottom: 24px; } .channel-channel-text { font-size: 18px; font-weight: 500; color: #3d3d3d; } .channel-help-text { color: #6c727f; font-size: 14px; margin-top: 6px; } .channel-radio-container { margin-top: 12px; margin-left: 24px; } .channel-radio-pref-text { color: #3d3d3d; font-size: 16px; font-weight: 500; margin-top: 18px; border-bottom: 1px solid #e8e8e8; } .channel-radiohelp-text { color: #6c727f; font-size: 14px; margin-left: 32px; margin-top: 4px; } .label-required { margin-left: 12px; } .radio-grp { margin-top: 12px; } .radio-grp-2 { margin-bottom: 8px; } .radio-grp-container { display: flex; align-items: center; } .all-label { margin-left: 12px; cursor: pointer; } .required-label { margin-left: 12px; cursor: pointer; } ``` # Javascript Source: https://docs.suprsend.com/docs/preferences-javascript Integration guide to add notification preference centre in Javascript website. * **Older Version** (`@suprsend/web-sdk@<=2.0.0`): In `@suprsend/web-sdk@2.0.0` we have revamped SDK to support JWT based authentication along with few structural changes so please upgrade to newer version. Refer older version documentation [here](https://docs.suprsend.com/v1.2.1/docs/preferences-javascript). * **New Version** ([@suprsend/web-sdk@>=2.0.0](https://docs.suprsend.com/docs/js-preferences)): In new version of web-sdk, JWT based authentication has been implemented which provides better security compared to workspace\_key and secret authentication. Along with these, few changes haven been made in terms of preferences method names (added camel casing) and return response structures (return responses inside body key). # React Source: https://docs.suprsend.com/docs/preferences-react-headless Integration guide to add notification preference centre in React website. **End of Support for @suprsend/react-preferences-headless: Migrate to @suprsend/react** * **Older Version** (@suprsend/react-preferences-headless): Development of the `@suprsend/react-preferences-headless` SDK has been discontinued in favour of the new `@suprsend/react` SDK. The whole structure of preferences have been changed from SDK side. Please upgrade to newer version. Refer older version documentation [here](https://docs.suprsend.com/v1.2.1/docs/react-headless). * **New Version** ([@suprsend/react](https://docs.suprsend.com/docs/preferences-1)): In new version we have added preferences in our core react SDK to bring all SuprSend features under one SDK along with few minor changes. # Broadcast Source: https://docs.suprsend.com/docs/python-broadcast Trigger broadcast notifications to a list of users with Python SDK. ## Pre-Requisites [Create a list of users](/docs/python-lists) ## Trigger Broadcast You can trigger broadcast using `supr_client.subscriber_lists.broadcast()` method. ```python Request theme={"system"} from suprsend import Suprsend, SuprsendAPIException, SubscriberListBroadcast supr_client = Suprsend( "_workspace_key_", "_workspace_secret_" ) broadcast_body = { "list_id": "_list_id_", "template": "_template_slug_", "notification_category": "promotional", ## ---- Optional Parameters ------ "data": { "key1": "value1", "key2": "value2" }, "channels": [], # Channel keys - email, sms, whatsapp, androidpush, iospush, ms_teams, slack, webpush "delay": "xxdxxhxxmxxs", "trigger_at": "ISO 8601 timestamp" } inst = SubscriberListBroadcast(body=broadcast_body) resp = supr_client.subscriber_lists.broadcast(inst) print(resp) ``` ```python Sample theme={"system"} from suprsend import Suprsend, SuprsendAPIException, SubscriberListBroadcast supr_client = Suprsend( "_workspace_key_", "_workspace_secret_" ) broadcast_body = { "list_id": "application_abandoned", "template": "complete_application", "notification_category": "promotional", # "channels": ["iospush"], # "delay": "30m", # "trigger_at": "2022-12-27T11:14:51.643Z", "data": { "page_no": "2" } } inst = SubscriberListBroadcast(body=broadcast_body) resp = supr_client.subscriber_lists.broadcast(inst) print(resp) ``` ```python Response theme={"system"} { 'success': True, 'status': 'success', 'status_code': 202, 'message': 'OK' } ``` Broadcast body field description: | Parameter | Description | | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | list\_id | list of users that you want to send broadcast messages to. | | | template | Add template slug here. You can get this slug by clicking on the clipboard icon next to the Template name on SuprSend templates page. It is the same for all channels | | | notification\_category | Preference Category to apply user preference settings while sending. Root categories- system / transactional / promotional | | | data | variable data defined in templates or workflow | | | channels | Specify channels if you don't want to send notification of all live channels in the template. Available channel keys - email, SMS, WhatsApp, androidpush, iospush, ms\_teams, slack, webpush | | | delay | Broadcast will be halted for the time mentioned in delay, and become active once the delay period is over. Example: 1d2h3m4s / 60 | | | trigger\_at | Trigger broadcast on a specific date-time. Pass in ISO 8601 timestamp (e.g. "2021-08-27T20:14:51.643Z") | | *** # Manage Users Source: https://docs.suprsend.com/docs/python-create-user-profile Create, update, & manage user profiles and communication channels using Python SDK methods. ## How Suprsend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Create User To create a new user or to update an existing user, you'll have to fetch user instance. Call `supr_client.user.get_instance` to instantiate user object. ```python theme={"system"} import suprsend supr_client = suprsend.Suprsend("workspace_key", "workspace_secret") distinct_id = "distinct_id" # Unique identifier of user in your application # Instantiate User profile user = supr_client.users.get_edit_instance(distinct_id) ``` ## Edit User To Edit user, you need to first fetch user instance, call all the update methods and save changes using `users.async_edit` method. ```python Request theme={"system"} #Fetch user instance user = supr_client.users.get_edit_instance("_distinct_id_") #Call user update methods user.set_timezone("America/Los_Angeles") user.set("name", "John Doe") #Save Changes res = supr_client.users.async_edit(user) print(res) ``` ```json Response theme={"system"} { "success": boolean, "status": "success"/"fail", "status_code": http_status_code, "message": "response_message"/"error_message", } ``` Here's a list of all edit methods: Add communication channels on which you want to notify user. Push sand Inbox tokens are automatically tracked on user identification when the corresponding frontend SDK is integrated. Other channels (Email, SMS, Slack, MS teams, WhatsApp) need to be explicitly set in user profile. Use `user.add_*` method(s) to add user channels. ```python python theme={"system"} # Add channel details to user-instance. Call relevant add_* methods user.add_email("user@example.com") # - To add Email user.add_sms("+15555555555") # - To add SMS user.add_whatsapp("+15555555555") # - To add WhatsApp user.add_androidpush("__android_push_fcm_token__") user.add_iospush("__iospush_token__") # - To add Slack using email user.add_slack( { "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }) # - To add Slack if slack member_id is known user.add_slack( { "user_id": "U03XXXXXXXX", "access_token": "xoxb-XXXXXXXX" }) # - To add Slack channel user.add_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-XXXXXXXX" }) # - To add Slack incoming webhook user.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }) # - To add MS teams user or channel using conversation_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) # - To add MS teams user using user_id user.add_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) # - To add MS teams using incoming webhook user.add_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }) ``` Use `user.remove_*` method(s) to remove channels. ```python python theme={"system"} # Remove channel details from user-instance. Call relevant remove_* methods user.remove_email("user@example.com") user.remove_sms("+15555555555") user.remove_whatsapp("+15555555555") user.remove_androidpush("__android_push_fcm_token__") user.remove_iospush("__iospush_token__") # - To remove Slack email user.remove_slack( { "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }) # - To remove Slack if slack member_id is known user.remove_slack( { "user_id": "U03XXXXXXXX", "access_token": "xoxb-XXXXXXXX" }) # - To remove Slack channel user.remove_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-XXXXXXXX" }) # - To remove Slack incoming webhook user.remove_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }) # - To remove MS teams user or channel using conversation_id user.remove_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }) # - To remove MS teams user using user_id user.remove_ms_teams( { "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }) # - To remove MS teams using incoming webhook user.remove_ms_teams( { "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }) ``` This method will delete/unset all values in specified channel for user (ex: remove all emails attached to user). ```python python theme={"system"} # --- To delete all emails associated with user user.unset("$email") user.unset(["$email", "$sms", "$whatsapp"]) # Supported channel keys are: # $email, $whatsapp, $sms, $androidpush, $iospush, $webpush, $slack, $ms_teams ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```python theme={"system"} user.set_preferred_language("en") ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```python theme={"system"} user.set_timezone("America/Los_Angeles") ``` Set is used to add custom user properties. It is an upsert function, meaning any existing property value with the same key will be overwritten on subsequent updates. ```python theme={"system"} user.set(key, value) user.set("name","John Doe") user.set({ key1: value1, key2: value2 }) user.set({"name": "John Doe","city": "San Francisco"}) ``` Works just like user.set, except it will not override already existing property values. This is useful for properties like first\_login\_date. ```python theme={"system"} user.set_once(key, value) user.set_once("first_login","2021-11-02") user.set_once({ key1: value1, key2: value2 }) user.set_once({"first_login": "2021-11-02","signup_date": "2021-11-02"}) ``` Unset is used to remove a property key. ```python theme={"system"} user.unset(key) user.unset("name") user.unset([key1, key2]) user.unset(["name","city"]) ``` This method will append a value to the array list. ```python theme={"system"} user.append(key, value) user.append("played_games", "game_1") user.append({ key1: value1, key2: value2 }) user.append({"played_games": "game_1", "liked_games": "game_2"}) ``` This method will remove a value from the array list. ```python theme={"system"} user.remove(key, value) user.remove("played_games", "game_1") user.remove({ key1: value1, key2: value2 }) user.remove({"played_games": "game_1", "liked_games": "game_2"}) ``` Increase or decrease integer values on consecutive action, like login count. To reduce a property, provide a negative number for the value. ```python theme={"system"} user.increment(key, value) user.increment("login_count", 1) user.increment({ key1: value1, key2: value2 }) user.increment({"login_count" : 1, "order_count" : 1}) ``` After calling `add*/remove*/unset` methods, don't forget to call `users.async_edit()` since user edit is async update and the changes will be sent to SuprSend only after calling this method. ## Bulk Update users There isn't any limit on number-of-records that can be added to `bulk_users` instance. Use `.append()` on bulk\_users instance to add however-many-records to call in bulk. ```python Request theme={"system"} # Create bulk instance bulk_ins = supr_client.users.get_bulk_edit_instance() # Prepare multiple users edit instance distinct_id1 = "__distinct_id1__" # User 1 u1 = supr_client.users.get_edit_instance(distinct_id1) u1.add_email("u1@example.com") distinct_id2 = "__distinct_id2__" # User 2 u2 = supr_client.users.get_edit_instance(distinct_id2) u2.add_email("u2@example.com") # Append users to the bulk instance bulk_ins.append(u1, u2) # ------- res = bulk_ins.save() print(res) ``` ```python Response theme={"system"} { status = "fail", total = 10, # number of records sent in bulk success = 0, # number of records succeeded failure = 10, # number of records failed failed_records = [{"record": {...}, "error": "error_str", "code": 500}] } ``` **Bulk API supported in SDK version 0.2.0 and above:** Bulk API is supported in SuprSend python-sdk version 0.2.0 and above. If you are using an older version, please upgrade to the latest SDK version. ## Get user details ```javascript Request theme={"system"} res = supr_client.users.get("_distinct_id_") print(res) ``` ```json Response theme={"system"} { "distinct_id": "_distinct_id_", "properties": { "name": "John Doe", "email": ["johndoe@example.com"], "created_at": "2024-01-01T12:00:00Z" }, "status": "active" } ``` ## Delete user ```javascript Request theme={"system"} res = supr_client.users.delete("_distinct_id_") print(res) ``` ```json Response theme={"system"} { "success": True, "status_code": 204 } ``` ## Get list of objects subscribed by user You can pass optional query parameters -`limit`, `before`, `after` ```javascript Request theme={"system"} res = supr_client.users.get_objects_subscribed_to("_distinct_id_", {"after": "01JJW6HXXXXPB59ARDW85G0KN", "limit": 1}) print(res) ``` ```json Response theme={"system"} { "objects": [ { "object_id": "Frontend", "type": "Developers", "subscribed_at": "2024-02-20T10:15:30Z" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": false } } ``` ## Get lists subscribed by user You can pass optional query parameters -`limit`, `before`, `after` ```javascript Request theme={"system"} res = supr_client.users. get_lists_subscribed_to("_distinct_id_", {"after": "01JJW6HXXXXPB59ARDW85G0KN", "limit": 1}) print(res) ``` ```json Response theme={"system"} { "lists": [ { "list_id": "product_updates", "subscribed_at": "2024-02-20T12:00:00Z", "status": "subscribed" } ], "paging": { "after": "01JJW6HXXXXPB59ARDW85G0KN", "has_more": true } } ``` # Lists Source: https://docs.suprsend.com/docs/python-lists Manage subscriber lists with Python SDK: create/update list, add/remove/replace users. The Lists SDK methods lets you create / manage list of subscribers. You can then send [broadcast](/docs/broadcast) to all the users in the list or create a workflow that triggers when a new user enters / exits list. ## Create / Update list You can use `supr_client.subscribers_list.create` method to create a new list ```python Request theme={"system"} from suprsend import SubscriberListBroadcast,SuprsendAPIException,SuprsendValidationError from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("workspace_key", "workspace_secret") try: data = supr_client.subscriber_lists.create({ "list_id": "_list_id_", #Unique identifier for the list "list_name": "_list_name_", #readable name of the list (optional) "list_description": "_some sample description for the list_" }) print(data) except (SuprsendAPIException,SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "list_id":"_list_id_", "list_name":"_list_name_", "list_description":"_some sample description for the list_", "list_type":"static_list", "subscribers_count":0, "source":"None", "is_readonly":false, "status":"active", "track_user_entry":false, "track_user_exit":false, "requested_for_delete":false, "created_at":"2025-03-14T13:42:45.641000Z", "updated_at":"2025-03-14T13:42:45.641000Z", "drafts":"None" } ``` Guidelines on defining the list\_id * `list_id` is case-insensitive. Suprsend first converts list\_id to lowercase before storing it or doing any sort of comparison on it. * `list_id `can be of max 64 characters. * It can contain characters \[a-z0-9\_-] that is alphanumeric characters, \_(underscore) and -(hyphen). ## Get list data You can get the latest information of a list using `supr_client.subscribers_list.get` method. ```python Request theme={"system"} from suprsend import SubscriberListBroadcast, SuprsendAPIException, SuprsendValidationError from suprsend import Suprsend supr_client = Suprsend("workspace_key", "workspace_secret") try: data = supr_client.subscriber_lists.get("_list_id_") print(data) except (SuprsendAPIException, SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "list_id":"_list_id_", "list_name":"_list_name_", "list_description":"_some sample description for the list_", "list_type":"static_list", "subscribers_count":0, "source":"None", "is_readonly":false, "status":"active", "track_user_entry":false, "track_user_exit":false, "requested_for_delete":false, "created_at":"2025-03-14T13:42:45.641000Z", "updated_at":"2025-03-14T13:42:45.641000Z", "drafts":"None" } ``` ## Get all lists To get the data of all the lists created in your workspace, use `supr_client.subscribers_list.get_all()`  method ```python Request theme={"system"} from suprsend import SubscriberListBroadcast,SuprsendAPIException,SuprsendValidationError from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("workspace_key", "workspace_secret") try: const data = supr_client.subscribers_list.get_all(); print(data) except (SuprsendAPIException,SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "meta": { "count": 14, "limit": 20, "offset": 0 }, "results": [ { "list_id": "newsletter_subscribers", "list_name": "Newsletter Subscribers", "list_description": "all users who opted in to receive product updates", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": false, "track_user_exit": false, "requested_for_delete": false, "created_at": "2025-01-14T13:42:45.641000Z", "updated_at": "2025-01-14T13:42:45.641000Z", "drafts": "None" }, { "list_id": "abandoned_application", "list_name": "Signups abandoned application", "list_description": "Users who didn’t finish application", "list_type": "static_list", "subscribers_count": 0, "source": "None", "is_readonly": false, "status": "active", "track_user_entry": true, "track_user_exit": true, "requested_for_delete": false, "created_at": "2025-02-14T13:42:45.641000Z", "updated_at": "2025-02-14T13:42:45.641000Z", "drafts": "None" } ... ] } ``` ## Add Subscribers to the list Use `supr_client.subscribers_list.add()` to add list subscribers. There is no limit to the number of subscribers that you can add to a list. ```python Request theme={"system"} from suprsend import SubscriberListBroadcast, SuprsendAPIException, SuprsendValidationError from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("workspace_key", "workspace_secret") try: data = supr_client.subscriber_lists.add("_list_id_", [ "_distinct_id1_", "_distinct_id2_" ]) print(data) except (SuprsendAPIException, SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "success":true } ``` ## Remove Subscribers from the list You can remove subscribers from the list using `supr_client.subscribers_list.remove()` ```python Request theme={"system"} from suprsend import SubscriberListBroadcast, SuprsendAPIException, SuprsendValidationError from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("workspace_key", "workspace_secret") try: data = supr_client.subscriber_lists.remove("_list_id_", [ "_distinct_id1_", "_distinct_id2_" ]) print(data) except (SuprsendAPIException, SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "success":true } ``` ## Delete list You can delete a list using the `supr_client.subscribers_list.delete()` method. ```python Request theme={"system"} from suprsend import SubscriberListBroadcast, SuprsendAPIException, SuprsendValidationError from suprsend import Suprsend # Initialize SDK supr_client = Suprsend("workspace_key", "workspace_secret") try: data = supr_client.subscriber_lists.delete("_list_id_") print(data) except (SuprsendAPIException, SuprsendValidationError) as ex: print(ex) ``` ```python Response theme={"system"} { "success": true, "message": "List deleted successfully" } ``` Deleting a list is a permanent action and cannot be undone or restored. ## Replace users in list If you want to refresh a list completely with a new set of users, you can replace users by creating a draft version of the list and updating users in it. Use this method only if you want to completely overwrite the list members. This method will create a draft version of the list where you can add the new set of users to replace existing users. ```python Request theme={"system"} data = supr_client.subscriber_lists.start_sync("_list_id_") ``` You can use this method to add subscribers to the list's draft version created in Step-1. You'll get `version_id` in the start sync response. ```python Request theme={"system"} data = supr_client.subscriber_lists.add_to_version("_list_id_", "01HHCTXXXXXXXXXXX", ["_distinct_id1_", "_distinct_id2_"]) ``` You can use this method to remove subscribers from the list's draft version created in Step-1. You'll get `version_id` in the start sync response. ```python Request theme={"system"} data = supr_client.subscriber_lists.remove_from_version("_list_id_", "01HHCTXXXXXXXXXXX", ["_distinct_id1_", "_distinct_id2_"]) ``` Once your subscribers are updated in the list, use this method to finish sync and make the draft version updated in the above steps live. ```python Request theme={"system"} data = supr_client.subscriber_lists.finish_sync("_list_id_", "01HHCTXXXXXXXXXXX") ``` ### Delete Draft list You can also delete the draft list if it was created by mistake. ```python Request theme={"system"} data = supr_client.subscriber_lists.delete_version("_list_id_", "01HHCTXXXXXXXXXXX") ``` # Objects Source: https://docs.suprsend.com/docs/python-objects Create, update, & manage objects and their subscriptions using Python SDK methods. Objects represent non-user entities such as organizations, teams, roles, and projects. Understand more about objects [here](https://docs.suprsend.com/docs/objects). ## Create / Update object This is an `upsert` method, used to create or update an object. ```python Request theme={"system"} import suprsend supr_client = suprsend.Suprsend("Workspace_Key", "Workspace_Secret") # Object details object_id = "engineering" object_type = "departments" # Optional: for setting additional properties on object properties = { "key1": "val1", "$email":"devs@abc.com" } res = supr_client.objects.upsert(object_type, object_id, properties) print(res) ``` ```python Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "key1": "val1" }, "created_at":"2025-01-20T19:21:42.030343+00:00", "updated_at":"2025-02-14T14:06:05.928675+00:00", "$inbox":[ { "value":"H4iGeGSHyXXXXXXSeuBb_PJGXXXgWu_LsXXXXXyiw", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ], "$email":[ { "value":"abc@example.com", "status":"active", "perma_status":"active" } ] } ``` ## Edit an object There are 2 ways in which you can edit an object data. * Build edit payload yourself * Use helper methods provided by SDK (Recommended) ### Edit an object (build edit payload yourself) Use this to modify an object, typically for removing channels or unsetting properties. The payload will follow the same structure as the [Object Edit API](https://docs.suprsend.com/reference/edit-object-profile) ```python Request theme={"system"} res = supr_client.objects.edit ("object_type", "object_id", {"operations": [{"$set": {"company_name": "ABC company"}}]}) print(res) ``` ```python Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "name":"John Doe" }, "created_at":"2025-03-14T15:31:59.151061+00:00", "updated_at":"2025-03-14T15:31:59.182616+00:00", "$inbox":[ { "value":"75Ev_m8yln0N_i1iXXXXXXr2eNOE_4ROXXXXXX1TgTc", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` ### Edit an object (using helper methods) \[Recommended] To edit object, you need to first fetch object instance, call all the update methods and save changes using `objects.edit` method. ```python Request theme={"system"} # Fetch object instance obj_inst = supr_client.objects.get_edit_instance("object_type", "object_id") # Call object update methods obj_inst.set_timezone("America/Los_Angeles") obj_inst.set("company_name", "ABC company") # Save Changes res = supr_client.objects.edit(obj_inst) print(res) ``` ```python Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "$preferred_language":"en", "$timezone":"America/Los_Angeles", "company_name":"ABC company" }, "created_at":"2025-03-04T08:34:58.061696+00:00", "updated_at":"2025-03-14T14:40:21.467156+00:00", "$inbox":[ { "value":"dHvPs6pmUXXXXXXTAsfM2yroXXXX2CXZOtjXXXXXX8", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` List of available methods: Use `object_ins.add*` method(s) to add channels ```python theme={"system"} obj_inst = supr_client.objects.get_edit_instance("object_type", "object_id") # Add Email obj_inst.add_email("user@example.com") # Add SMS obj_inst.add_sms("+15555555555") # Add Whatsapp obj_inst.add_whatsapp("+15555555555") # Add Androidpush token with vendor obj_inst.add_androidpush("androidpush_fcm_token__", "fcm") # Add iOS Push token obj_inst.add_iospush("__iospush_apns_token__") # Add Slack using user email id slack_ident_email = { "access_token": "xoxb-XXXXXXXX", "email": "user@example.com" } obj_inst.add_slack(slack_ident_email) # Add Slack using member_id of the user slack_ident_user_id = { "access_token": "xoxb-XXXXXXXX", "user_id": "U03XXXXXXXX" } obj_inst.add_slack(slack_ident_user_id) # Add Slack channel_id slack_ident_channel_id = { "access_token": "xoxb-XXXXXXXX", "channel_id": "C04XXXXXXXX" } obj_inst.add_slack(slack_ident_channel_id) # Add Slack incoming webhook slack_ident_webhook = { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } } obj_inst.add_slack(slack_ident_webhook) # Add Webpush token json (VAPID) webpush_ident = { "endpoint": "__end_point__", "expirationTime": "", "keys": { "p256dh": "__p256dh__", "auth": "__auth_key__" } } obj_inst.add_webpush(webpush_ident, "vapid") # Save changes res = supr_client.objects.edit(obj_inst) print(res) ``` Use `object_ins.remove_*` method(s) to remove channels from an object ```python theme={"system"} obj_inst = supr_client.objects.get_edit_instance("object_type", "object_id") # Remove Email obj_inst.remove_email("user@example.com") # Remove SMS obj_inst.remove_sms("+15555555555") # Remove Whatsapp obj_inst.remove_whatsapp("+15555555555") # Remove Androidpush token with vendor obj_inst.remove_androidpush("androidpush_fcm_token__", "fcm") # Remove iOS Push token obj_inst.remove_iospush("__iospush_apns_token__") # Remove Slack using user email id slack_ident_email = { "access_token": "xoxb-XXXXXXXX", "email": "user@example.com" } obj_inst.remove_slack(slack_ident_email) # Remove Slack using member_id of the user slack_ident_user_id = { "access_token": "xoxb-XXXXXXXX", "user_id": "U03XXXXXXXX" } obj_inst.remove_slack(slack_ident_user_id) # Remove Slack channel_id slack_ident_channel_id = { "access_token": "xoxb-XXXXXXXX", "channel_id": "C04XXXXXXXX" } obj_inst.remove_slack(slack_ident_channel_id) # Remove Slack incoming webhook slack_ident_webhook = { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } } obj_inst.remove_slack(slack_ident_webhook) # Remove Webpush token json (VAPID) webpush_ident = { "endpoint": "__end_point__", "expirationTime": "", "keys": { "p256dh": "__p256dh__", "auth": "__auth_key__" } } obj_inst.remove_webpush(webpush_ident, "vapid") # Save changes res = supr_client.objects.edit(obj_inst) print(res) ``` This method will delete/unset all values in specified channel for object (ex: remove all emails attached to object). ```python theme={"system"} # Remove multiple channels in bulk channels_to_remove = ["$email", "$whatsapp", "$sms", "$androidpush", "$iospush", "$webpush", "$slack", "$ms_teams"] obj_inst.unset(channels_to_remove) ``` If you want to send notification in user's preferred language, you can set it by passing [language code](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) in this method. This is useful especially for the applications which offer vernacular or multi-lingual support. ```python theme={"system"} obj_inst.set_preferred_language("en") ``` You can set timezone of user using this method. Value for timezone must be from amongst the [IANA timezones](https://data.iana.org/time-zones/tzdb-2024a/zonenow.tab). ```python theme={"system"} obj_inst.set_timezone("America/Los_Angeles") ``` Set any custom property using this method. It will shallow merge existing properties with new values. Key shouldn't start with `$` or `ss`. ```python Request theme={"system"} # Set a single property obj_inst.set(key, value) obj_inst.set("company_name","ABC company") # Set multiple properties obj_inst.set({ key1: value1, key2: value2 }) obj_inst.set({"company_name": "ABC company","city": "San Francisco"}) ``` Use this to unset existing channels or properties. ```python Request theme={"system"} #unset single channel or property obj_inst.unset("$email") #unset multiple channels or properties obj_inst.unset(["$email","company_name"]) ``` Use this to append item to an array based property. ```python Request theme={"system"} #append single property obj_inst.append(key, value) obj_inst.append("played_games", "game_1") #append multiple properties obj_inst.append({ key1: value1, key2: value2 }) obj_inst.append({"played_games": "game_1", "liked_games": "game_2"}) ``` Use this to remove an item from array based property ```python Request theme={"system"} #for single property obj_inst.remove(key, value) obj_inst.remove("played_games", "game_1") #for multiple properties obj_inst.remove({ key1: value1, key2: value2 }) obj_inst.remove({"played_games": "game_1", "liked_games": "game_2"}) ``` Use this to add new properties which are not overridden, such as `first_login_at` ```python Request theme={"system"} #single property obj_inst.set_once(key, value) obj_inst.set_once("first_login", "2021-11-02") #multiple properties obj_inst.set_once({ key1: value1, key2: value2 }) obj_inst.set_once({"first_login": "2021-11-02","signup_date": "2021-11-02"}) ``` Use on numeric values to increment/decrement. Provide a negative value for decrement ```python Request theme={"system"} #single property obj_inst.increment(key, value) obj_inst.increment("login_count", 1) #multiple property obj_inst.increment({ key1: value1, key2: value2 }) obj_inst.increment({"login_count" : 1, "order_count" : 1}) ``` ## List objects List objects for an `object_type`. You can also pass listing options in the payload which includes `limit`,`before`,`after` ```python Request theme={"system"} res = supr_client.objects.list("object_type") {"after": "01JJW6H55NXXXX59ARDW85G0KN", "limit": 1} print(res) ``` ```python Response theme={"system"} { "meta":{ "count":1, "limit":10, "has_prev":false, "before":"None", "has_next":false, "after":"None" }, "results":[ { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "company_name":"ABC company" }, "created_at":"2025-03-14T15:31:59.151061+00:00", "updated_at":"2025-03-14T15:31:59.182616+00:00", "$inbox":[ { "value":"75Ev_m8yln0N_i1iXXXXXr2eNOE_4RXXXX7T1TgTc", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ] } ``` ## Get object details ```python Request theme={"system"} res = supr_client.objects.get("object_type", "object_id") print(res) ``` ```python Response theme={"system"} { "object_type":"departments", "id":"engineering", "subscriptions_count":0, "properties":{ "company_name":"ABC company" }, "created_at":"2025-03-14T15:31:59.151061+00:00", "updated_at":"2025-03-14T15:31:59.182616+00:00", "$inbox":[ { "value":"75Ev_m8yXXXX_i1iHXXXXX2eNOE_4ROuXXXXXTgTc", "id_provider":"suprsend", "status":"active", "perma_status":"active" } ] } ``` ## Add object subscription ```python Request theme={"system"} res = supr_client.objects.create_subscriptions( "object_type", "object_id", { "recipients": [ "distinct_id_1", {"object_type": "teams", "object_id": "product"}, ], "properties": {"type": "members"}, }, ) print(res) ``` ```python Response theme={"system"} { "results":[ { "properties":{ "type":"members" }, "user":{ "distinct_id":"distinct_id_1", "updated_at":"2025-02-16T22:45:16.395682+00:00" }, "object":"None", "created_at":"2025-02-16T22:45:16.399883+00:00", "updated_at":"2025-02-16T22:45:16.399883+00:00" }, { "properties":{ "type":"members" }, "user":"None", "object":{ "id":"product", "object_type":"teams", "subscriptions_count":6, "updated_at":"2025-03-16T22:45:16.399427+00:00" }, "created_at":"2025-02-16T22:45:16.399883+00:00", "updated_at":"2025-02-16T22:45:16.399883+00:00" } ] } ``` ## List object subscription ```python Request theme={"system"} res = supr_client.objects.get_subscriptions("object_type", "object_id") {"after": "01JJW6H55NXXXXXX5G0KN", "limit": 1} print(res) ``` ```python Response theme={"system"} { "meta":{ "count":1, "limit":10, "has_prev":false, "before":"None", "has_next":false, "after":"None" }, "results":[ { "properties":{ "type":"admin" }, "user":, "object":{ "id":"product", "object_type":"teams", "subscriptions_count":0, "updated_at":"2025-03-12T22:30:51.508+00:00" }, "created_at":"2025-03-12T22:30:51.510014+00:00", "updated_at":"2025-03-12T22:30:51.510014+00:00" } ] } ``` ## Remove object subscription ```python Request theme={"system"} res = supr_client.objects.delete_subscriptions ( "object_type", "object_id", { "recipients": [ "distinct_id_1", {"object_type": "departments", "object_id": "engineering"}, ] }, ) print(res) ``` ```python Response theme={"system"} { "success":true, "status_code":204 } ``` ## Get list of objects subscribed by object An object can subscribe to other objects. Use this method to get the list of all objects that the current object has subscribed to ```python Request theme={"system"} res = supr_client.objects.get_objects_subscribed_to("object_type", "object_id", {"after": "01JJW6H55NXXXXX85G0KN", "limit": 1}) print(res) ``` ```python Response theme={"system"} { "meta":{ "count":1, "limit":10, "has_prev":false, "before":"None", "has_next":false, "after":"None" }, "results":[ { "properties":{ "type":"admin" }, "object":{ "id":"product", "object_type":"teams", "subscriptions_count":1, "updated_at":"2025-03-12T22:30:50.865092+00:00" }, "created_at":"2025-03-12T22:30:51.510014+00:00", "updated_at":"2025-03-12T22:30:51.510014+00:00" } ] } ``` ## Delete object ```python Request theme={"system"} res = supr_client.objects.delete("object_type", "object_id") print(res) ``` ```python Response theme={"system"} { "success":true, "status_code":204 } ``` ### Bulk Delete objects ```python Request theme={"system"} res = supr_client.objects.bulk_delete("object_type", {"object_ids": ["object_id1","object_id2"]}) print(res) ``` ```python Response theme={"system"} { "success": True, "status_code": 204 } ``` # Send and Track Events Source: https://docs.suprsend.com/docs/python-send-event-data Learn how to send events to trigger workflows, with code snippets and examples. ## Pre-Requisites [Create User Profile](/docs/python-create-user-profile) ## Send Event You can send event to Suprsend platform by using the `supr_client.track_event` method. When you call `supr_client.track_event`, the SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. The actual processing/execution of event happens asynchronously. ```python Request theme={"system"} from suprsend import Suprsend from suprsend import Event supr_client = suprsend.Suprsend("workspace_key", "workspace_secret") distinct_id = "distinct_id" # Mandatory, Unique id of user in your application event_name = "event_name" # Mandatory, name of the event you're tracking brand_id = "brand_id" # Mandatory, name of the event you're tracking # Properties: Optional, default=None, a dict representing event-attributes properties = { "key1":"value1", "key2":"value2" } event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties, idempotency_key="__uniq_request_id__", brand_id="default") # Track event response = supr_client.track_event(event) print(response) ``` ```python Sample theme={"system"} from suprsend import Event # Track Event Example distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" event_name = "product_purchased" # Properties: Optional, default=None, a dict representing event-attributes properties = { "first_name": "User", "spend_amount": "$10", "nested_key_example": { "nested_key1": "some_value_1", "nested_key2": { "nested_key3": "some_value_3", }, } } event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties) # Track event response = supr_client.track_event(event) print(response) ``` ```python Response theme={"system"} # Response structure { "success": True, # if true, request was accepted. "status": "success", "status_code": 202, # http status code "message": "OK", } { "success": False, # error will be present in message "status": "fail", "status_code": 500, # http status code "message": "error message", } ``` | Parameter | Description | Format | Obligation | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----------- | | `distinct_id` | `distinct_id` of user performing the event | int, bigint, string, UUID | *mandatory* | | `event_name` | string identifier for the event like `product_purchased` | string | *mandatory* | | `properties` | a dictionary representing event attributes like `first_name` Event properties can be used to pass template variables in case of event based trigger | Dictionary | *optional* | **Event naming guidelines:** Event Name or Property Name should not start with or , as we have reserved these symbols for our internal events and property names. ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. You can use this key to track webhooks related to workflow notifications. Idempotency key should be unique that you generate for each request. You may use any string up to 255 characters in length. Any space at start and end of the key will be trimmed. Here are some common approaches for assigning idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ## Add file attachment in event (for email) To add one or more Attachments to a Notification (viz. Email), you can just append the filepath of attachment to the event instance. * Call `event.add_attachment()` for each file with an accessible URL. * Ensure that file\_path is a publicly accessible URL. Since event API size can't be > 100 KB, local file paths can't be passed in event attachment. ```python Request theme={"system"} from suprsend import Event distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" event_name = "invoice_generated" properties = {...} event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties) # this snippet can be used to add attachment to event file_path = "https://www.adobe.com/sample_file.pdf" event.add_attachment(file_path) response = supr_client.track_event(event) print(response) ``` ```python Response theme={"system"} # Response structure { "success": true, // if true, request was accepted. "status": "success", "status_code": 202, // http status code "message": "OK", } { "success": false, // error will be present in message "status": "fail", "status_code": 500, // http status code "message": "error message", } ``` Please add public accessible URL only as attachment file otherwise it will throw an error `404 not found` and workflow will not be triggered ## Bulk Event trigger You can use Bulk API to send multiple events. Use `.append()` on bulk\_events instance to add however-many-records to call in bulk. ```python Request theme={"system"} from suprsend import Event bulk_ins = supr_client.bulk_events.new_instance() # Example e1 = Event("distinct_id1", "event_name1", {"k1": "v1"}) # Event 1 e2 = Event("distinct_id2", "event_name2", {"k2": "v2"}) # Event 2 # --- use .append on bulk instance to add one or more records bulk_ins.append(e1) bulk_ins.append(e2) # OR bulk_ins.append(e1, e2) # ------- response = bulk_ins.trigger() print(response) ``` ```python With Email attachment theme={"system"} from suprsend import Event bulk_ins = supr_client.bulk_events.new_instance() # Example e1 = Event("distinct_id1", "event_name1", {"k1": "v1"}) # Event 1 # this snippet can be used to add attachment to event file_path1 = "https://www.adobe.com/sample_file.pdf" e1.add_attachment(file_path1) e2 = Event("distinct_id2", "event_name2", {"k2": "v2"}) # Event 2 # this snippet can be used to add attachment to event file_path2 = "https://www.africau.edu/images/default/sample.pdf" e2.add_attachment(file_path2) # --- use .append on bulk instance to add one or more records bulk_ins.append(e1) bulk_ins.append(e2) # OR bulk_ins.append(e1, e2) # ------- response = bulk_ins.trigger() print(response) ``` ```python Response theme={"system"} { status = "fail", total = 10, # number of records sent in bulk success = 0, # number of records succeeded failure = 10, # number of records failed failed_records = [{"record": {...}, "error": "error_str", "code": 500}] } ``` Bulk API is supported in SuprSend python-sdk version 0.2.0 and above. If you are using an older version, please upgrade to the latest SDK version. *** # Tenants Source: https://docs.suprsend.com/docs/python-tenants Learn how to create, update, fetch, & list tenants using Python SDK. Tenants (previously named as brands) are used for white labeling notifications, personalizing template content or capturing admin preferences for another entity/organization. Tenants are workspace-level entities and by default, a tenant with `tenant_id="default"` (representing your organization) is created your workspace. Read more about tenants [here](/docs/tenants). ### Create / Update tenant ```python Request theme={"system"} from suprsend import Suprsend, SuprsendAPIException supr_client = Suprsend("workspace_key", "workspace_secret") # Define tenant ID and prepare tenant payload tenant_id = "tenant_01" tenant_payload = { "tenant_name": "ABC Company", "logo": "https://company_logo_image.png", "primary_color": "#ff0000", # ---- Optional Parameters ------- "secondary_color": "#00ff00", "tertiary_color": "#0000ff", "timezone": "America/New_York", "blocked_channels": ["email"], # These channels will be skipped for sending notification to recipients when triggering for this tenant. "social_links": { "website": "https://suprsend.com", "facebook": "", "linkedin": "", "x": "", "instagram": "", "medium": "", "discord": "", "telegram": "", "youtube": "", "tiktok": "", }, "embedded_preference_url": "", # In-product Notification centre link for capturing user preferences. "properties": { "prop1": "value1", "prop2": { "prop3": ["value2"] } } } try: response = supr_client.tenants.upsert(tenant_id, tenant_payload) print(response) except SuprsendAPIException as ex: print(ex) ``` ```python Response theme={"system"} { "tenant_id":"tenant_01", "tenant_name":"ABC Company", "logo":"https://company_logo_image.png", "timezone":"America/New_York", "blocked_channels":["email"], "embedded_preference_url":"", "hosted_preference_domain":"https://preferences.suprsend.com", "primary_color":"#ff0000", "secondary_color":"#00ff00", "tertiary_color":"#0000ff", "social_links":{ "website":"https://suprsend.com", "facebook":"", "linkedin":"", "x":"", "instagram":"", "medium":"", "discord":"", "telegram":"", "youtube":"", "tiktok":"" }, "properties":{ "prop1": "value1", "prop2": { "prop3": ["value2"] } }, "updated_at":"2025-03-13T18:16:35.744843Z" } ``` | Field | Description | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | tenant\_id (mandatory) | Unique identifier for the tenant (max 64 characters, case insenstive). It can contain characters \[a-z0-9\_\\-] that is alphanumeric characters, \\\_(underscore) and -(hyphen). | | tenant\_name (\*mandatory) | Tenant's display name in a human-readable format | | logo | Logo URL, used in email headers or on hosted preference page for per-tenant branding. | | primary\_color, secondary\_color, tertiary\_color | Tenant branding colors- used in template design or hosted preference page for per-tenant branding. | | timezone | The primary timezone for most tenants' recipients. Used as a fallback when sending notifications in the recipient's timezone and timezone is not set in recipient profile. | | blocked\_channels | These channels will be skipped for sending notification to recipients when triggering for this tenant. Used to apply channel level opt-outs at admin level. | | social\_links | URLs of the tenant's social media accounts. | | embedded\_preference\_url | | | properties | Custom properties of the tenant like address, to be used in template or workflow. | All properties of the tenant can be referred as `{{$brand.prop}}` (handlebars) or `data\["tenant"\].prop` in JSONNET format. ### Get tenant ```python Request theme={"system"} from suprsend import Suprsend, SuprsendAPIException supr_client = Suprsend("workspace_key", "workspace_secret") try: response = supr_client.tenants.get("tenant_id") print(response) except SuprsendAPIException as ex: print(ex) ``` ```python Response theme={"system"} { "tenant_id":"tenant_id", "tenant_name":"ABC Company", "logo":"https://logo_url.png", "timezone":"America/New_York", "blocked_channels":[ "whatsapp", "email" ], "embedded_preference_url":"https://app.suprsend.com/preferences", "hosted_preference_domain":"preferences.suprsend.com", "primary_color":"#0751E8", "secondary_color":"#e308e3", "tertiary_color":"#1EE9F1", "social_links":{ "website":"https://www.suprsend.com/", "facebook":"", "linkedin":"", "x":"", "instagram":"", "medium":"", "discord":"", "telegram":"", "youtube":"", "tiktok":"" }, "properties":{ "address":"5678 Market Street Suite 300 San Francisco, CA 94103 USA" }, "updated_at":"2025-02-27T10:21:15.235666Z" } ``` ### List tenants By default, `limit=20`. The maximum value for `limit` is `1000`. ```python Request theme={"system"} from suprsend import Suprsend, SuprsendAPIException supr_client = Suprsend("workspace_key", "workspace_secret") try: params = {"limit": 20, "offset": 0} response = supr_client.tenants.list(**params) print(response) except SuprsendAPIException as ex: print(ex) ``` ```python Response theme={"system"} { "meta":{ "count":28, "limit":20, "offset":0 }, "results":[ { "tenant_id":"tenant_01", "tenant_name":"ABC Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" }, { "tenant_id":"tenant_02", "tenant_name":"CDE Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" } ... ] } ``` # Trigger Workflow from API Source: https://docs.suprsend.com/docs/python-trigger-workflow-from-api Learn how to trigger workflows using direct workflow API, with code snippets and examples. It is a unified API to trigger workflow and doesn't require user creation before hand to trigger notification. Recommended for platforms transitioning their existing notifications to SuprSend. If you are using our frontend SDKs to configure notifications and passing events and user properties from third-party data platforms like Segment, then [event-based trigger](/docs/python-send-event-data) would be a better choice. 📘 Available in SDK version >= v0.11.0 ## Payload Schema ```python Sample Payload theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true, }, "recipients": [ # notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true, }, # notify object {"object_type": "teams", "id": "finance", "$skip_create": true}, ], "data": { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234", }, }, tenant_id="tenant_id1", idempotency_key="_unique_identifier_of_the_request_", ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```python Response theme={"system"} { success: true, status: 'success', status_code: 202, message: { status: 'success' } } ``` To prevent automatic creation of an actor, or recipient (user/object) in SuprSend (the case where they already exist in your system), you can use the `"$skip_create": true` flag. This can be applied inside the actor, individual user recipient objects, or object recipient objects. Once your request is accepted, you can check the status of your request on [SuprSend Logs](https://app.suprsend.com/en/staging/logs/api?last_n_minutes=1440) page. | Property | Type | Description | | ---------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `workflow` | `string` | Slug of the designed workflow on the SuprSend dashboard. You'll get the slug from workflow settings. | | `actor` *(optional)* | `string / object` | Includes `distinct_id` and properties of the user who performed the action. Used for [cross-user notifications](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#sending-cross-user-notifications), where you need to include actor properties in the notification template. Actor properties can be added as `$actor.`. | | `recipients` | `array of string / array of objects` | List of users who need to be notified. You can add up to 100 recipients in a workflow trigger. Recipients can be passed as an array of `distinct_ID` (if the user is pre-synced in the SuprSend database) or [defined inline](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#identifying-recipients-inline). | | `data` | `object` | Variable data required to render dynamic template content or workflow properties such as dynamic delay or channel override in the send node. | | `tenant_id` | `string` | Unique identifier of the [brand / tenant](https://docs.suprsend.com/docs/brands). | | `idempotency_key` | `string` | Unique identifier of the request. It will be returned in the [outbound webhook response](https://docs.suprsend.com/docs/outbound-webhook). You can use it to map notification statuses and replies in your system. | | `recipients[].$timezone` | `string` | Used to set the recipient's timezone. Allows sending notifications in the user's local timezone. Pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | `recipients[].$preferred_language` | `string` | Used to set the recipient's preferred language. | ### Sending notification to multiple recipients Recipients in workflow call is an array of `distinct_ids` or recipient objects. You can pass up to 100 recipients in a single workflow trigger. SuprSend will internally convert it into multiple workflow triggers, one for each recipient in the array. ```json json theme={"system"} "recipients": [ { "distinct_id": "id1", "$email":["id1@example.com"], "name":"recipient_1" }, { "distinct_id": "id1", "$email":["id2@example.com"], "name":"recipient_2" } ] ---- OR ------ "recipients": ["id1","id2"] ``` 📘**Use lists to broadcast to a large list of users:** We recommend you to use [lists](/docs/python-lists) and [broadcasts](/docs/python-broadcast) to send notifications to a user list larger than 1000 users. This approach allows for bulk processing within SuprSend, resulting in significantly faster delivery compared to individual workflow calls. Sending individual workflows to a large set of users may introduce delays in your notification queue and is not an optimized way of handling bulk trigger. ### Identifying recipients inline One of the benefits of using direct workflow trigger is that you can identify recipients inline. You can include recipient channel information, their channel preferences, and their user properties along with the workflow trigger. Upon triggering the workflow, the recipient will be automatically created in the SuprSend database in the background. This facilitates dynamic synchronization of your user data within SuprSend and eliminates the need for any migration efforts on your end to start sending notifications from SuprSend. You can also use recipient properties in your template as `$recipient.`. This is how the complete recipient object with look like ```json json theme={"system"} { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "$channels":["email","inbox"], "user_prop1":"value_1", "$preferred_language":"en", "$timezone": "America/New_York" } ``` | Property | Type | Description | | -------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `distinct_id` | `string` | Unique identifier of the user to be notified. | | `$` (e.g. `$sms`) | `array of string / objects` | You can pass user channel information using `$` key. The channel info will be updated in the user profile in the background. For this workflow, only channel values specified in this key will be used for sending notifications (instead of all channel values present in the user profile). Refer to how different communication channels can be passed [here](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#add-user-communication-channel). | | `$channels` | `array of string` | Use it to pass the user's channel preference in the payload. You can always use our [in-built preference APIs](/docs/user-preferences) to maintain user notification preferences. Preferences defined within SuprSend will automatically apply with the workflow trigger. By default, notifications will be sent to all channels defined in the workflow delivery node. However, if the user has a specific channel preference for a notification (e.g. only wants to receive payment reminders via email), you can include that preference in the workflow payload. This ensures notifications are sent only to the channels specified in the `$channels` key. Supported channel values: `email, sms, whatsapp, androidpush, iospush, slack, webpush, ms_teams`. | | `$preferred_language` | `string` | To set the recipient's preferred language. This supports localization in notification content. Pass the language in `ISO 639-1` 2-letter format. [Refer to all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | | `$timezone` | `string` | To set the recipient's timezone. Used to send notifications in the user's local timezone. Pass the timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | `*` | `key-value pair` | You can pass other user properties to render dynamic template content in key-value pairs like `"user_prop1": "value1"`. Extra properties will be set in the subscriber profile (as subscriber properties), which can then be used in the template as `$recipient.`. | #### Add user communication channel ```json json theme={"system"} "$email":["user@example.com"], "$whatsapp":["+15555555555"], "$sms":["+15555555555"], "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }] // slack using email "$slack": [{ "user_id": "U/WXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack using member_id "$slack": [{ "channel": "CXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack channel "$slack": [{ "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }] // slack incoming webhook "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }] // MS teams user or channel using conversation_id "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }] // MS teams user using user_id "$ms_teams": [{ "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }] // MS teams incoming webhook ``` ### Sending cross-user notifications In scenarios where you need to notify a group of users based on another user's action, such as sending a notification to the document owner when someone comments on it, you can specify the actor in your workflow call. This allows you to use actor's name or other properties in your notification template. Actor properties can be included in the template as `$actor.`. Sample template with actor and recipient properties: ```text text theme={"system"} //handlebar template Hi {{$recipient.name}}, {{$actor.name}} added {{length comments}} new comments on the {{doc_name}}. //Rendered content Hi recipient_1, actor_1 added 2 new comments on the annual IT report. ``` ```json Sample workflow body theme={"system"} { "workflow": "new_comment", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name":"actor_1" }, "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com""], "name":"recipient_1" } ], "data":{ "doc_name": "annual IT report", "date": "2024-01-01", "comments":["change the date","rest looks good"] } } ``` ### Sending notification to anonymous user You can send notifications to anonymous users by passing ` "is_transient": True` in your recipient object. This approach is recommended for scenarios where you need to send notifications to unregistered users without creating them in the SuprSend platform. The same way, you can pass ` "is_transient": True` in your actor object to use actor properties in template without creating user profile. ```python Request theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "actor": { "is_transient": True, "name":"actor_1" }, "recipients": [ { "is_transient": True, "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, tenant_id = "tenant_id1", idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ### Multi-tenant notifications For use cases where you want to send notifications to your enterprise customers' end users, pass the `tenant_id` in your workflow instance. You can use this to dynamically manage [tenant level notification customizations](/docs/tenants). This includes the ability to customize template design or content and route notifications via tenant vendors. ```python Request theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={...}, tenant_id = "tenant_id1", idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ### Idempotent requests SuprSend supports idempotency to ensure that requests can be retried safely without duplicate processing. If Suprsend receives and processes a request with an idempotency\_key, it will skip processing requests with same `idempotency_key` for next 24 hours. Idempotency key should be uniquely generated for each request (max 255 characters allowed). Spaces in start and end of the key will be trimmed. Here are some common approaches for generating idempotency keys: * **Generate a random UUID** for each request. * **Construct the idempotency key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency key. ## Bulk trigger multiple workflows Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.append()` on `workflows.bulk_trigger_instance()` instance to add however-many-records to call in bulk. ```python Request theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Workflow: 1 w1 = WorkflowTriggerRequest( body={...}, tenant_id = "tenant_id1", idempotency_key = "_unique_identifier_of_the_request_" ) # Workflow: 2 w2 = WorkflowTriggerRequest( body={...} ) # ...... Add as many Workflow records as required. bulk_ins = supr_client.workflows.bulk_trigger_instance() bulk_ins.append(w1,w2) # Trigger workflow response = bulk_ins.trigger() print(response) ``` ```python Response theme={"system"} { status = "fail", total = 10, # number of records sent in bulk success = 0, # number of records succeeded failure = 10, # number of records failed warnings = [], failed_records = [{"record": {...}, "error": "error_str", "code": 500}] } ``` ## Add file attachment in email To add one or more Attachments to a Notification (viz. Email), call `wf_instance.add_attachment()` for each file with local-path. Ensure that file\_path is proper, otherwise it will raise FileNotFoundError. ```python Request theme={"system"} from suprsend import Workflow workflow_body = {...} wf_instance = Workflow(body=workflow_body) # this snippet can be used to add attachment to workflow file_path = "/home/user/billing.pdf" wf_instance.add_attachment(file_path) ``` A single workflow body size (including attachment) must not exceed 800KB (800 x 1024 bytes). *** ## Dynamic workflow trigger You can trigger workflow from python SDK using `supr_client.trigger_workflow` method. The SDK internally makes an `HTTP` call to SuprSend Platform to register this request, and you'll immediately receive a response indicating the acceptance status. Note: The actual processing/execution of workflow happens asynchronously. Once your request is accepted, you can check the status of your request in the '[ SuprSend Logs](https://app.suprsend.com/en/staging/logs)' section. ```python Request theme={"system"} from suprsend import Workflow # Prepare Workflow body workflow_body = { "name": "workflow_name", "template": "template_slug", "notification_category": "notification_category", # notification category transactional/promotional/system "delay": "time_delay", # time delay after which the first notification will be sent "trigger_at": "date string in ISO 8601", #to trigger scheduled notifications "users": [ { "distinct_id": "distinct_id", # unique identifier of the user # if $channels is present, communication will be triggered on mentioned channels only. # "$channels": ["email"], # User communication channel can be added as [optional]: # "$email":["user@example.com"], # "$whatsapp":["+15555555555"], # "$sms":["+15555555555"], # "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], # "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], # "$slack": { # "email": "user@example.com", # "access_token": "xoxb-XXXXXXXX" #} --- slack using email # "$slack": { # "user_id": "U/WXXXXXXXX", # "access_token": "xoxb-XXXXXX" #} --- slack using member_id # "$slack": { # "channel": "CXXXXXXXX", # "access_token": "xoxb-XXXXXX" #} --- slack channel # "$slack": { # "incoming_webhook": { # "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" # } #} --- slack incoming webhook # "$ms_teams": { #"tenant_id": "c1981ab2-9aaf-xxxx-xxxx", #"service_url": "https://smba.trafficmanager.net/amer", #"conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" #} --- MS teams user or channel using conversation_id # "$ms_teams": { #"tenant_id": "c1981ab2-9aaf-xxxx-xxxx", #"service_url": "https://smba.trafficmanager.net/amer", #"user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" #} --- MS teams user using user_id # "$ms_teams": { # "incoming_webhook": { # "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" # } #} --- MS teams incoming webhook } ], # delivery instruction [optional]. how should notifications be sent, and whats the success metric "delivery": { "smart": , "success": "success_metric", "time_to_live": "TTL duration", # will be applicable for smart = TRUE "mandatory_channels": [] # list of mandatory channels e.g ["email"], will be applicable for smart = TRUE }, # data can be any json / serializable python-dictionary "data": { "key":"value", "nested_key": { "nested_key1": "some_value_1", "nested_key2": { "nested_key3": "some_value_3", }, } } } wf = Workflow(body=workflow_body, idempotency_key="__uniq_request_id__", brand_id="default") # Trigger workflow response = supr_client.trigger_workflow(wf) print(response) ``` ```python Response theme={"system"} # Response structure { "success": True, # if true, request was accepted. "status": "success", "status_code": 202, # http status code "message": "OK", } { "success": False, # error will be present in message "status": "fail", "status_code": 500, # http status code "message": "error message", } ``` For configuring a workflow from backend, you can pass following properties in your method | Parameter | Description | Format | Obligation | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | **`name`** | It is the unique name of the workflow. You can see workflow-related analytics on the workflow page (how many notifications were sent, delivered, clicked, or interacted). The workflow name should be easily identifiable for your reference at a later stage. | *text* | *Mandatory* | | **`template`** | It is the unique slug of the template created on the SuprSend platform. You can get this slug by clicking on the clipboard icon next to the Template name on the SuprSend templates page. It is the same for all channels. | **`slug name`** | *Mandatory* | | **`notification_category`** | You can understand more about them in the [Notification Category](https://docs.suprsend.com/docs/notification-category) documentation. | `system / transactional / promotional` | *Mandatory* | | **`delay`** | Workflow will be halted for the time mentioned in delay and becomes active once the delay period is over. | `**XX**d**XX**h**XX**m**XX**s` or if it's a number (`n`), then delay is in seconds (`n`). | *Optional* | | **`trigger_at`** | Trigger workflow on a specific date-time. | Date string in ISO 8601 format e.g. `"2021-08-27T20:14:51.643Z"` | *Optional* | | **`users`** | Array object of target users. At least 1 user mandatory.`distinct_id` for each user is mandatory. You can pass up to 100 entries in the users array. Channel information is non-mandatory. If you pass channel information here, then these channels will be used for sending notifications; otherwise, channels will be picked from the user profile. | `json"users": [ { "distinct_id": "value", "$channels": [], channel_information_dict #(optional) } ]` | *Mandatory* | | **`delivery`** | Delivery instructions for the workflow. You can enable smart delivery by setting `"smart": True`.By default, the delivery instruction will be:`json"delivery": { "smart": False, "success": "seen" }`Further details are given in the [below section](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#smart-delivery). | `json"delivery": { "smart": True/False, "success": "delivered/seen/interaction/", "time_to_live": "", "mandatory_channels": [] }` | *Optional* | | **`data`** | JSON. To replace variables in the template, templates use the [`handlebars`](https://handlebarsjs.com/guide/) language. | `json "data": { "key": { "key": "value", "key": "value" } }` | *Optional* | | **`brand_id`** | `Brand_id` of the tenant to trigger a notification on behalf of your tenants. | *string* | *Optional* | | **`idempotency_key`** | Unique key in the request call for [idempotent requests](https://docs.suprsend.com/docs/python-trigger-workflow-from-api#idempotent-requests). | *string* | *Optional* | **+CountryCode Required for SMS and Whatsapp:** For setting `$sms` and `$whatsapp`, `+` is mandatory to send along with phone number. Eg: +1 for US #### Bulk API for triggering multiple workflows Bulk API allows you to send multiple workflow requests in a single call. There isn't any limit on number-of-records that can be added to bulk\_workflows instance. Use `.append()` on `bulk_workflows` instance to add however-many-records to call in bulk. ```python Request theme={"system"} from suprsend import Workflow bulk_ins = supr_client.bulk_workflows.new_instance() # one or more workflow instances workflow1 = Workflow(body={...}) # body must be a proper workflow request json/dict workflow2 = Workflow(body={...}) # body must be a proper workflow request json/dict # --- use .append on bulk instance to add one or more records bulk_ins.append(workflow1) bulk_ins.append(workflow2) # OR bulk_ins.append(workflow1, workflow2) # ------- response = bulk_ins.trigger() print(response) ``` ```python Response theme={"system"} { status = "fail", total = 10, # number of records sent in bulk success = 0, # number of records succeeded failure = 10, # number of records failed failed_records = [{"record": {...}, "error": "error_str", "code": 500}] } ``` *** # Overview Source: https://docs.suprsend.com/docs/quick-start-guide Start setting up your notifications with SuprSend by following quick start guides for one of the mentioned channels. You can start with your primary channel and add rest of the channels just by [integrating with their respective vendors](/docs/vendors) and [adding their template content](/docs/templates). ### Setup and test notification using APIs You can try out SuprSend APIs from our [Postman collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521). ### What's next 1. Check out [best practices](docs/best-practices-notification-system-design) to design the right notification experience for your users. 2. **Invite other team members**- You can invite other collaborators from your team to your SuprSend account by inviting them from [Account Settings -> Teams](https://app.suprsend.com/en/account-settings/team) page. You'll get this option by clicking on the profile icon in the top nav bar. *** # Customising Feed Source: https://docs.suprsend.com/docs/react-customising-feed Customization options to adjust the look and feel of In-app feed as per your product designs. ## Customising tenant By default `tenantId` prop is set to `default` tenant. If you use multi-tenant architecture and want to fetch inbox notifications of other tenant, you can pass `tenant_id` prop. ```javascript Inbox theme={"system"} ``` ```javascript SuprSendFeedProvider theme={"system"} .... ``` If you are passing `tenant_id` in feed, make sure to pass scope key while creating [userToken](https://docs.suprsend.com/docs/client-authentication#2-creating-signed-user-jwt-token) else 403 error will be thrown due to scope mismatching. ## Adding tabs You can pass `stores` prop to add multiple tabs in feed. For each tab you can write your own logic to get notifications based on tags, notification categories and notification status like read, archived. Read more about it [here](https://docs.suprsend.com/docs/multi-tabs). ```javascript Inbox theme={"system"} ``` ```javascript SuprSendFeedProvider theme={"system"} .... ``` ## Disable pagination By default, feed supports infinite scrolling type pagination to get older notifications when you scroll through notifications list. If you want to remove infinite scroll, that is stop getting previous page notifications, you can set `pagination=false`. ```javascript Inbox theme={"system"} ``` ```javascript NotificationFeed theme={"system"} ``` ## Enable dark mode ```javascript Inbox theme={"system"} ``` ```javascript NotificationFeed theme={"system"} ``` | Light Theme | Dark Theme | | ---------------------------------------------- | ---------------------------------------------- | | ![](https://files.readme.io/bb3ea43-image.png) | ![](https://files.readme.io/e4b8764-image.png) | ## Custom notification card click handler Clicking on notification card will open the link if `Action URL` field is present in triggered inbox template. If you want to override this with your custom callback function you can pass `notificationClickHandler`. ```javascript Inbox theme={"system"} { console.log('notification clicked', notificationData.n_id) }} /> ``` ```javascript NotificationFeed theme={"system"} { console.log('notification clicked', notificationData.n_id) }} /> ``` ## Custom action button click handlers Clicking on action buttons in notification card will open link if you provide url on action button fields in triggered inbox template. If you want to override this with your custom action button callback functions you can pass `primaryActionClickHandler` and `secondaryActionClickHandler`. ```javascript Inbox theme={"system"} { console.log('primary action button clicked', notificationData.n_id) }} secondaryActionClickHandler={(notificationData: IRemoteNotification) => { console.log('secondary action button clicked', notificationData.n_id) }} /> ``` ```javascript NotificationFeed theme={"system"} { console.log('primary action button clicked', notificationData.n_id) }} secondaryActionClickHandler={(notificationData: IRemoteNotification) => { console.log('secondary action button clicked', notificationData.n_id) }} /> ``` ## Popover position Popover component is wrapper around NotificationFeed component and will be opened on click of bell icon. By default popover is shown at bottom ie., the inbox notifications popup list will be shown at bottom of the bell icon. Example: Feed needs to be shown at bottom of left sidebar above profile icon, in that case on click of bell icon you would want to show notifications popover at right side. ```javascript Inbox theme={"system"} ``` ## Custom bell component For Bell, you can customise either css styles of existing bell component or replace existing bell with your own bell component. To replace existing bell component with your own component use `bellComponent` prop. ```javascript Inbox theme={"system"}

MyBell

} /> ```
## Custom badge component This element shows the number of unseen notifications on Bell. You can customise either css styles of existing badge component or replace existing badge with your own badge component. To replace existing badge component with your own component use `badgeComponent` prop. ```javascript Inbox theme={"system"}

{count}

} /> ```
## Custom header component You can add custom component on right side of Header, this will replace `Mark all as read` text and add any JSX you pass as component in that place. You can even add custom icons like setting or preferences in that JSX and navigate user to that pages. ```javascript Inbox theme={"system"} void, closeInboxPopover: ()=>void }) =>

Custom Mark All as Read

} /> ``` ``` void, closeInboxPopover: ()=>void }) =>

Custom Mark All as Read

} /> ```
## Custom notification card component You can customise either css styles of existing notification card component or replace existing card with your own card component. To replace existing component use `notificationComponent` prop. ```javascript Inbox theme={"system"} (

config.markAsRead(config.notificationData.n_id)}> My Notification card

)} /> ``` ``` (

config.markAsRead(config.notificationData.n_id)}> My Notification card

)} /> ``` ``` interface CustomNotificationCardProps { notificationData: IRemoteNotification; markAsRead: (e?: Event) => Promise | undefined; markAsUnread: (e?: Event) => Promise | undefined; markAsArchived: (e?: Event) => Promise | undefined; markAsInteracted: (e?: Event) => Promise | undefined; } ```
### BodyMarkdown component We support markdown language in body field in inbox template. So while designing your own notification card component if you want to use our inbuilt body component which includes support for markdown language to save some time you could use `BodyMarkdown` component. ```javascript Inbox theme={"system"} (
config.markAsRead(config.notificationData.n_id)}>

My Notification card

)} /> ``` ``` interface BodyMarkdownProps { body: string; handleActionClick?: HandleActionClick; // for links, this callback will be executed on click style?: NotificationCardBodyTextThemeProps; } interface NotificationCardBodyTextThemeProps extends React.CSSProperties { color?: string; blockquoteColor?: string; tableBorderColor?: string; linkColor?: string; } type HandleActionClick = ( e: React.MouseEvent, clickData: ClickHandlerPayload ) => void; ```
## Custom loader component Existing loader icon which is displayed while getting notifications from SuprSend server, can be changed. ```javascript Inbox theme={"system"} (

Loading...

)} /> ``` ``` (

Loading...

)} /> ```
## Custom no notifications component When no notifications are present then we show empty component. This component can be overridden by using `noNotificationsComponent` prop. ```javascript Inbox theme={"system"} (

No Notifications Yet

)} /> ``` ```javascript NotificationFeed theme={"system"} (

No Notifications Yet.

)} /> ```
## Customising CSS styles If you don't want to replace existing components, you have an option to change styles of them. ```javascript ITheme TypeDefs theme={"system"} // typedef for Inbox drop-in component interface ITheme extends INotificationFeedTheme { bell?: IconThemeProps; badge?: BadgeThemeProps; } interface IconThemeProps { height?: number | string; width?: number | string; color?: string; } interface BadgeThemeProps extends React.CSSProperties { backgroundColor?: string; color?: string; } // typedef for NotificationFeed drop-in component interface INotificationFeedTheme { header?: IHeaderTheme; tabs?: TabsThemeProps; notificationsContainer?: INotificationsContainerTheme; notification?: INotificationCardTheme; } interface IHeaderTheme { container?: React.CSSProperties; headerText?: React.CSSProperties; markAllReadText?: React.CSSProperties; } interface TabsThemeProps { color?: string; unselectedColor?: string; bottomColor?: string; badgeColor?: string; badgeText?: string; } interface INotificationsContainerTheme { container?: React.CSSProperties; noNotificationsText?: React.CSSProperties; noNotificationsSubtext?: React.CSSProperties; loader?: LoaderThemeProps; } interface INotificationCardTheme { container?: NotificationCardContainerThemeProps; pinnedIcon?: IconThemeProps; pinnedText?: React.CSSProperties; headerText?: React.CSSProperties; bodyText?: NotificationCardBodyTextThemeProps; unseenDot?: React.CSSProperties; avatar?: React.CSSProperties; createdOnText?: React.CSSProperties; subtext?: React.CSSProperties; expiresText?: NotificationCardExpriesTextThemeProps; actions?: Array<{ container?: NotificationCardActionButtonViewThemeProps; text?: React.CSSProperties; }>; actionsMenuIcon?: CardActionMenuIconThemeProps; actionsMenu?: React.CSSProperties; actionsMenuItem?: CardActionsMenuItem; actionsMenuItemIcon?: IconThemeProps; actionsMenuItemText?: React.CSSProperties; } interface LoaderThemeProps { color?: string; } interface NotificationCardContainerThemeProps extends React.CSSProperties { borderBottom?: string | number; readBackgroundColor?: string; unreadBackgroundColor?: string; hoverBackgroundColor?: string; } interface NotificationCardBodyTextThemeProps extends React.CSSProperties { color?: string; blockquoteColor?: string; tableBorderColor?: string; linkColor?: string; } interface NotificationCardExpriesTextThemeProps extends React.CSSProperties { expiringBackgroundColor?: string; expiringColor?: string; } interface NotificationCardActionButtonViewThemeProps extends React.CSSProperties { hoverBackgroundColor?: string; } interface CardActionMenuIconThemeProps extends IconThemeProps { hoverBackgroundColor?: string; } interface CardActionsMenuItem extends React.CSSProperties { hoverBackgroundColor?: string; } ``` ``` const darkColors = { primary: '#2E70E8', primaryText: '#EFEFEF', secondaryText: '#CBD5E1', border: '#3A4A61', main: '#1D2635', error: '#F97066', }; const darkInboxStyles = { bell: { color: '#fff' }, badge: { backgroundColor: darkColors.primary }, ...darkNotificationsFeedStyles }; const darkNotificationsFeedStyles={ header: { container: { backgroundColor: darkColors.main, borderBottom: `0.5px solid ${darkColors.border}`, boxShadow: '0 0 5px 0 rgba(0, 0, 0, 0.5)', }, headerText: { color: darkColors.primaryText }, markAllReadText: { color: darkColors.primary }, }, tabs: { color: darkColors.primaryText, unselectedColor: darkColors.secondaryText + 'D9', bottomColor: darkColors.primary, badgeColor: 'rgba(100, 116, 139, 0.5)', badgeText: darkColors.primaryText, }, notificationsContainer: { container: { backgroundColor: darkColors.main, borderColor: darkColors.border, }, noNotificationsText: { color: darkColors.primaryText, }, noNotificationsSubtext: { color: darkColors.secondaryText, }, loader: { color: darkColors.primary }, }, notification: { container: { borderBottom: `1px solid ${darkColors.border}`, readBackgroundColor: darkColors.main, unreadBackgroundColor: '#273244', hoverBackgroundColor: '#2D3A4D', }, pinnedText: { color: darkColors?.secondaryText, }, headerText: { color: darkColors.primaryText }, bodyText: { color: darkColors.secondaryText, blockquoteColor: 'rgba(100, 116, 139, 0.5)', }, unseenDot: { backgroundColor: darkColors.primary }, createdOnText: { color: darkColors.secondaryText }, subtext: { color: '#94a3b8' }, actions: [ { container: { backgroundColor: darkColors.primary } }, { container: { borderColor: darkColors.border, backgroundColor: 'transparent', hoverBackgroundColor: darkColors.main, }, text: { color: darkColors.secondaryText }, }, ], expiresText: { backgroundColor: 'rgba(100, 116, 139, 0.5)', color: darkColors.secondaryText, expiringBackgroundColor: 'rgba(217, 45, 32, 0.15)', expiringColor: darkColors.error, }, actionsMenuIcon: { color: darkColors.secondaryText, hoverBackgroundColor: 'rgba(100, 116, 139, 0.5)', }, actionsMenu: { backgroundColor: darkColors.main, borderColor: darkColors.border, }, actionsMenuItem: { hoverBackgroundColor: 'rgba(100, 116, 139, 0.2)' }, actionsMenuItemIcon: { color: darkColors.secondaryText }, actionsMenuItemText: { color: darkColors.secondaryText, }, } } ``` # Events and User methods Source: https://docs.suprsend.com/docs/react-events-and-user-methods Methods to send event or manage user updates based on user action in react-based websites. Integrate [SuprSendProvider](/docs/react-sdk#suprsendprovider) as it is needed for creating SuprSend Client and authenticating user. Call [useSuprSendClient](https://docs.suprsend.com/docs/react-1#usesuprsendclient) hook in your react component code to get SuprSend client instance which has all event and user update methods. Please refer these sections to call [events](https://docs.suprsend.com/docs/js-events-and-user-methods#trigger-events) and [user update](https://docs.suprsend.com/docs/js-events-and-user-methods#update-user-profile) methods in your react application, as integration steps are same for both the web-sdk and react-sdk. # Full screen or Sidesheet notifications feed Source: https://docs.suprsend.com/docs/react-full-screen-or-sidesheet-notifications-feed Guide to integrate the `NotificationFeed` component for displaying notifications in full screen/ sidesheet in your React app. If you want to render notifications in separate screen or in side-sheet or in any other way instead of popover, you can use `NotificationFeed` component wrapped inside [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider). Both of these components should be children of [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider). ```javascript Example.jsx theme={"system"} import { SuprSendProvider, SuprSendFeedProvider, NotificationFeed } from '@suprsend/react'; function Example() { return ( ); } ``` ```javascript TypeDef theme={"system"} interface SuprSendFeedProviderProps { tenantId?: string; pageSize?: number; stores?: IStore[] | null; host?: { socketHost?: string, apiHost?: string }; } interface NotificationFeedProps { pagination?: boolean; showUnreadCountOnTabs?: boolean; hideAvatar?: boolean; themeType?: ThemeType; theme?: INotificationFeedTheme; notificationClickHandler?: (notification: IRemoteNotification) => void; primaryActionClickHandler?: (notification: IRemoteNotification) => void; secondaryActionClickHandler?: (notification: IRemoteNotification) => void; loaderComponent?: React.FC; noNotificationsComponent?: React.FC; tabBadgeComponent?: React.FC<{ count: number }>; notificationComponent?: React.FC; headerRightComponent?: React.FC<{markAllRead: () => void, closeInboxPopover: () => void}>; } ``` > Please refer [Customising CSS styles](https://docs.suprsend.com/docs/customising-feed#customising-css-styles) section to view typedefs for `INotificationFeedTheme`. Infinite scroll is also included to fetch more pages in `NotificationFeed` component. Specifying height for the container is needed for infinite scroll to work properly. ```javascript Example.jsx theme={"system"} ``` # React Headless Inbox Source: https://docs.suprsend.com/docs/react-headless Integrate the SuprSend React Headless Inbox, providing control over inbox UI and features like notifications and event handling. **🚧 End of Support for \`@suprsend/react-inbox\`: Migrate to \`@suprsend/react\`** Development of the \[@suprsend/react-inbox]\([https://github.com/suprsend/suprsend-react-inbox](https://github.com/suprsend/suprsend-react-inbox)) SDK has been discontinued in favour of the new \[@suprsend/react]\([https://docs.suprsend.com/docs/headless-feed](https://docs.suprsend.com/docs/headless-feed)) SDK. Please refer the \[migration guide]\([https://docs.suprsend.com/docs/migration-guide-from-v1](https://docs.suprsend.com/docs/migration-guide-from-v1)) for instructions on transitioning to the new SDK. This Headless Inbox can be used if you want more control on the UI of Inbox Notifications like showing notifications in Sidepanel or in Fullscreen etc. ## Installation ```javascript npm theme={"system"} npm install @suprsend/react-inbox ``` ``` yarn add @suprsend/react-inbox ``` ## Adding SuprSend Inbox component ### SuprSendProvider component Enclose your notifications component in \*\*SuprSendProvider\*\*. SuprSend hooks can only be used in children of SuprSendProvider component ``` import { SuprSendProvider } from "@suprsend/react-inbox"; ``` | Props | Type | Description | | ------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | workspaceKey | string (Mandatory) | You can find it in SuprSend Dashboard inside \_Settings -> API Keys\_. | | distinctId | string (Mandatory) | Unique identifier for the user. | | subscriberId | string (Mandatory) | This is unique string for every distinctId used for authentication to inbox service. You check [generation docs](/docs/hmac-authentication) | | tenantId | string (Optional) | If you use multi-tenant architecture you can get inbox notifications for that specific tenant/brand only. | | pageSize | number (Optional) | Notifications to get in one API call. Used for pagination to get older notifications. Maximum allowed is 50. Defaults to 20. | | stores | Store (optional) | Pass stores array if you want to use [multi tab feature](/docs/multi-tabs) | **🚧 SuprSend uses socket.io to get realtime updates in inbox channel.** Make sure that you have only one active SuprSend's socket connection at a time (inspect -> network -> ws tab) to avoid unexpected issues. ### useUnseenCount hook This hook provides unSeenCount, markAllSeen which you can show on Bell icon. ``` import { useUnseenCount } from "@suprsend/react-inbox"; const { unSeenCount, markAllSeen } = useUnseenCount(); ``` | Returns | Type | Description | | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- | | unSeenCount | number | Use this variable to show the unseen notification count anywhere in your application. | | markAllSeen | method | Used to mark seen for all notifications. Call this method on clicking the bell icon so that it will reset the unSeenCount to 0. | ### useNotifications hook This hook is used to get notifications related data and method ``` import { useNotifications } from "@suprsend/react-inbox"; const {notifications, markClicked, markAllRead, initialLoading, hasNext, fetchPrevious, fetchMoreLoading } = useNotifications(storeId) ``` | Parameter | Type | Description | | --------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | storeId | string (Optional) | Optional parameter which return data of that specific store if you are using multi-tab feature. You can ignore this if you are not using multi-tab feature. | | Returns | Type | Description | | ---------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | notifications | IRemoteNotification\\\[] | Contains information related to notifications. This array can be looped to display notifications. | | markClicked | (id: string) => void | used to mark notification as clicked. | | markRead | (id: string) => void | used to mark notification as read. | | markUnRead | (id: string) => void | used to mark notification as unread. | | markArchived | (id: string) => void | used to mark notification as archived | | markAllRead | ( ) => void | Method used to mark all notifications as read. | | initialLoading | boolean | used for showing initial loader while fetching notifications first time | | hasNext | boolean | check if older notifications are present and then call \_fetchPrevious\_ method if its true to get older notifications. | | fetchPrevious | ( ) => void | used to get older notifications if you implement pagination, by checking \_hasNext\_ flag | | fetchMoreLoading | boolean | used to show loader while getting fetch older notifications. | ``` interface IRemoteNotification { n_id: string n_category: string created_on: number seen_on?: number interacted_on?: number tags?: string[] is_pinned?: boolean can_user_unpin?: boolean is_expiry_visible?: boolean expiry?: number archived?: boolean message: IRemoteNotificationMessage } interface IRemoteNotificationMessage { header?: string schema: string text: string url?: string open_in_new_tab?: boolean extra_data?: string actions?: { url: string, name: string, open_in_new_tab?: boolean}[] avatar?: { avatar_url: string; action_url?: string } subtext?: { text: string; action_url?: string } } ``` ### useStoreUnseenCount hook This hook can be used to get unread notifications count of a specific store, Use it only if you are using multi-tab. ``` import { useStoreUnseenCount } from "@suprsend/react-inbox"; const { unSeenCount } = useUnseenCount(); ``` ### useStoresUnseenCount hook This hook can be used to get unread notifications count of all stores, Use it only if you are using multi-tab ``` import { useStoresUnseenCount } from "@suprsend/react-inbox"; const { [storeId: string]: number } = useUnseenCount(); ``` ### useEvent hook This hook is an event emitter, takes arguments event type and callback function(called when event occurs). Must be called anywhere inside SuprSendProvider ``` import { useEvent } from "@suprsend/react-inbox"; useEvent(event_name: string, (notification: IRemoteNotification)=> void) ``` **Handled Event Names:** 1. **new\_notification:** Called when the new notification occurs, can be used to show toast notification in your application. ``` import { useEvent } from "@suprsend/react-inbox"; function Home() { useEvent("new_notification", (notificationData) => { alert("You have new notifications"); }); return

Home

; } ``` # Popover feed Source: https://docs.suprsend.com/docs/react-in-app-feed Guide to add In-App Popover feed in react-based websites using drop-in components. For react applications you can implement InApp feed using our Drop-In components (popover, full screen and side sheet notifications) or Build your own UI using headless react hooks. ### Pre-Requisite Integrate [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) as it is needed for creating SuprSend Client and authenticating user. ## Inbox (Popover Feed) The Inbox component comes with Bell, Badge and Popover that contains notifications and it should be child of [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider). This component is already wrapped in [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider) internally, so you don't have to wrap it again. ```javascript Example.jsx theme={"system"} import { Inbox, SuprSendProvider } from '@suprsend/react'; function Example() { return ( ); } ``` ```javascript TypeDef theme={"system"} interface InboxProps { tenantId?: string; stores?: IStore[] | null; // pass it if you want to use multiple tabs host?: { socketHost?: string, apiHost?: string, }; pageSize?: number; // defined page size defaults to 20, max value that can be passed is 100 pagination?: boolean; // pass false to disable pagination theme?: ITheme; // used to customise css styles of existing component themeType?: ThemeType; // dark or light popperPosition?: Placement; // placement of inbox popover on click of bell hideAvatar?: boolean; showUnreadCountOnTabs?: boolean; notificationClickHandler?: (notification: IRemoteNotification) => void; // callback executed on click of notification card primaryActionClickHandler?: (notification: IRemoteNotification) => void; // callback executed on click of primary action button secondaryActionClickHandler?: (notification: IRemoteNotification) => void; // callback executed on click of secondary action button bellComponent?: React.FC; // custom component for bell badgeComponent?: React.FC<{ count: number }>; // custom component for badge loaderComponent?: React.FC; // custom component for loader noNotificationsComponent?: React.FC; // custom component when no notifications are present tabBadgeComponent?: React.FC<{ count: number }>; notificationComponent?: React.FC; // custom notification card component headerRightComponent?: React.FC<{markAllRead: () => void,closeInboxPopover: () => void}>; // custom right side header component to override mark all as read etc } ``` Note: Refer [Customising Feed](https://docs.suprsend.com/docs/customising-feed) for detailed explanation on customisation options available in feed components. ![](https://files.readme.io/8134005a504eceaeeba615a8d9189cdc363e99a62f51be601e23177f8651c166-Screenshot_2025-01-14_at_5.50.46_PM.png) **Toast Notifications in @suprsend/react** In the `@suprsend/react` SDK, toast notification functionality is no longer included in Inbox by default. Developers who wish to use toast notifications will need to implement them separately to achieve similar functionality. This change allows for more flexibility and modularity in customizing the user experience. # Overview Source: https://docs.suprsend.com/docs/react-inbox-integration Ways to implement inbox feed functionality in React **End of support for [@suprsend/react-inbox](https://github.com/suprsend/suprsend-react-inbox/blob/main/docs/intergration.md). Migrate to `@suprsend/react`** We have upgraded authentication of inbox from HMAC to JWT as it is more secure. Please [migrate](/docs/react-migration-guide) to newer SDK if you are on old one. There are 2 ways in which you can implement inbox/feed functionality: * **Drop-in components:** Pre-built UI with many customizable options which require minimal effort to build. For setup please refer [docs](/docs/react-in-app-feed). * **Headless implementation:** For more advanced use cases where you want to build UI/UX from scratch. For setup please refer [docs](/docs/react-sdk-headless-feed). # Internationalization Source: https://docs.suprsend.com/docs/react-language-support Guide to translate Inbox content in user's locale. To enable internationalization in your inbox, you need to wrap your components in `SuprSendI18nProvider`. This provider will take care of loading the translations and providing them to your components. This context provider is present in both `@suprsend/react` and `@suprsend/react-core` packages. ```javascript Inbox theme={"system"} ``` ```javascript Feed theme={"system"} ``` `locale` is optional param which defaults to English language. We support translations for below languages internally. * `en` - [English](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/en.ts#L5) (default) * `fr` - [French](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/fr.ts#L5) * `de` - [German](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/de.ts#L5) * `es` - [Spanish](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/es.ts#L5) * `ar` - [Arabic](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/ar.ts#L5) If you want to use other languages that are not supported by us or to override strings of existing languages, you can pass `translations` prop to `SuprSendI18nProvider`. ```typescript Translations theme={"system"} interface ITranslations { notifications?: string; markAllAsRead?: string; noNotificationsTitle?: string; noNotificationsDescription?: string; pinned?: string; markAsUnread?: string; markAsRead?: string; archive?: string; expiresIn?: string; minute?: string; minutes?: string; hour?: string; hours?: string; day?: string; days?: string; week?: string; weeks?: string; month?: string; months?: string; year?: string; years?: string; } ``` ```javascript Example theme={"system"} ``` # Migration Guide Source: https://docs.suprsend.com/docs/react-migration-guide Step-by-step guide to migrate from `@suprsend/react-inbox` to `@suprsend/react`. Development of the [@suprsend/react-inbox](https://github.com/suprsend/suprsend-react-inbox) SDK has been discontinued in favour of the new [@suprsend/react ](https://docs.suprsend.com/docs/inapp-feed-1)SDK. Reason for this change is to bring all drop-in components for inbox and preferences, under one SDK. Currently `@suprsend/react-inbox` only contains inbox related code. ## Migrating from @suprsend/react-inbox to @suprsend/react changes Most of features that inbox component supports remain unchanged. Following are changes that happened in new SDK. ### Authentication changes Authentication mechanism has been changed from [HMAC authentication](https://docs.suprsend.com/docs/hmac-authentication) to [JWT authentication](https://docs.suprsend.com/docs/client-authentication) because HMAC authentication (subscriberId) is not as secure as that of JWT authentication. Earlier `SuprSendInbox`component and `SuprSendProvider` component(headless) used to take care of authentication if you pass `distinctId`, `workspaceKey` and `subscriberId`. But in new SDK you can use [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) to authenticate user and all other features like inbox, preferences, webpush etc can be accessed inside that context provide. ### Toast functionality has been removed `SuprSendInbox`used to have inbuilt toast notification feature using [react-toastify](https://fkhadra.github.io/react-toastify/introduction/) library. But the latest versions of this library created few peerDependencies which created issues for us. So we have exposed event listener to listen to new notification arrival and in that callback you will get new notification payload which you can use to show toast notification using any of your toast library. Refer [implementation](https://docs.suprsend.com/docs/toast-notifications) guide here. ## Steps to migrate `SuprSendInbox` component (Drop-in) ```shell npm theme={"system"} npm uninstall @suprsend/react-inbox npm install @suprsend/react ``` ```shell yarn theme={"system"} yarn remove @suprsend/react-inbox yarn add @suprsend/react ``` For authenticating user, follow integration steps of [SuprSendProvider](https://docs.suprsend.com/docs/react-1#integration). Import `Inbox` component and use it instead of `SuprSendInbox`. Remove `distinctId`, `workspaceKey` and `subscriberId` props since authentication is handled by SuprSendProvider in above step. Please remove any invalid prop present in existing component. Check all [available props](https://github.com/suprsend/suprsend-react-sdk?tab=readme-ov-file#inbox-popover) in new component. ```javascript Example.jsx theme={"system"} import SuprSendInbox from '@suprsend/react-inbox'; // remove it import { Inbox } from '@suprsend/react'; // old code function Example() { return (
); } // new code function Example() { return (
); } ```
Since default toast notification feature has been removed, you can refer this section to [integrate](https://docs.suprsend.com/docs/toast-notifications) toast notification functionality.
## Steps to migrate headless implementation If you are migrating headless version of inbox then its better to re-implement the headless inbox, as architecture of methods have been changed. Please refer this [guide](https://docs.suprsend.com/docs/headless-feed). Please reach out to use if you face any issues in migration. # Android Integration Source: https://docs.suprsend.com/docs/react-native-android-integration This document will cover integration steps for Android side of your ReactNative application. ## Installation ```javascript npm theme={"system"} npm install @suprsend/react-native-sdk@latest ``` ```javascript yarn theme={"system"} yarn add @suprsend/react-native-sdk@latest ``` Add the this dependency in project level **build.gradle**, inside **allprojects > repositories**. ```java build.gradle theme={"system"} allprojects { repositories { ... mavenCentral() // add this } } ``` ```java build.gradle theme={"system"} dependencies { ... implementation 'com.suprsend:rn:0.1.10' // add this } ``` **Note:** If you get any error regarding minSdkVersion please update it to 19 or more. ## Initialization Initialise the Suprsend android SDK in **MainApplication.java** inside `onCreate` method and just above `super.onCreate()` line. ```javascript javascript theme={"system"} import app.suprsend.SSApi; // import sdk ... SSApi.Companion.init(this, WORKSPACE KEY, WORKSPACE SECRET); // inside onCreate method just above super.onCreate() line ``` Replace **`WORKSPACE KEY`** and **`WORKSPACE SECRET`** with your workspace values. You will get them the tokens from **Settings -> API Keys** inside [Suprsend dashboard](https://app.suprsend.com/). ```javascript javascript theme={"system"} import suprsend from "@suprsend/react-native-sdk"; ``` ## Logging By default the logs of SuprSend SDK are disabled. You can enable the logs just in debug mode while in development by the below condition. ```javascript javascript theme={"system"} suprsend.enableLogging(); // available from v2.0.2 // deprecated from v2.0.2 suprsend.setLogLevel(level) suprsend.setLogLevel("VERBOSE") suprsend.setLogLevel("DEBUG") suprsend.setLogLevel("INFO") suprsend.setLogLevel("ERROR") suprsend.setLogLevel("OFF") ``` *** # Android Push (FCM) Source: https://docs.suprsend.com/docs/react-native-androidpush-integration Step-by-Step guide to setup FCM Push notifications in react native Android app. ## Integration steps To start sending notifications from FCM, you'll have to first create a firebase project. Create a firebase project and application in [firebase console](https://firebase.google.com/) with your applications package name which you can find in *MainApplication.java* or *AndroidManifest.xml*. You can get your Service Account JSON from Firebase Console Project Settings. Download *google-services.json* and add the file inside your *android > app* folder. Add the below dependency inside projects *build.gradle* inside dependencies ```groovy build.gradle theme={"system"} dependencies { ... classpath 'com.google.gms:google-services:4.3.10' // or latest version } ``` Add the below plugin inside the app *build.gradle* ```groovy build.gradle theme={"system"} apply plugin: 'com.google.gms.google-services' ``` Add the below dependency inside apps *build.gradle* inside dependencies ```groovy build.gradle theme={"system"} implementation("com.google.firebase:firebase-messaging:22.0.0") // or latest version ``` Push feature can be implemented in two ways: You may use this option if all of your android push notifications are to be handled via SuprSend SDK. We recommend you use this method as it is just a single-step process to just register the service in your application manifest and everything else will be ready. ```xml AndroidManifest.xml theme={"system"} ``` Once you get a token from Firebase you can pass the token by using the below code ```javascript javascript theme={"system"} suprsend.user.setAndroidFcmPush(fcm_token); ``` When you get a push notification you will get a payload and it can be passed to the method provided by Suprsend React Native SDK and the notification displaying part will be handled by SDK. ```javascript javascript theme={"system"} suprsend.showNotification(notification_payload); ``` If notification payload contains key **supr\_send\_n\_pl** then simply consider this as payload sent from suprsend and pass the payload to suprsend SDK by: if (payload?.data?.supr\_send\_n\_pl) \{ suprsend.showNotification(payload.data.supr\_send\_n\_pl); } ### Targeting Android 13 (API-33): In Android13 (API 33) or higher [notification permission](https://developer.android.com/develop/ui/views/notifications/notification-permission) will be disabled by default so permission needs to be asked to enable notifications if you are targeting android 13 users. You can follow this doc to update to support Andriod 13(API 33), if not already supported: [https://developer.android.com/about/versions/13/setup-sdk](https://developer.android.com/about/versions/13/setup-sdk). Please test the application as well as upgrading to API 33 may causes breaking changes. ```xml AndroidManifest.xml theme={"system"} ... ``` This method is asynchronous and returns permission values like granted, denied, and never\_ask\_again. NOTE: This method is asynchronous and returns permission values like granted, denied, and never\_ask\_again. ```javascript App.js theme={"system"} const permission = await Suprsend.askNotificationPermission(); ``` Once permission is granted users can be able to get push notifications. *** # Manage Users Source: https://docs.suprsend.com/docs/react-native-create-user Methods to create user and set their mobile push token and other communication channels for sending notifications in ReactNative application. ## How Suprsend identifies a user SuprSend identifies users with immutable `distinct_id`. It's best to map the same identifier in your DB with `distinct_id` in SuprSend. Do not use identifiers that can be changed like email or phone number. You can view synced users by searching `distinct_id` on [Users page](https://app.suprsend.com/en/production/users). ## Identify user and Set Push token You can identify a user using the`suprsend.identify()` method. iospsuh token is automatically set in user's profile when this method is called. Call this method as soon as you know the identity of user, that is after login authentication. If you don't call this method, user will be identified using distinct\_id (uuid) that SDK generates internally. We internally create an event called **`$user_login`**. You can see this event on SuprSend workflows event list and you can configure a workflow on it. ```javascript javascript theme={"system"} suprsend.identify(distinct_id); //Sample Values suprsend.identify("291eaa13-62f5-4d52-b2dd"); ``` | Parameters | Type | Description | | ---------------- | ------------------------- | ----------------------------------------------------------------------------------- | | **distinct\_id** | int, bigint, string, UUID | *mandatory* Unique identifier for a user across devices or between multiple logins. | As soon as the user logs out, call `suprsend.reset()` method to clear data attributed to a user. This allows you to handle multiple user logins on a single device and keep their data isolated from each other. ```javascript javascript theme={"system"} suprsend.reset(); ``` Don't forget to call reset on user logout. If not called, users' distinct\_id will not reset and multiple tokens and channels will get added to the distinct\_id who logged in first on the device. ## Set communication channels Mobile Push tokens automatically gets updated in user profile on `suprsend.identify()` call. All you have to do is to integrate [push notification ](/docs/flutter-androidpush-integration)service in your application to start sending mobile push notification. To set other communication channels, use below methods: Add user channels on signup, or whenever a user updates their communication channels in the application flow. ```javascript Javascript theme={"system"} suprsend.user.setEmail("user@example.com"); // To add Email suprsend.user.setSms("+91XXXXXXXXXX"); // To add SMS, mandatory to pass country code suprsend.user.setWhatsApp("+91XXXXXXXXXX"); // To add Whatsapp, mandatory to pass country code ``` ``` suprsend.user.unSetEmail("user@example.com"); // To remove Email suprsend.user.unSetSms("+91XXXXXXXXXX"); // To remove SMS suprsend.user.unSetWhatsApp("+91XXXXXXXXXX"); // To remove Whatsapp ``` ## Set user properties (*currently not available in iOS SDK*) You can sync other user properties in SuprSend and use it to pass dynamic content in template or add in workflow conditions. Set is used to add property. Set is upsert function and will override existing values corresponding to a key. ```javascript javascript theme={"system"} // for single property suprsend.user.set(key, value); suprsend.user.set("name", "john doe"); // for multiple properties suprsend.user.set(property_obj); suprsend.user.set({"name" : "john doe", "age" : "27"}); ``` Key should not start with **`$`** or **`ss_`**, as we have reserved these symbols for our internal events and property names. Works just like `user.set`, except it will not overwrite existing property values. This is useful for properties like *First login date* ```javascript javascript theme={"system"} suprsend.user.setOnce(key, value); // for single property suprsend.user.setOnce(properties); // for multiple properties // Sample values for setting once a single property: suprsend.user.setOnce("first_login", "2021-11-02"); // Sample values for setting once multiple properties: suprsend.user.setOnce({"first_login" : "2021-11-02", "DOB" : "1991-10-02"}); ``` This will remove a property key ```javascript javascript theme={"system"} suprsend.user.unSet(key); // for single property suprsend.user.unSet(property_list); // for multiple properties // Sample values for unsetting a single property: suprsend.user.unSet("wishlist"); // Sample values for unsetting multiple properties: suprsend.user.unSet(["wishlist", "cart"]); ``` This method will append a value to an array ```javascript javascript theme={"system"} suprsend.user.append(key, value); suprsend.user.append(property_obj); // for multiple properties // Sample values for appending a single property: suprsend.user.append("wishlist", "iphone12"); // Sample values for appending multiple properties: suprsend.user.append({"wishlist" : "iphone12", "wishlist" : "Apple airpods"}); ``` This will remove value from an array but not remove property key ```javascript javascript theme={"system"} // for single property suprsend.user.remove(key, value); suprsend.user.remove("wishlist", "iphone12"); // for multiple properties suprsend.user.remove(property_obj); suprsend.user.remove({"wishlist" : "iphone12", "wishlist": "Apple airpods"}); ``` Increase or decrease integer values on consecutive action, like login count. To reduce a property, provide a negative number for the value. ```javascript javascript theme={"system"} // for single property suprsend.user.increment(key, value); suprsend.user.increment("login_count", 1); // for multiple properties suprsend.user.increment(property_obj); suprsend.user.increment({"login_count" : 1, "order_count" : 1}); // Sample values for incrementing a single property: // Sample values for incrementing multiple properties: ``` *** # iOS Integration Source: https://docs.suprsend.com/docs/react-native-ios-integration This document will cover integration steps for iOS side of your ReactNative application. ## Installation ```javascript npm theme={"system"} npm install @suprsend/react-native-sdk@latest ``` ```javascript yarn theme={"system"} yarn add @suprsend/react-native-sdk@latest ``` If you are upgrading the SDK version, run `pod update` inside **ios** folder. This step is needed only if your react native project iOS side doesn't support swift language. To check for swift support in Xcode, open `Project Settings --> Build Settings --> Swift Language Version` (attached image). If entry is present, you already have swift support in your project. 2880 If your project doesn't support swift then its mandatory to add Swift support in your project by following the steps below. * Open "xcodeworkspace" file inside iOS folder. This will open Xcode. * Add Swift file to project like shown below. 2880 * Select **Swift File** and click on **`Next`** button. * Now give a suitable name to your file and click Create. * After that popup will be shown asking to configure Objective-C bridge header. Click on **`Create Bridge Header`**. ![](https://files.readme.io/4e4d8ea-Screenshot_2022-05-19_at_6.02.20_PM.png) Thats it! your React native iOS project can now understand code written in Swift language. ```ruby PodFile theme={"system"} platform :ios, '13.0' // this version has to be 13 or greater ``` ``` pod install ``` SuprSend SDK needs an iOS deployment target of 11 or above, update the target as given in below image if needed. ## Initialization In **AppDelegate** add the below code inside `didFinishLaunchingWithOptions` method, just before last returning line. Refer any one of code snippet below based on your projects AppDelegate file language. ```objectivec AppDelegate.m theme={"system"} #import //add this ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... SuprSendSDKConfiguration* configuration = [[SuprSendSDKConfiguration alloc] initWithKey:@workspace_key secret:@workspace_secret]; // add this line [SuprSend.shared configureWithConfiguration:configuration launchOptions:launchOptions]; // add this line return YES; ``` ```swift AppDelegate.swift theme={"system"} import SuprSendSdk // Add this ... override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { ...... // Add below 2 lines let suprSendConfiguration = SuprSendSDKConfiguration(withKey: "your_workspace_key", secret:"your_workspace_secret") SuprSend.shared.configureWith(configuration: suprSendConfiguration, launchOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } ``` Replace the **`workspace_key`** and **`workspace_secret`** with your workspace values. You will get both the tokens from **Settings -> API Keys** in [SuprSend dashboard](https://app.suprsend.com/). ## Logging By default the logs of SuprSend SDK are disabled. You can enable the logs just in debug mode while in development by the below condition. ```javascript javascript theme={"system"} suprsend.enableLogging(); // available from v2.0.2 // deprecated from v2.0.2 suprsend.setLogLevel(level) suprsend.setLogLevel("VERBOSE") suprsend.setLogLevel("DEBUG") suprsend.setLogLevel("INFO") suprsend.setLogLevel("ERROR") suprsend.setLogLevel("OFF") ``` *** # iOS Push Setup Source: https://docs.suprsend.com/docs/react-native-ios-push-integration Step-by-step guide to setup APNS iOSpush notifications in your react native app. **Major Release (V 1.0.0)** 🚧 Starting from iOS version v1.0.0, we have introduced explicit push notification permission and option to add images in your notification. Also, introduced background mode for improved tracking of notification delivery. If you are using an iOS version older than v1.0.0 and upgrading to the new version. Please ensure to use the latest integration steps, especially for below methods: 1. [Adding Background mode capability (point 3 of capabilities)](/docs/react-native-ios-push-integration) 2. [Calling registerPush method](/docs/react-native-ios-push-integration) 3. [Tracking delivery methods](/docs/react-native-ios-push-integration) 1. Inside Targets select **signing and capabilities** 2. Click on **+capabilities** and select **Push Notifications** and **Background Modes** In Background Modes, select Remote Notifications option. We use background notifications to receive delivery reports when your app is in quit and background state. Refer [doc](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app) to know more about background notification Call *registerForPushNotifications* method below the SuprSend SDK initialised code which will register the iOS device for push service. If you have AppDelegate.m file then make follow code changes given in AppDelegate.h and AppDelegate.m file. If you have AppDelegate.swift file then make follow code changes given in AppDelegate.swift file. ```objectivec AppDelegate.h theme={"system"} #import // Add this @interface AppDelegate : UIResponder // Add UNUserNotificationCenterDelegate in the already existing line ``` ```objectivec AppDelegate.m theme={"system"} #import // Add this // ... existing code ... [SuprSend.shared configureWithConfiguration:configuration launchOptions:launchOptions]; // init code which is already added at time of initialisation UNAuthorizationOptions options = UNAuthorizationOptionAlert+UNAuthorizationOptionBadge+UNAuthorizationOptionSound; // Add this [SuprSend.shared registerForPushNotificationsWithOptions:options]; // Add this ``` ```swift AppDelegate.swift theme={"system"} import UserNotifications // Add this // extend AppDelegate with UNUserNotificationCenterDelegate class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate { override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // ... existing code ... SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: launchOptions) // init code which is already added at time of initialisation var options: UNAuthorizationOptions = [.badge, .alert, .sound] // Add this SuprSend.shared.registerForPushNotifications(options: options) // Add this } } ``` There are 2 ways in which your app can prompt the users to allow push notifications on their device: Explicit authorization allows you to display alerts, add a badge to the app icon, or play sounds whenever a notification is delivered. In this type of authorization, the request is made the first time user launches your app. If the user denies the request, you can't send subsequent prompts to send the notification. Explicit authorization is our default authorization method as it automatically sets alert, sound and badge as soon as the user allows this request. Provisional notifications are sent quietly to the users —they don’t interrupt the user with a sound or banner. Also, they will not be shown when your app is in foreground. First time this type of notifications are sent, user is asked to "Keep" or "Turn off" the notifications. If they click on "Keep", the further notifications continue to be sent Add below code in `AppDelegate` file for provisional authorization. ```objectivec AppDelegate.m theme={"system"} [SuprSend.shared configureWithConfiguration:configuration launchOptions:launchOptions]; // init code which is already added at time of initialisation UNAuthorizationOptions options = UNAuthorizationOptionAlert+UNAuthorizationOptionBadge+UNAuthorizationOptionSound; // Add this if (@available(iOS 12.0, *)) { options = options | UNAuthorizationOptionProvisional; } [SuprSend.shared registerForPushNotificationsWithOptions:options]; // Add this ``` ```swift AppDelegate.swift theme={"system"} SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: launchOptions) // init code which is already added at time of initialisation var options: UNAuthorizationOptions = [.badge, .alert, .sound] // If you want to use provisional authorization if #available(iOS 12.0, *) { options = [.badge, .alert, .sound, .provisional] } SuprSend.shared.registerForPushNotifications(options: options) ``` To enable sending iOS APNS token to SuprSend backend, delivery and tracking of push notification delivery/clicks/dismiss events, add below 4 methods in **AppDelegate file** after last existing method. ```objectivec AppDelegate.m theme={"system"} - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSUInteger dataLength = deviceToken.length; if (dataLength == 0) { return; } const unsigned char *dataBuffer = (const unsigned char *)deviceToken.bytes; NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; for (int i = 0; i < dataLength; ++i) { [hexString appendFormat:@"%02x", dataBuffer[i]]; } [SuprSend.shared setPushNotificationTokenWithToken:hexString]; } - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{ if (@available(iOS 14.0, *)) { completionHandler(UNAuthorizationOptionSound | UNNotificationPresentationOptionBanner | UNAuthorizationOptionBadge); } else { completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge); } } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler API_AVAILABLE(ios(7.0)){ [SuprSend.shared application:application didReceiveRemoteNotification:userInfo]; completionHandler(UIBackgroundFetchResultNewData); } - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler{ if ([response isSuprSendNotification]) { [SuprSend.shared userNotificationCenter:center didReceive:response]; } completionHandler(); } @end // before this line ``` ```swift AppDelegate.swift theme={"system"} override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() SuprSend.shared.setPushNotificationToken(token: token) // Send APNS Token to SuprSend } @available(iOS 10.0, *) func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { if response.isSuprSendNotification() { SuprSend.shared.userNotificationCenter(center, didReceive: response) } completionHandler() } override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]){ SuprSend.shared.application(application, didReceiveRemoteNotification: userInfo) } @available(iOS 10.0, *) func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { completionHandler([.alert, .badge, .sound]) } } ``` iOS Push notifications only work on real devices so while developing/testing use real device to test it instead of simulators For better notification status (delivered, seen) tracking this step is needed. 1. In Xcode go to **File > New > Target**. 2. Select Notification Service Extension from the template list. 3. Then in Next popup give it any product name, select your team, select swift language and click finish. After clicking on "Finish", a folder will be created with your given product name. Inside that there will be **NotificationService.swift** file like below. In your project podFile add following snippet at the end of existing code like shown in image. Replace **\** with name you given to notification service while creating it. After that Run `pod install`. ```yaml podFile theme={"system"} target '' do pod 'SuprsendCore' pod 'SuprSendSdk' end ``` Replace the content in **NotificationService.swift** file with below code. In this snippet on line 11, 12 replace values with your workspace key and workspace secret. ```swift NotificationService.swift theme={"system"} import UserNotifications import UIKit class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var modifiedNotificationContent: UNMutableNotificationContent? private func track(request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { let suprSendConfiguration = SuprSendSDKConfiguration( withKey: "your workspace key", secret: "your workspace secret" ) SuprSend.shared.configureWith(configuration: suprSendConfiguration , launchOptions: [:]) SuprSend.shared.didReceive(request, withContentHandler: contentHandler) } override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler modifiedNotificationContent = (request.content.mutableCopy() as? UNMutableNotificationContent) track(request: request, withContentHandler: contentHandler) if let modifiedNotificationContent = modifiedNotificationContent { // Modify the notification content here... // 1 guard let imageURLString = modifiedNotificationContent.userInfo["image_url"] as? String else { contentHandler(modifiedNotificationContent) return } getMediaAttachment(for: imageURLString) { [weak self] image in guard let self = self, let image = image, let fileURL = self.saveImageAttachment( image: image, forIdentifier: "attachment.png") else { contentHandler(modifiedNotificationContent) return } let imageAttachment = try? UNNotificationAttachment( identifier: "image", url: fileURL, options: nil) if let imageAttachment = imageAttachment { modifiedNotificationContent.attachments = [imageAttachment] } contentHandler(modifiedNotificationContent) } } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = modifiedNotificationContent { contentHandler(bestAttemptContent) } } } extension NotificationService { private func saveImageAttachment(image: UIImage, forIdentifier identifier: String ) -> URL? { let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()) let directoryPath = tempDirectory.appendingPathComponent( ProcessInfo.processInfo.globallyUniqueString, isDirectory: true) do { try FileManager.default.createDirectory( at: directoryPath, withIntermediateDirectories: true, attributes: nil) let fileURL = directoryPath.appendingPathComponent(identifier) guard let imageData = image.pngData() else { return nil } try imageData.write(to: fileURL) return fileURL } catch { return nil } } private func getMediaAttachment(for urlString: String, completion: @escaping (UIImage?) -> Void ) { // 1 guard let url = URL(string: urlString) else { completion(nil) return } let task = URLSession.shared.dataTask(with: url) { data, response, error in if error != nil { completion(nil) return } guard let data = data else { completion(nil) return } guard let image = UIImage(data: data) else { completion(nil) return } completion(image) } task.resume() } } ``` You are now all set to send push notifications. All you have to do is add [iOS vendor configuration](/docs/ios-push-vendor-integration) on SuprSend dashboard and your push notifications will be configured. Please refer [vendor integration guide](/docs/ios-push-vendor-integration) to integrate your apns push service *** # Sync Events Source: https://docs.suprsend.com/docs/react-native-send-event-data Methods for sending events from your react native app to the SuprSend platform to trigger workflows. In this section, we'll cover how to send events to the Suprsend platform for both iOS and Android application ## Pre-Requisites 1. [Create User](/docs/react-native-create-user)- Mandatory to pass in event trigger ## Sending Events to SuprSend You can setup events on user actions in your app and configure workflows on top of it that triggers when the corresponding event is passed through App. Variables added in the template or workflow should be passed as event `properties` You can send Events from your app to SuprSend platform by using `suprsend.track()` method. ```javascript javascript theme={"system"} suprsend.track(event_name, properties); suprsend.track("grocery_purchased", {"item":"olive oil","amount":"$12"}); ``` Event Name or Property Name should not start with **`$`** or **`ss_`**. These keywords are reserved for internal events and property names. ### System Events tracked by SuprSend There are some system events tracked by SuprSend SDK by default. These are some basic events, as well as events that are necessary for tracking notifications related activity (like delivered, clicked, etc). You are not required to do anything here. | Event Name | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \$app\_installed | \$app\_installed will get tracked when user launch his app for the first time. FYI cases in which it will also get called 1. When user launches his app for first time. 2. When user uninstall the app and installs it again. 3. \[Multiple device login ]When user launch app for first time on different devices. 4. When user clears the app cache and relaunches the app. | | \$app\_launched | Gets tracked when user launches the app each time. | | \$user\_login | Gets tracked when user logs in inside the app | | \$user\_logout | Gets tracked when user logs in to the app | | \$notification\_delivered | Will get tracked when the suprsend notification payload is received at SDK end. | | \$notification\_clicked | Will get tracked when user either clicks the notification body or any action button in the notification. | | \$notification\_dismissed | Will get tracked when user dismisses the notification by left swiping the notification or by clicking on "Clear All" button | ## Advanced concepts ### 1. Super Properties Super Properties are data that are always sent with events data. These super properties will be sent in each event after calling this method. Super Properties will be stored in local storage, and will persist across invocations of app. There are some super properties that SuprSend SDK will send by default. Developer can set custom super properties as well with `suprsend.setSuperProperties()` method ```javascript javascript theme={"system"} //method suprsend.setSuperProperties(key, value); suprsend.setSuperProperties(property_obj); //Example suprsend.setSuperProperties("Location", "Banglore"); suprsend.setSuperProperties({"Location": "Banglore","Pincode": 1234567}); ``` Default Super Properties tracked by SuprSend SDK: | Super Property | Description | Sample Value | | ---------------------- | -------------------------------------------- | ---------------- | | \$app\_version\_string | Version of your app | 0.0.1 | | \$app\_build\_number | Build number of your app | 2 | | \$os | Operating system of the user | android | | \$manufacturer | Manufacturer of the user's device | OnePlus | | \$brand | Brand of the user's device | OnePlus | | \$model | Model of the user's device | GM1901 | | \$deviceId | Device id | 89eead05a0150146 | | \$ss\_sdk\_version | SuprSend SDK version | 0.1.31 | | \$network | Network on which the user is | wifi | | \$connected | Whether the user is connected to the network | true | There are unset custom super properties with `suprsend.unSetSuperProperty()` method. This method will stop calling that property with every event trigger. ```javascript javascript theme={"system"} suprsend.unSetSuperProperty(key); suprsend.unSetSuperProperty("Location"); ``` ### 2. Flush Events SuprSend SDK automatically flushes events at an interval of 5 seconds, and on certain activities like app relaunch, etc. If you wish to flush a time sensitive event to SuprSend immediately, you can use the `suprSendApi.flush()` method. All the system tracked events are flushed immediately ```javascript javascript theme={"system"} suprSendApi.flush(); ``` *** # SDK Integration Source: https://docs.suprsend.com/docs/react-sdk SDK Integration to enable SuprSend features like Inbox, Preferences, and Webpush into React-based web applications. ## Installation We support 2 SDK's for react based applications. * [@suprsend/react-core](https://www.npmjs.com/package/@suprsend/react-core): This provides context providers and hooks to integrate SuprSend in to your application. If you want to use web-push, user methods, track events or implement your own UI for preferences and inbox by using provided methods, this library is better option. If you want to use any of inbuilt components for inbox or preferences then use `@suprsend/react`. * [@suprsend/react](https://www.npmjs.com/package/@suprsend/react): This library is built on top of `@suprsend/react-core`, so all hooks, context providers and methods that are present in `@suprsend/react-core` library are also present in this, with addition to that drop-in components like Inbox, NotificationsFeed, Preferences etc are available which comes with UI to ease integration. ```javascript npm theme={"system"} npm install @suprsend/react ``` ```javascript yarn theme={"system"} yarn add @suprsend/react ``` ## Integration ### SuprSendProvider This context provider need to be wrapper around your component in which you want to use SuprSend methods. This is responsible for creating client instance(`new SuprSend()`), identify and reset user. You can access the SuprSend client instance using `useSuprSendClient` hook. This instance contains all methods needed to integrate preferences, webpush, track events and user methods. ```javascript Example.js theme={"system"} import { SuprSendProvider } from '@suprsend/react'; function Example() { return ( ); } ``` ```javascript TypeDef theme={"system"} interface SuprSendProviderProps { publicApiKey: string; distinctId?: unknown; userToken?: string; host?: string; vapidKey?: string; swFileName?: string; refreshUserToken?: (oldUserToken: string, tokenPayload: Dictionary) => Promise; userAuthenticationHandler?: ({ response: ApiResponse }) => void; } ``` | Parameter | Description | | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | publicApiKey | public API Key is mandatory field without which error will be thrown by SuprSendProvider. You can get this from [SuprSend Dashboard](https://app.suprsend.com/en/staging/developers/api-keys). | | distinctId | Unique identifier to identify a user across platform. If a value is passed SDK will create user and authenticate user. If null value is passed authenticated user's instance data will be cleared in your application, kind of logout. | | [userToken](https://docs.suprsend.com/docs/client-authentication) | Mandatory when enhanced security mode is on. This is ES256 JWT token generated in your server-side. Refer [docs](https://docs.suprsend.com/docs/client-authentication) to create userToken. | | refreshUserToken | This function is called by SDK internally to get new userToken before existing token is expired. The returned JWT token string is used as the new userToken. | | userAuthenticationHandler | This callback will be called after authenticating user internally when you pass distinctId field to give you back the response of user creation API call. | | host | Customise the host url. | | vapidKey | This key is needed only if you are implementing WebPush notifications. You can get it in SuprSend Dashboard --> Vendors --> WebPush | | swFileName | This key is needed only if you are implementing WebPush notifications and want to customise default `serviceworker.js` file name with your own service worker file name. | After implementing the above SuprSendProvider you can be able to use all SuprSend features. ### useSuprSendClient This hook is used to access internal SuprSend client instance which has all methods related to webpush, preferences, user methods and track event. Use this hook inside child of SuprSendProvider. ```javascript Example.js theme={"system"} import {SuprSendProvider, useSuprSendClient} from "@suprsend/react" function Example() { return ( ); } function MyComponent() { const suprSendClient = useSuprSendClient(); return (

{ // suprSendClient.track('testing'); // suprSendClient.user.setEmail('johndoe@gmail.com') // suprSendClient.webpush.registerPush() // suprSendClient.user.preferences.getPreferences() }} > Click Me

); } ```
### useAuthenticateUser This hook is used to get authenticated user anywhere in your application inside SuprSendProvider. This can also be used to check if user is authenticated before calling any method of SuprSend. ```javascript Example.js theme={"system"} import { useAuthenticateUser } from '@suprsend/react'; function MyComponent() { const { authenticatedUser } = useAuthenticateUser(); useEffect(() => { if (authenticatedUser) { console.log('User is authenticated', authenticatedUser); } }, [authenticatedUser]); return

Hello world

; } ```
# Build your own feed UI (headless) Source: https://docs.suprsend.com/docs/react-sdk-headless-feed A comprehensive guide to integrating a feed using React hooks to design and build a custom UI for your feed in a React app To implement your own UI for Feed in your react application we provide context providers and hooks. If you are not using any of the [In-build UI components](https://github.com/suprsend/suprsend-react-sdk?tab=readme-ov-file#suprsend-react-sdk) while designing your custom UI for feed, you can use [@suprsend/react-core](https://github.com/suprsend/suprsend-react-core) SDK. These hooks and context providers are available in both `@suprsend/react-core` and `@suprsend/react` SDK's. Integration steps mentioned in this guide are also same for both. ### Pre-Requisite Integrate [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) as it is needed for creating SuprSend Client and authenticating user. ## Installation ```javascript npm theme={"system"} npm install @suprsend/react-core ``` ``` yarn add @suprsend/react-core ``` In `@suprsend/react-core` v1.0.0, we have changed the `pageInfo` object data. Please refer to [Migration guide](https://docs.suprsend.com/docs/migration-guide-from-v1#migrating-to-v4-from-v3) for more details. ## Integration ### SuprSendFeedProvider This provider internally creates InApp feed client instance and also removes it on unmounting. This should be called inside [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider). FeedClient can be accessed using [useFeedClient](https://docs.suprsend.com/docs/headless-feed#usefeedclient) hook. ```javascript Example.jsx theme={"system"} import { SuprSendProvider, SuprSendFeedProvider } from '@suprsend/react-core'; // import { SuprSendProvider, SuprSendFeedProvider } from '@suprsend/react'; // if you use our inbuilt react components function Example() { return ( Your Feed Component ); } ``` ```javascript TypeDef theme={"system"} interface SuprSendFeedProviderProps { tenantId?: string; pageSize?: number; stores?: IStore[] | null; host?: { socketHost?: string, apiHost?: string }; } ``` ### useFeedClient This hook is used to get Feed client instance. Using this instance you can access [feed methods](https://docs.suprsend.com/docs/inapp-feed#other-methods) like mark seen, archive, read etc. Use this hook inside [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider). ```javascript Example.jsx theme={"system"} import {useFeedClient} from '@suprsend/react-core'; // import { useFeedClient } from '@suprsend/react'; // if you use our inbuilt react components function MyComponent() { const feedClient = useFeedClient(); return (

{ feedClient.markAsSeen(notificationId); }} > Click Me

); } ```
### useFeedData This hook returns react state that contains notification store data which includes notifications list and other meta data. This state gets updated internally when there is any update in store. Use this state to render UI in your application. Use this hook inside [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider). ```javascript Example.jsx theme={"system"} import {useFeedData} from "@suprsend/react-core" // import { useFeedData } from '@suprsend/react'; // if you use our inbuilt react components function MyComponent() { const feedData = useFeedData(); const notificationsList = feedData.notifications; return
{notificationsList.map(notification)=>

{notification.n_id}

}
; } ```
### Example ```javascript Example.jsx theme={"system"} import { SuprSendProvider, SuprSendFeedProvider, useFeedData, useFeedClient, } from "@suprsend/react-core"; function Example() { return ( ); } function MyFeedComponent() { const feedData = useFeedData(); const feedInstance = useFeedClient(); if (!feedData) return null; if (feedData.apiStatus === "LOADING") return

Loading Data

; if (feedData.apiStatus === "SUCCESS" && !feedData?.notifications?.length) { return

No Notifications

; } if (feedData.notifications) { return (
{feedData.notifications.map((notification) => { return (
{ feedInstance.markAsRead(notification.n_id); }} > {notification.n_id}
); })}
{feedData.apiStatus === "FETCHING_MORE" ? (

Loading More

) : (
{feedData.pageInfo.hasMore && ( )}
)}
); } return null; } ```
## Using built-in components in headless implementation We exported few inbuilt components you can use to save time while building your own UI. If you are using `@suprsend/react-core` and want to use these components please remove that package and install `@suprsend/react`, integration steps remain unchanged other than changing import statement. ### NotificationCard component This is single notification card component. It will be handy if you want to implement your own UI but want to just use our notification card as it has a bit complex logic and UI. ```javascript Example.jsx theme={"system"} import { NotificationCard } from '@suprsend/react'; ``` ``` interface NotificationCardProps { notificationData: IRemoteNotification; notificationClickHandler?: (notificationData: IRemoteNotification) => void; notificationComponent?: React.FC; hideAvatar?: boolean; themeType?: ThemeType; primaryActionClickHandler?: (notification: IRemoteNotification) => void; secondaryActionClickHandler?: (notification: IRemoteNotification) => void; theme?: INotificationCardTheme; } ``` > Please refer [Customising CSS styles](https://docs.suprsend.com/docs/customising-feed#customising-css-styles) section to view typedefs for `INotificationCardTheme`. ### BodyMarkdown component This is simple div element but also supports rendering of markdown language. [Read more](https://docs.suprsend.com/docs/customising-feed#bodymarkdown-component) about the component. #### Sample Notification JSON Structure ```json theme={"system"} { "tenant_id": "default", "is_expiry_visible": false, "seen_on": 1754346745613, "read_on": 1754346745613, "is_pinned": false, "archived": false, "created_on": 1753966489304, "n_category": "transactional", "interacted_on": 1754346745613, "n_id": "01K1G8SBGS4A1QG2CGYSVXQ517", "message": { "schema": "1.0", "header": "Your invoice for {{event.billing_month}} is ready", "text": "An invoice of **{{event.amount}}** has been generated for your workspace {{event.workspace_name}}.", "subtext": { "text": "View full billing history", "action_url": "https://app.suprsend.com/billing/history" }, "avatar": { "avatar_url": "https://cdn-icons-png.flaticon.com/512/3144/3144456.png", "action_url": "https://app.suprsend.com/settings" }, "url": "https://app.suprsend.com/billing/{{event.invoice_id}}", "extra_data": "{\n \"workspace\": \"{{event.workspace_name}}\"\n}", "tags": [ "billing", "finance" ], "expiry": { "format": "absolute", "expiry_type": "fixed", "is_expiry_visible": true, "value": "2025-09-30T23:59:59Z" }, "is_pinned": true, "is_expiry_enabled": true, "actions": [ { "url": "https://app.suprsend.com/billing/{{event.invoice_id}}", "name": "View Invoice" }, { "url": "https://app.suprsend.com/billing/pay/{{event.invoice_id}}", "name": "Pay Now" } ] } } ``` **Fields and its description** **Top-level Fields** | Field | Type | Description | | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `n_id` | string | Unique notification ID for a message. | | `n_category` | string | [Category of the notification](/docs/notification-category). Used to fetch and apply category-level preferences (e.g., `transactional`, `marketing`). You can also use categories to group notifications in different tabs. | | `created_on` | number | Timestamp (epoch ms) when the notification reached the inbox. | | `seen_on` | number | Timestamp (epoch ms) when the notification was first seen. | | `read_on` | number | Timestamp (epoch ms) when the notification was marked as read using the `markAsRead` method. | | `interacted_on` | number | Timestamp (epoch ms) when the user clicked or interacted with the notification. | | `message` | object | Payload containing the actual notification content and metadata. | | `tenant_id` | string | [Tenant identifier](/docs/tenants). Used to identify the tenant in a multi-tenant setup. | | `is_pinned` | boolean | Whether the notification is pinned in the inbox. Pinned notifications remain fixed at the top of the inbox feed. Commonly used for critical messages that should not scroll down as new notifications arrive. | | `archived` | boolean | Whether the notification has been archived by the user. Archived notifications are hidden from the inbox feed by default unless you explicitly pass `is_archived = false` in the [store config](docs/multi-tabs). | | `is_expiry_visible` | boolean | Whether the notification’s expiry is visible to the user. Expiry details are found inside the `message` object. Useful when you want notifications to auto-delete after a certain time. | **`message` Object Fields** | Field | Type | Description | | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `schema` | string | Schema version for the message format. | | `header` | string | Main title/header of the notification. | | `text` | string | Body text of the notification; supports Markdown and HTML content. Default format is markdown. | | `subtext` | object | Secondary text with optional `action_url`. Is visible below the main text. Generally used to show additional information or footer text. | | `avatar` | object | Avatar image shown with the notification, generally used to show sender's profile picture; can include an action link. | | `url` | string | URL where user will be redirected when they click the notification. | | `extra_data` | string | JSON string for passing additional data that can be used to design custom notification card UI. | | `tags` | array | List of tags to classify the notification (e.g., `feature_launch`, `mentions`). Generally used to filter and organize notifications inside [multiple tabs](/docs/multi-tabs). | | `is_expiry_enabled` | boolean | Whether expiry handling is enabled for this notification. Expiry is set for notifications that are supposed to auto delete after a certain time (e.g., upcoming maintenances, events, etc.). Used to send notifications like upcoming maintenances, events, etc. | | `expiry` | object | Expiry configuration (format, type, visibility, value) set when `is_expiry_enabled` is true. | | `is_pinned` | boolean | Whether this message should pin at top of the notification feed. Generally used to send critical notifications to the user that you don't want to go down when new notifications are added. | | `actions` | array | List of call-to-action buttons (each with `url` and `name`). | You can understand more about usage and details of message fields in it's [template documentation](/docs/in-app-inbox-template). # Toast Notifications Source: https://docs.suprsend.com/docs/react-toast-notifications Integration steps to show a toast when new notification arrives, in react application. **Toast Notifications in @suprsend/react** In the `@suprsend/react` SDK, toast notification functionality is no longer included in Inbox by default. Developers who wish to use toast notifications will need to implement them separately to achieve similar functionality. This change allows for more flexibility and modularity in customizing the user experience. SDK exposes event listener `feed.new_notification` which is available on feedClient. You can use this listener to show toast notification by using your own toast library. We also export `ToastNotification` component, which is a light version of `NotificationCard` that can be used by your toast library. The following example snippet uses [react-hot-toast](https://react-hot-toast.com/). ```javascript Example.jsx theme={"system"} import toast, { Toaster } from 'react-hot-toast'; import { SuprSendFeedProvider, Inbox, useFeedClient } from '@suprsend/react'; // drop-in Inbox component example function Example() { return ( ); } // example when using SuprSendFeedProvider provider in headless or fullscreen feed function HeadlessExample() { return ( ); } function ToastNotification() { const feedClient = useFeedClient(); useEffect(() => { if (!feedClient) return; // event listener which return new notification data feedClient.emitter.on('feed.new_notification', (data) => { toast.custom(); // show toast with new notification data feedClient.markAsSeen(data.n_id); // marking seen }); return () => { feedClient.emitter.off('feed.new_notification'); }; }, [feedClient]); return ; } ``` ```javascript TypeDef theme={"system"} interface ToastNotificationProps { notificationData: IRemoteNotification; hideAvatar?: boolean; themeType?: ThemeType; theme?: ToastNotificationCardTheme; } interface ToastNotificationCardTheme { avatar?: React.CSSProperties; headerText?: React.CSSProperties; bodyText?: NotificationCardBodyTextThemeProps; container?: React.CSSProperties; } interface NotificationCardBodyTextThemeProps extends React.CSSProperties { color?: string; blockquoteColor?: string; tableBorderColor?: string; linkColor?: string; } ``` # WebPush Source: https://docs.suprsend.com/docs/react-webpush Integration steps of webpush in react application. ## Integration steps Please follow this guide to integrate [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) in your application. Pass `vapidKey` prop to `SuprSendProvider`. You can find it in *`SuprSendDashboard --> Vendors --> WebPush`*. Service worker file is the background worker script which handles push notifications. Create `serviceworker.js` file such that it should be publicly accessible from `https:///serviceworker.js`. If you want to customise this default service worker file name `serviceworker.js` to other file, pass `swFileName` prop to `SuprSendProvider`. Add below lines of code in that service worker file and replace `YOUR_PUBLIC_API_KEY` with key you find in API Keys page in SuprSend Dashboard. ```javascript serviceworker.js theme={"system"} importScripts('https://cdn.jsdelivr.net/npm/@suprsend/web-sdk@2.0.0/public/serviceworker.min.js'); initSuprSend(YOUR_PUBLIC_API_KEY); // replace publicApiKey with your value ``` All available webpush methods can be accessed using SuprSend client instance. You can get the SuprSend client instance using [useSuprSendClient](https://docs.suprsend.com/docs/react-1#usesuprsendclient) hook. Call `registerPush` in your code, which will perform following tasks: * Ask for notification permission. * Register push service and generate webpush token. * Send webpush token to SuprSend. ```javascript TypeScript theme={"system"} const response = await suprSendClient.webpush.registerPush(); ``` **Returns:** `Promise` **NOTE:** This method should be called on user action like button click for better UX, don't call this on page load. ## Other available methods ### Check for permission To check if user has enabled notifications permission use method provided by library ```javascript theme={"system"} suprSendClient.webpush.notificationPermission(): ``` This will return a string representing the current permission. The value can be: **granted:** The user has granted permission for the current origin to display notifications. **denied:** The user has denied permission for the current origin to display notifications. **default:** The user's decision is unknown. This will be permission when user first lands on website. # Remove User from list Source: https://docs.suprsend.com/docs/remove-user-from-list Dynamically remove users from list within a workflow. You can use this node to dynamically remove recipient or actor from the list. You can use it for use cases like when someone unsubscribes from an event or completes an action, you can remove them from the notification list to stop sending them further alerts. ### Compute List ID at runtime You can either add users to a pre-defined list or dynamically render list ID during workflow execution. Dynamic list are defined in handlebars format as `{{...}}`. One common use-case of computing list ID dynamically is when you have different lists based on user topic subscription. For example, there are multiple events happening and you have separate list for each event. List ID in such case can be `{{event_id}}_subscribers` and List name `{{event_name}} - subscribers`. There should be an existing list with the ID computed during trigger else this action will fail. ### Lists vs. Objects for topic subscriptions * [Lists](/docs/lists) are ideal for one-time broadcasts to large user groups with high throughput (up to 1000 notifications/second). Example: Sending announcements or updates to all users subscribed to a particular event. * [Objects](/docs/objects) are better for reusable topic subscriptions when additional workflows need to be triggered for the same subscribers. Example: Notifying team members or running nested workflows for hierarchical subscribers. *** # Resend Source: https://docs.suprsend.com/docs/resend Guide to connect your Resend account with SuprSend to send email notifications. This section is a step by step guide to add Resend as your email service provider. You can use your existing Resend account to integrate, or create a new account from [here](https://resend.com/signup) ## Resend integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Email -> Resend from the list of Vendors. This will open vendor details page as shown below: ![](https://files.readme.io/00da130-image.png) | Form Field | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | API Key | You will get the [API Key from your Resend account](https://resend.com/api-keys).SuprSend uses this API Key to send email on your behalf via your registered Resend account. | | Form Name | Default 'From Name' that email will go from. You can override this in the individual template. e.g. SuprSend | | Form Email | Default 'From Email ID' that email will go from. You can override this in the individual template. e.g. [*support@suprsend.com*](mailto:support@suprsend.com) | | Reply Email | Default 'Reply To Email id' on which replies are received. You can override this in the individual template. e.g. [*support@suprsend.com*](mailto:support@suprsend.com) | | Price per notification | This is the amount you pay per email notification to Resend. It helps us to calculate, estimate and optimise your cost spent on notifications. | ## Get API Key from Resend account Once logged in to your Resend account, navigate to API page from the left navigation panel. You can use your existing API Key and click on **"Create API Key"** to create one. ## Whitelist email domain on Resend A domain is used to send emails using your own domain. Verifying a domain is a required step because it’s essential to ensuring the deliverability of your emails. You can verify a domain by setting DKIM DNS record. Go to Domains tab on Resend dashboard to add your domain. Know more about [domain verification here](https://resend.com/docs/dashboard/domains/introduction). ## Configuring Open/Click tracking for emails on Resend One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account at a single place. Go to the webhook tab from side navigation on your resend account and add SuprSend webhook endpoint and select all events to listen. `URL: `[**`https://hub.suprsend.com/webhook/resend/`**](https://hub.suprsend.com/webhook/resend/) ## How to add Unsubscription link in email You can directly add unsubscription link in your email using [`$hosted_preference_url`](/docs/user-preferences#hosted-preference-page) variable in your template. This link directs users to the [SuprSend preference page](/docs/user-preferences). With SuprSend preference setting, you have the flexibility to create multiple notification categories based on the type of notifications. This allows users to opt-out of specific notification categories, providing them with granular control over their preferences compared to other email services where they can only opt-out from all marketing communications at once. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don’t give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don’t have them. *** # Security Source: https://docs.suprsend.com/docs/security Learn how SuprSend handles security- data encryption, access control, network policies, & compliance standards. At SuprSend, we prioritize the security and privacy of your data. Our comprehensive security measures and adherence to industry standards ensure that your information is protected at every level. ## Security Compliance Our security measures meet industry standards for data protection and security. * **SOC 2 Type II Compliance:** SuprSend has passed independent audits that confirm our systems are secure, reliable, and handle your data with care, not just once, but continuously over time. * **GDPR Compliance:** We support EU-data residency options and support GDPR's right to erasure workflows, ensuring compliance with data privacy regulations. * **HIPAA Compliance:** SuprSend strictly safeguards sensitive health information, aligning with U.S. healthcare data protection regulations. * **CPRA Compliance:** This compliance, an amendment of the earlier California Consumer Privacy Act (CCPA), implies to California residents' privacy rights to correct inaccurate personal information and to limit the use of sensitive personal information for their customer rights. SuprSend strictly adheres to the CPRA standardization. * **ISO 27001:** This international standardization continually improves and maintains an Information Security Management System (ISMS). SuprSend, being ISO 27001 complaint, majorly prioritizes confidentiality, integrity, & availability of information through its risk management framework. ## Security Controls The Security controls below highlight high-level details about our steps to implement best practices, identify and mitigate risks, and continuously develop ways to improve. To learn about our full security controls, please look at [security practices.](https://www.suprsend.com/privacy) ### Data Security * **Production Databases Access Restriction:** Access to production databases is highly restricted, limited to authorized personnel only. * **Multi-factor Authentication:** All Staff members with access to critical systems use secure login mechanisms, including multi-factor authentication. * **Third-Party Assessments:** We have thorough third-party penetration tests and infrastructure audits regularly. * **Data Encryption:** Customer data in production databases and critical endpoints are encrypted to protect against unauthorized access. * **Data Backups:** Regular backups are performed to meet recovery objectives, and the integrity of these backups is periodically tested. * **Server Logs Retention Policy:** Server logs are retained for a maximum of 6 months, after which they are securely and permanently deleted. * **Automatic Session Timeout for Risk Mitigation:** Sessions for accounts using email and password authentication, including those with two-factor authentication, automatically expire after three days of user inactivity to mitigate risks. ### Network Security * **Transmission Confidentiality:** We use HTTPS with TLS 1.2 & above encryption, to secure transmitted data. * **Data Protection in Testing:** Customer data in non-production environments receives the same security standards as in production. * **Centralized Security Event Logging:** Our infrastructure logs security-related actions across critical systems for auditing purposes. We are committed to continually improving our privacy and data protection practices in accordance with relevant regulations. For more details, please visit: * [Privacy Policy](https://www.suprsend.com/privacy) * [Data Processing Agreement](https://www.suprsend.com/data-processing-addendum) * [Data Subprocessors](https://www.suprsend.com/subprocessors) *** If you identify a security issue or an area for improvement, please email us at [support@suprsend.com](mailto:support@suprsend.com). We will work with you to understand and resolve the issue. Security concerns are our top priority, and we will address any reported issues promptly. *** # Segment Source: https://docs.suprsend.com/docs/segment Guide to integrate and sync data from Segment on SuprSend using webhooks. The SuprSend segment integration enables you to sync users and events from Segment to SuprSend. This allows for seamless migration of users from Segment to SuprSend and the ability to use events from Segment to power automated workflows within SuprSend. ## Connector setup on SuprSend Login to SuprSend dashboard. Go to **Settings**-> **Connectors**. Select **"Segment"** from the connectors list. This is how segment integration page looks like. Before beginning the Segment integration, it is important to establish how Segment events and users will be mapped in SuprSend. The following form fields will assist in setting up the desired data mapping within SuprSend: ### 1. Add Prefix to track events This option allows for distinguishing the events coming from multiple data sources such as SuprSend SDK and Segment, or any other connector. By enabling this feature, a prefix of **`segment : `**will be added to all event names coming from Segment. This is particularly useful for existing Segment and SuprSend users who are passing the same events from both SuprSend SDK and Segment. By default, SuprSend assumes that events coming from all sources are unique and does not add any prefix to the event name. For example- if the [segment track](https://segment.com/docs/connections/spec/track/) API call looks like this ```javascript javascript theme={"system"} { "type": "track", "event": "User Registered", "properties": { "plan": "Pro Annual", "accountType" : "Facebook" } } ``` associated event JSON at SuprSend with **add prefix enabled** will look like this ```javascript javascript theme={"system"} event_name = "segment : User Registered" properties = { "plan": "Pro Annual", "accountType" : "Facebook" } ``` ### 2. User preference mapping There are certain reserved properties in SuprSend user profile, such as language preference. In order to map these reserved properties, you have to define the trait in [Segment identify](https://segment.com/docs/connections/spec/identify/) call whose value should be mapped to the reserved property. e.g.. if you define `language` as the mapping key for Preferred Language in SuprSend. The value passed against `language` trait in Segment identify call will be mapped as user preferred language in SuprSend. Example of Segment `identify` API call ```javascript javascript theme={"system"} { "type": "identify", "traits": { "name": "Peter Gibbons", "email": "peter@example.com", "language": "hi", "plan": "premium", "logins": 5 }, "userId": "97980cfea0067" } ``` associated user JSON at SuprSend will be. * Segment`userID`is used to map the`distinct_id`of user on SuprSend. * All properties inside`traits`are considered as user properties which can be further used to map user channels and preferences in SuprSend. ```javascript javascript theme={"system"} distinct_id = "97980cfea0067" properties = { "name": "Peter Gibbons", "email": "peter@example.com", "language": "hi", "plan": "premium", "logins": 5 } ``` and with language set as mapping key, `hi` will be set as the preferred\_language in user profile Language is stored in ISO 639-1 standard language codes. So, if you pass any value in language which is not in this format, it will be ignored. You can refer to all the [language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) ### 3. User channel mapping For existing segment users, you can use the same mapping that is used to track user channels in your segment identify call in SuprSend as well. Just map the channel keys in the [Segment identify](https://segment.com/docs/connections/spec/identify/) call with the corresponding communication channel in SuprSend interface. By default, the `email` trait in the Segment identify call is mapped to the Email channel in SuprSend, and the `phone` trait is mapped to the SMS channel. You can add the mapping of other channels in the similar way For new users setting up a fresh Segment integration, below table can be used as a reference for mapping user channels in both SuprSend and Segment: | SuprSend User Channel | Segment key | | --------------------- | ----------------- | | Email | email | | SMS | phone | | Whatsapp | whatsapp | | Slack (Email) | slackEmail | | Slack (User ID) | slackUserID | | Androidpush (FCM) | fcmAndroidpush | | Androidpush (Xiaomi) | xiaomiAndroidpush | | iospush (APNS) | apnsIospush | Example of Segment `identify` API call ```javascript javascript theme={"system"} { "type": "identify", "traits": { "name": "Peter Gibbons", "email": "peter@example.com", "phone":"+15555555555", "slack_email":"peter@abccompany.com", "language": "hi", "plan": "premium", "logins": 5 }, "userId": "97980cfea0067" } ``` with below User Channel mapping Communication channels in user profile will look like this Click on **`Save Mapping`** to save the changes **Changes in mapping keys will be applied to future syncs** It's important to note that any changes made to channel or preference mapping will only be applied to future user syncs. If the mapping is changed, you'll have to replay all past user syncs in order to update the data in the user profile for existing users. Also, we don't override channel information. So, if a user's email or any other channel value is changed in user profile, it will be appended to the user channel list and notification will be sent on both old and new email. This is to ensure that no communication is lost during the change and users can continue to receive notifications on their preferred channels. Now that the desired mapping of Segment events and users in SuprSend has been established, the next step is to start Segment integration. ## Setting up data sync from Segment to SuprSend You'll have to configure SuprSend as a destination in Segment. **Use webhooks destination to connect with SuprSend** Since, we are in beta and we don't have a first-party integration with Segment yet, you can use webhooks in Segment destination list to connect with SuprSend. Follow below steps to configure SuprSend Destination in Segment: 1. Login to your Segment account. Select **Connections**-> **Destinations** from the side navigation menu and click on **"Add Destination"** This will open destinations catalog. Search for webhook and select **"Webhooks (Actions)"** On the Webhooks page, click on **"Add destination"** Select the data source from which you are syncing your user and event data and click on "Next" On the setup screen, add Destination name. Destination name can be **"SuprSend \{workspace}"**. For example- For staging workspace, the destination name can be "SuprSend Staging". Choose **"Fill in settings manually"** and click on **"Create destination"**. Now, on your webhook destination page, go to **Mappings** tab and create a **+New Mapping**. Select **Send an HTTP request** in Add Mapping modal and add below details * Select any of event type- track and identify ![](https://files.readme.io/71ff24e-image.png) * In Add test event, select **Load Test Event from Source** ![](https://files.readme.io/141834f-image.png) * In Select Mappings, add below details: > URL: [https://hub.suprsend.com/connector/segment/](https://hub.suprsend.com/connector/segment/) >
Method: `POST` >
`Authorization` in headers: `Bearer _api_key_`(Please make sure to replace "*`api_key`*" with actual api key from segment connector settings page.) ![](https://files.readme.io/8d6594d-image.png)
Now, send a test event of type `track` or `identify`. You'll see status `200 OK` response and the corresponding event on [SuprSend dashboard -> API logs](https://app.suprsend.com/en/staging/logs/api/?last_n_minutes=1440) page. Callout ContentIf you don't get `200 OK` response in event tester, this means that Segment was unable to successfully setup your SuprSend connection. Double check Webhook URL and header in step-7 and see if its configured correctly in the Destination settings. Once your SuprSend connection is successful, try sending a test `identify call` and see if the user channels are mapped correctly in SuprSend. You can see the synced user on [*SuprSend dashboard -> Subscribers*](https://app.suprsend.com/en/staging/subscribers/subscribers/) page. Also, send a test `track event` call and check if the workflows are getting triggered. 1. After successful testing, **Save and Enable** the mapping. Also enable destination in Settings tab to start sending webhook data to SuprSend.
Your setup is now complete, enabling events and users data to flow from Segment to SuprSend. You can use this info to automatically power SuprSend workflows without making any changes in your codebase. *** # Sendgrid Source: https://docs.suprsend.com/docs/sendgrid Guide to connect your Sendgrid account with SuprSend to send email notifications. This section is a step by step guide to select Sendgrid as your email service provider. You can use your existing Sendgrid account to integrate, or create a new account from [here](https://signup.sendgrid.com/) ## Sendgrid integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click Email -> Sendgrid from the list of Vendors. This will open vendor details page as shown below: 1942 | Form Field | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily. For example- *Sendgrid \[Production]* | | API Key | You will get this API Key from your Sendgrid account. SuprSend uses this API Key to send email on your behalf via your registered Sendgrid account. | | From Name | Default 'From Name' that email will go from. You can override this in the individual template. *e.g. SuprSend* | | From Address | Default 'From Email ID' that email will go from. You can override this in the individual template. *e.g. [support@suprsend.com](mailto:support@suprsend.com)* | | Reply Address | Default 'Reply To Email id' on which replies are received. You can override this in the individual template. *e.g. [support@suprsend.com](mailto:support@suprsend.com)* | | Price per notification | This is the amount you pay per email notification to Sendgrid. It helps us to calculate, estimate and optimise your cost spent on notifications. | ### How to get API Key from Sendgrid account In order to generate the Sendgrid API Key, you can refer the section: [Create an API Key on Sendgrid](https://docs.sendgrid.com/ui/account-and-settings/api-keys#creating-an-api-key). ## Setting callback URL in Sendgrid account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. Go to Settings->Mail settings -> Event Webhook Authorization: None Post URL: [`https://hub.suprsend.com/webhook/sendgrid/`](https://hub.suprsend.com/webhook/sendgrid/) Deliverability data: select all Engagement Data: select all ## How to add Unsubscription link in email It's recommended to allow recipients to unsubscribe from emails. You can either: 1. **Use your vendor's unsubscribe link** by enabling **Unsubscribes** in *Sending → Domain settings (Tracking section)*. This opts users out of all emails from that vendor. 2. **Use SuprSend's** [**hosted preference page** ](/docs/user-preferences#hosted-preference-page)for more granular control, allowing users to manage preferences per category while also reducing unnecessary vendor API calls for opt-outs. **Why it's important to give unsubscribe option in email?** First, it is required by the [CAN-Spam Act](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business). Second, if you don't give them this option, they are more likely to click on the spam complaint button, which will cause more harm than allowing them to unsubscribe. Finally, many ESPs look for unsubscribe links and are more likely to filter your email if they don't have them. *** ## Migrating Email templates from Sendgrid If you have used sendgrid design library to design email templates and planning to migrate your existing templates to SuprSend. You can do that using our email HTML editor. Please follow the steps below for migration: * Go to Sendgrid Design Library * Select the email template that you want to export and click on "Edit" * In the design editor, you'll find the option to **`Export HTML`** in build -> Advanced section * Go to Sendgrid Design Library * Select the email template that you want to export and click on "Edit" * In the editor, simply copy paste the HTML content and [add it in SuprSend email editor](/docs/sendgrid#step2-copy-paste-the-html-content-in-suprsend-email-editor) (in step-2) * Go to [SuprSend dashboard -> Templates page](https://app.suprsend.com/en/staging/templates) * Click on "+New Template" and add a template name. * Go to email editor and click on `Switch to HTML editor` and paste the HTML template downloaded from Sendgrid and click on `Publish` ![](https://files.readme.io/58f3d0d-ezgif.com-gif-maker_6.gif) Make sure to add sample data for all variables before publishing the template. You can add sample data via "Mock data" button on the top right side of template editor. If you using default footer in Sendgrid template, please add below mock data along with other template variables. ```json json theme={"system"} { "Sender_Zip": "test", "Sender_City": "test", "Sender_Name": "Nikita", "unsubscribe": "test", "Sender_State": "test", "Sender_Address": "test", "unsubscribe_preferences": "test" } ``` After publishing the template, you can test template using ["Test template"](https://docs.suprsend.com/docs/testing-the-template) functionality on the template editor. # Slack Source: https://docs.suprsend.com/docs/slack Guide to integrate Slack App for sending notification to user DM or channels in any workspace. ## Pre-Requisites For sending notifications through Slack, you'll need to create a Slack App. This App will basically be responsible for sending notifications to users / channels in their Slack workspace. To create Slack App, you'll need a Slack account and a Slack workspace where you'll be configuring the App. You will also need permission to create a Slack app. If you do not have access already, you can create your own free Slack workspace. 1. [Create a Slack Account](https://slack.com/get-started#/createnew) 2. [Create a Slack Workspace](https://slack.com/get-started#/landing) 3. [Create a Slack App](/docs/slack#create-slack-app) ## What all is covered here? In this documentation, we'll create an app to send direct messages to users or channels in a slack workspace. Here's an overview of the steps required to integrate and send messages to a slack workspace: 1. [Enable Slack Channel](/docs/slack#step-1-enable-slack-channel) on SuprSend Platform 2. [Create Slack App](/docs/slack#step-2-create-a-slack-app)- You'll use this app to send direct messages to a user or channel 3. [Building OAuth flow](/docs/slack#step-3-assigning-oauth-scopes-to-app) to get the user permission and access token to send them notifications. This information will be stored as user's slack channel information. Unlike email where if you know user's email id, you can send email to that user. In case of Slack, your users have to authenticate and allow your app to send them notifications on Slack. By following the above steps, your Slack integration will be setup. The next steps will be to 1. [Update slack channel information in user or channel profile](/docs/slack#step-4-update-slack-channel-in-user-profile) to send message 2. [Create a Slack template](/docs/slack-template) and trigger workflow This is how you get started: ## Enable Slack channel on SuprSend vendor page Go to SuprSend dashboard -> [Slack Vendor Settings](https://app.suprsend.com/en/staging/vendors/slack/slack-slack) page. Enable Slack channel ## Create Slack app You’ll need to create a Slack app and assign required OAuth Scopes to that App to send notifications via SuprSend. If you have an existing Slack app, you can directly go to [OAuth Scopes](/docs/slack#step-3-assigning-oauth-scopes-to-app). Navigate to [Apps page](https://api.slack.com/apps) and click on "Create New App" button Give your App a suitable name and select the workspace you will be using to develop this app. For your test app, you can use something like "SuprSend Test App" for app name OAuth is a protocol that lets your app request authorization to private details in a user's Slack account without getting their password. It's also the vehicle by which Slack apps are installed on a team. You can authorize your app using one of the below methods: [OAuth V2 method. ](/docs/slack#1-oauth-v2-method)Whenever a user joins a slack workspace or adds your slack app in their existing workspace, your app asks for specific permission scopes and is rewarded with access upon a user's approval. This is how a typical OAuth flow works: 1. You give a button somewhere on your product to add slack channel. The easiest way to enable workspaces to install your app is with the [Add to Slack](https://api.slack.com/docs/slack-button) button. Another way is to give the option to [Sign in with Slack ](https://api.slack.com/authentication/sign-in-with-slack) in your onboarding flow (which is not very common) 2. This will redirect the user to an [authorization link](https://slack.com/oauth/authorize). Refer [OAuth V2](https://api.slack.com/legacy/oauth#send_users) documentation to add slack authorization flow in your product. 3. The user will be asked to give permissions to access their profile information based on the scopes that you define in the OAuth & Permissions sidebar of your [app management page](https://api.slack.com/apps). We'll need access to following scopes to be able to send direct message to user's Slack workspace. | scope | obligation | description | | | ------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | `chat:write` | *mandatory* | Post messages in approved channels & conversations. At a minimum your app needs this scope to send notifications to a Slack workspace. | | | `im:write` | *mandatory* | Send direct messages to users in Slack | | | `chat:write.public` | *mandatory* | Send messages to public channels your Slack app isn't a member of. If your app doesn’t have this scope, you’ll need to use the `conversations.joins` method to join a public channel before sending it messages. | | | `users:read` | *optional* | To send DM to user's DM | | | `users:read.email` | *optional* | To send DM to user using their email id | | Navigate to ***"Features" -> "OAuth & Permissions"*** page from the left sidebar menu. Scroll down to the Scopes section and add the above scopes as Bot Token Scopes. [**Incoming Webhook Method**](/docs/slack#2-incoming-webhook)**:** Another way to send slack message is using [incoming webhooks](https://api.slack.com/messaging/webhooks). Incoming webhook is a quick and easy way to send message to a particular channel. During the OAuth flow that uses `incoming_webhook` scope, your user can connect to a particular channel (public, private, or even the direct message channel for that individual user) they have access to in their Slack workspace. We recommend using [OAuth V2 method](/docs/slack#1-oauth-v2-method) for authentication, since incoming webhook requires your users to explicitly give permission to every channel that you want to send message to and restricts your access to limited channels in users' workspace. It is best suited for use cases where you want to just update users on a particular channel such as * Sending announcements and product updates in `#announcement` channel. * Setting up an internal channel to send a message in case of anomaly detection in a product. Navigate to ***"Features" -> "Incoming webhooks"*** page from the left sidebar menu to activate incoming webhook. Once enabled, you can use this scope in your [OAuth flow](https://api.slack.com/legacy/oauth#flow) to get user access to send messages on a particular channel ## Update Slack channel in user / object profile The information required in the user profile is dependent on the type of message you are sending. To send a direct message to a user, you’ll need to add the slack `user_id` or `email` along with bot access token in user profile. You can pass this information in a json format using `user.add_slack` method as below: ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile // using slack email user.add_slack( { "email": "john@abc.com", "access_token": "xoxb-xxxxx" }) //using slack user_id user.add_slack( { "user_id": "U/WXXXXXXXX", "access_token": "xoxb-xxxxx" }) const response = user.save() response.then((res) => console.log("response", res)); ``` ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) # using slack email user.add_slack( { "email": "john@abc.com", "access_token": "xoxb-xxxxx" }) # using slack user_id user.add_slack( { "user_id": "U/WXXXXXXXX", "access_token": "xoxb-xxxxx" }) response = user.save() print(response) ``` To send a message to a public or private channel, you’ll need to pass its channel\_id along with access token. You can get channel\_id by opening slack in a browser, select the desired channel and copy the channel\_id from the URL We recommend creating a separate user for identifying channels and not mix it in user profile. You can create a slack channel user with `distinct_id` as `channel_` so it's easy for you to remember and pass in your workflow calls whenever you want to send message on a slack channel. Refer below example to add slack channel shown in above screenshot ```javascript node.js theme={"system"} const distinct_id = "channel_general" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-xxxxx" }) const response = user.save() response.then((res) => console.log("response", res)); ``` ```python python theme={"system"} distinct_id = "channel_general" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_slack( { "channel_id": "CXXXXXXXX", "access_token": "xoxb-xxxxx" }) response = user.save() print(response) ``` To send a direct message using incoming webhook, just pass the webhook URL in user profile. You can pass this information in a json format using `user.add_slack` method as below: ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }) const response = user.save() response.then((res) => console.log("response", res)); ``` ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }) response = user.save() print(response) ``` ### Order of precedence The Slack profile should only contain `incoming_webhook->url`, or the `access_token` with one of these keys: `channel_id`, `user_id`, or `email`. If there are multiple keys in `user.add_slack()` call, the order of precedence is as follows: `incoming_webhook->url` > `channel_id` > `user_id` > `email`. e.g., if your add\_slack argument is as follows: ```javascript node.js theme={"system"} const distinct_id = "__uniq_user_id__" // Unique id of user in your application const user = supr_client.user.get_instance(distinct_id) // Instantiate User profile user.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" }, "email": "john@abc.com", "user_id": "U/WXXXXXXXX", "channel_id": "CXXXXXXXX", "access_token": "xoxb-xxxxx", }) const response = user.save() response.then((res) => console.log("response", res)); ``` ```python python theme={"system"} distinct_id = "__uniq_user_id__" # Unique identifier of user in your application # Instantiate User profile user = supr_client.user.get_instance(distinct_id=distinct_id) user.add_slack( { "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" }, "email": "john@abc.com", "user_id": "U/WXXXXXXXX", "channel_id": "CXXXXXXXX", "access_token": "xoxb-xxxxx", }) response = user.save() print(response) ``` The profile will be saved as `{"incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }` ## Update Slack template Refer section to [create slack template](/docs/slack-template) using JSONNET editor. *** # Slack Source: https://docs.suprsend.com/docs/slack-quick-start Quick set up guide to start sending notification on Slack chat via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Integrate Slack and add option to join Slack in your product To activate Slack Integration, you'll have to first enable slack channel from [vendor page](https://app.suprsend.com/en/staging/vendors/slack/slack-slack?brand_id=default), [create a Slack App](/docs/slack#step-2-create-a-slack-app) and set up the right [authentication method](/docs/slack#step-3-assigning-oauth-scopes-to-app) to take user permission for sending Slack notifications. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period of 30 days. 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger) : You can trigger these events from your frontend application or from your backend systems, depending on the use case. (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * Template in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Whatsapp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. [Learn how to design Slack template here](/docs/slack-template). ![](https://files.readme.io/e5b9db7-template_edit.gif) 3\. **Functional nodes (Optional)**: These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow.[Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '`Text`' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**: End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id` within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile](/docs/users#creating-user-profile-on-suprsend) first for event based trigger. 2. **Data or Event Properties**: This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow)
**Sample Payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys ](https://app.suprsend.com/en/sandbox/developers/api-keys). Here, we are defining Slack channel inline using user email and access token. The access token here belongs to the bot added to your Slack App during creation. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ``` ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }], "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$slack", new JSONObject() .put("email", "user@example.com") .put("access_token", "xoxb-XXXXXXXX")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Slack Template Source: https://docs.suprsend.com/docs/slack-template How to design Slack templates using text editor or JSONNET editor for rich block kit templates. ## Edit Template SuprSend provides two ways to design Slack templates: * **Text Editor**: For simple text-based messages with variable interpolation * **JSONNET Editor**: For rich, interactive templates using Slack's Block Kit with buttons, images, and complex layouts ### Text Editor The text editor is ideal for simple text messages with variable content. You can add variables in Handlebars syntax as `{{...}}`. If the output has special html text, enclose variable in triple curly braces as `{{{url}}}` to avoid HTML escaping. ```text Sample Template theme={"system"} New Signup request in ABC company UserName: {{user_name}} Email: {{user_email}} Organization: {{org.name}} Domain: {{org.domain}} ``` ```json Mock Data theme={"system"} { "user_name": "John Doe", "user_email": "john@doe.com", "org": { "name": "ABC company", "domain": "abc.com" } } ``` ### JSONNET Editor The JSONNET editor enables rich template design using [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/). This allows you to create interactive templates with buttons, images, checkboxes, and styled text. It is essentially JSON template where variables can be added in JSONNET syntax as `data.variable_name` or `data["$variable_name"]`. ![Slack Editor Demo](https://files.readme.io/ae3572a-slack_editor.gif) #### Template Examples Slack Text Message Pn ```json JSONNET Template theme={"system"} [ { "type": "section", "text": { "type": "mrkdwn", "text": "New Signup on ABC company" } }, { "type": "section", "text": { "type": "mrkdwn", "text": ">_UserName_: *%(user_name)s*\n>_Email_: *%(email)s*\n>_Organization_: *%(org_name)s*\n>_Domain_: *%(domain)s*" % { user_name: data.user_name, email: data.user_email, org_name: data.org.name, domain: data.org.domain } } }, { "type": "divider" } ] ``` ```json Mock Data theme={"system"} { "user_name": "John Doe", "user_email": "john@doe.com", "org": { "name": "ABC company", "domain": "abc.com" } } ``` Slack Approval Template Pn ```json Template theme={"system"} [ { "type": "section", "text": { "type": "mrkdwn", "text": "Share access requested for *<%(document_link)s|%(document_name)s>*" % { document_link: data.document_link, document_name: data.document_name } } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Requested by:*\n" + data.requester_name }, { "type": "mrkdwn", "text": "*When:*\n" + data.submitted_at }, { "type": "mrkdwn", "text": "*Reason:*\n" + data.access_reason } ] }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "emoji": true, "text": "Approve" }, "style": "primary", "value": "approve_access" }, { "type": "button", "text": { "type": "plain_text", "emoji": true, "text": "Deny" }, "style": "danger", "value": "deny_access" } ] } ] ``` ```json Mock Data theme={"system"} { "access_type": "View", "submitted_at": "Jul 14, 2025", "access_reason": "Need to review figures before tomorrow's board meeting.", "document_link": "https://docs.company.com/12345", "document_name": "Q3 Financial Report", "requester_name": "Jane Doe" } ``` Slack Anomaly Alert Pn ```json Template theme={"system"} [ { "type": "section", "text": { "type": "mrkdwn", "text": ":warning: *High Error Rate Detected*\nOur system has experienced a spike in errors over the last *30 minutes*." } }, { "type": "section", "text": { "type": "mrkdwn", "text": "The error rate has significantly increased, impacting reliability.\nPlease investigate immediately to avoid service degradation." } }, { "type": "image", "title": { "type": "plain_text", "text": "Request vs Failure Trend (Last 6 Hours)", "emoji": true }, "image_url": data.image_url, "alt_text": "Graph showing high error rate spike" }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Impacted Services:*\n" + data.impacted_services }, { "type": "mrkdwn", "text": "*Time Range:*\n" + data.time_range }, { "type": "mrkdwn", "text": "*Error Rate:*\n" + data.error_rate } ] }, { "type": "divider" }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "🔍 View logs: <%(log_url)s|Open in Monitoring Tool>\n📊 See metrics dashboard: <%(dashboard_url)s|Error Trends>" % { log_url: data.log_url, dashboard_url: data.dashboard_url } } ] } ] ``` ```json Mock Data theme={"system"} { "log_url": "https://monitoring.company.com/logs?range=last_30_minutes", "image_url": "https://datadog-docs.imgix.net/images/dashboards/widgets/alert_graph/alert_graph.0ddfb73f548355b8adbe31723dff1dd3.png?fit=max&auto=format", "error_rate": "17.2% (normally <1%)", "time_range": "12:00 PM – 12:30 PM UTC", "dashboard_url": "https://monitoring.company.com/dashboards/error-trends", "impacted_services": "API Gateway, Auth Service" } ``` Slack Task Reminder Pn ```json Template theme={"system"} [ { "type": "section", "text": { "type": "mrkdwn", "text": "Hi " + data["$recipient"].name + " :wave:" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "You have "+data["$batched_events_count"]+" *pending tasks* for today:" } }, { "type": "rich_text", "elements": [ { "type": "rich_text_list", "style": "bullet", "indent": 0, "elements": [ { "type": "rich_text_section", "elements": [ { "type": "text", "text": task.title + " (" + task.status + ")" } ] } for task in data["$batched_events"] ] } ] }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Pending tasks", "emoji": true }, "url": "https://app.company.com/tasks", "value": "task_url" } ] } ] ``` ```json Mock Data theme={"system"} { "$batched_events_count": 3, "$batched_events": [ { "title": "Fix login bug on mobile", "status": "In Progress", }, { "title": "Write API docs for new endpoints", "status": "Todo" }, { "title": "Review design spec for onboarding", "status": "Blocked" } ] } ``` ### Adding dynamic content Here's how you can different types of variables in both [handlebars](https://docs.suprsend.com/docs/handlebars-helpers) and [JSONNET](https://jsonnet.org/ref/language.html) syntax. | Variable Type | Handlebars Syntax | JSONNET Syntax | | -------------------------------- | ----------------------------- | ----------------------------- | | **Parent Level variables** | `{{user_name}}` | `data.user_name` | | **Nested Object** | `{{org.name}}` | `data.org.name` | | **Print Array element at Index** | `{{task_list.[0].task_name}}` | `data.task_list[0].task_name` | | **Recipient** | `{{$recipient.name}}` | `data["$recipient"].name` | | **Actor** | `{{$actor.name}}` | `data["$actor"].name` | | **Tenant** | `{{$tenant.brand_name}}` | `data["$tenant"].brand_name` | **Print each item in the Array** ```text Handlebars theme={"system"} {{#each task_list}} {{task_name}}: {{task_description}} {{/each}} ``` ```json JSONNET theme={"system"} [ { "type": "section", "fields": [ { "type": "plain_text", "text": tasks.task_name + ": " + tasks.task_description, "emoji": true } for tasks in data.task_list ] } ] ``` **Conditional Logic** ```text Handlebars theme={"system"} {{#if is_new_org}} New Organization {{else}} Existing Organization {{/if}} ``` ```json JSONNET theme={"system"} { "type": "section", "text": { "type": "mrkdwn", "text": if "is_new_org" in data then "New Organization" else "Existing Organization" } } ``` **Batched Template** ```text Handlebars theme={"system"} Total events: {{$batched_events_count}} {{#each $batched_events}} {{item}} {{/each}} ``` ```json JSONNET theme={"system"} [ { "type": "section", "text": { "type": "plain_text", "text": "Total events: " + data["$batched_events_count"], "emoji": true } }, { "type": "section", "fields": [ { "type": "plain_text", "text": events.item, "emoji": true } for events in data["$batched_events"] ] } ] ``` ## Preview Template 1. Add mock JSON data using the `Mock data` button for all variables used in the template 2. Click `Load Preview` to see the rendered template 3. For JSONNET templates, click `View in Slack Block Kit` to see the actual Slack UI preview You must add mock data for all variables in your template. Missing mock data will cause rendering errors and prevent the preview from loading. ![Slack Preview Demo](https://files.readme.io/b481ef6-slack_preview.gif) ## Publish Template Once your template is ready, click **Publish Draft** and provide a version name to publish it. The published template becomes the live version and will be used whenever the associated workflow is triggered. ## Test Template Use this option to send a test message in Slack and preview how it will appear in user's device. Click the Test button, then enter the user's distinct\_id and select the Slack channel where the test message should be sent. Template testing only uses the published Live version, so make sure to publish your changes before testing. ## Promote to Production You can clone template across workspaces by using Clone -> Outside Template option. Clone -> Within template can be used to clone within different languages and versions of the same template. **Best Practice**: Always design templates in your staging workspace first, then promote them to production. This ensures thorough testing of the changes without impacting end users. # Smart Channel Routing Source: https://docs.suprsend.com/docs/smart-delivery Send multi-channel notifications sequentially with a delay between each channel to reduce bombarding. Smart Channel Routing is an optimal way of sending multi-channel notifications without bombarding your users. In case of smart routing, you send notification sequentially on channels with a delay rather than bombarding users on all channels at once. Example, If you have a template with 4 channels, the delivery on rest of the channels are skipped if the user sees or interacts with the message on 1 channel. This way, you can achieve the uptick in engagement (delivery, seen and interaction rate) without unnecessary noise and also save cost of paid notification channels. The content of the notification is designed with [templates](/docs/templates). In SuprSend, you can design the content of multiple channels within a single template group. ## How Smart Channel Routing node is executed? The First step is to identify applicable channels for delivery in user profile. Applicable channels are the subset of the following: * **Active channels on template** Active channels are published and live channels in the template. For WhatsApp and SMS (Indian vendors), templates become live upon approval by the respective provider. * **Active channels in user profile** Available channels in user profile which are not marked inactive. A set channel becomes inactive in case the channel is`removed `or`unset`using [SDK or API](/docs/users#via-sdk) or it is marked inactive by SuprSend. * **Channels where user preference is set as opt-in** If you are using SuprSend preference centre to take user preferences, check if the user preference is`opt-in`for the given channel and notification category (defined in workflow settings). You can check user preference using [get user preference API](/reference/get-user-category-preferences). Currently, the order of channel is defined to [optimize on](/docs/smart-delivery#optimize-on) cost (channels are tried from low to high cost picked from their vendor form). We'll also be introducing options to optimize on engagement so that notifications is sent on the channel with higher chances of open and click first. Now, the **notification will be delivered on each channel in the order with** [time-interval](/docs/smart-delivery#time-to-live) **\[time\_to\_live / (number\_of\_channels - 1)] apart** . e.g., if you have 3 active channels with time to live of 1 hour, notification will be delivered to each channel at 30 minutes apart till the [success metric](/docs/smart-delivery#success-metric) is not achieved. Once success metric is achieved, notification delivery on further channels is skipped. ## Setting routing rules Routing rules define how the notifications will be routed across channels. ### Optimize on This is used to define the order of channels based on the metric that you want to optimize. e.g., if you want to optimize on cost, SuprSend will internally set the order of channel in the ascending order of cost where the lowest cost channel is tried first. > We'll be introducing the option to manually set the channel order and optimize on other metrics like engagement. ### Time to live Time to live defines the delay in delivery between 2 subsequent channels. Notification will be delivered on each channel in the order with time-interval of \[time\_to\_live / (number\_of\_channels - 1)] apart. e.g., if you 3 channels with time to live of 1 hour, notification will be delivered to each channel at 20 minutes apart till the success metric is achieved. ### Must send to These are the channels on which notification has to be sent immediately, irrespective of the channel order. ### Success Metric Success Metric can be any event which defines the target user activity that you want to drive with your sent notification. In case of channel routing, it stops delivery on further channels when success metric is achieved. You can define 2 types of success metrics: 1. **Notification Status** Success Metric can be notification status (delivery, seen, interaction / click) of any of the sent channels. e.g., if the target of your notification is user opening the notification, you can set your success metric as `Notification Status - Seen`. 2. **Custom Event** It can be any other custom event that you want your user to perform on the platform in response to the sent notification. e.g., in case of payment reminder notification, you can set the success metric as`Invoice Paid`. ## Override Channels You can use this field to pass channel list dynamically using data in your event property. This feature comes in handy when user channels are dynamically defined at the user level for each workflow. For instance, when booking an appointment, your users are dynamically defining their preferred channel to receive booking updates for each appointment. For more consistent channel preferences, like user wanting to receive all communication via email only, or defining preferred communication channel for a notifications category (like booking updates), we recommend updating it using [user preferences](/docs/user-preferences). To override channels, include the channels array in event property and add the corresponding key in the override channels field on SuprSend workflow form. The expected channel values are: `\\\["email", "sms", "whatsapp", "androidpush", "iospush", "webpush", "slack", "inbox", "ms_teams"\\\]` You can add channel array as a [JQ-expression](https://jqlang.github.io/jq/manual/). So, in case your channel values do not match with the one mentioned in the above table, you can transform it using the JQ-expression. Below are some examples of how to add duration key in JQ format: 1. General format for duration key at parent level is`.channels` 2. If channel is a nested event property key like shown below, enter it in the format `.user.channels`. ```json json theme={"system"} properties = { "user": { "name": "Steve", "channels": ["email","inbox] } ``` When the channel key specified is missing, or resolves to an invalid value, workflow execution will stop and corresponding error will be logged in the logs *** ## Frequently Asked Questions Routing happens at channel identity level. So, if there are multiple email ids, notification will be sent to one email id and then on another after a delay. Vendor routing is independent of channel routing and doesn't add to the delay in channel routing. *** # SMS Source: https://docs.suprsend.com/docs/sms-quick-start Set up guide to send SMS notifications via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period and SMS notifications can be sent only to verified phone numbers (to prevent spam). 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on **`+ Create workflow`** button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger) (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * Template in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Whatsapp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. [Learn more about how to design SMS template here](/docs/sms-template). ![](https://files.readme.io/e5b9db7-template_edit.gif) 3. **Functional nodes (Optional)** These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '**`Test`**' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: * **Recipient**- End user who would be notified in the workflow run. Recipient is uniquely identified by `distinct_id` within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile first](/docs/users#creating-user-profile-on-suprsend) for event based trigger. In Sandbox environment, a sample user with your registered email ID is pre-created for testing. You can always add more users or edit existing user profile from subscriber page on UI. * **Data or Event Properties**- This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow) **Sample payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys](https://app.suprsend.com/en/sandbox/developers/api-keys) ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms": []string{"+12135555444"}, "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$sms", Arrays.asList("+12135555444")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Test with your SMS vendor You have to bring your own vendor to setup SMS notification in staging and production workspaces. However, you can test your workflows in Sandbox where sample vendors are pre-added. You can clone workflows from one workspace to another. All you have to do is fill in the vendor form for your respective vendor and you are good to go. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # SMS Template Source: https://docs.suprsend.com/docs/sms-template How to design and publish SMS template. ## Design Template You can design SMS template on SuprSend with a simple form editor tool. You can add variables with `Handlebarsjs` language. You can check how the message will look in the preview section on the right side. The SMS template has 3 parts: * **Message type** (Transactional, Promotional, Engagement), * **Header** (template headers added in the SMS integration settings), * **Body** (SMS template is added here). Once designed, you can save the SMS template by clicking on "Save" button. When you are ready, you can Publish Draft by providing a name to the version. This will create a version in 'Pending Approval' state. Every SMS template goes through an approval process, where the templates are submitted to the registered DLT portal for review, where the SMS is reviewed based on the **DLT guidelines** and SMS either gets approved or rejected. SuprSend handles the template approval process for you. All you have to do is create a template on SuprSend while following DLT template guidelines, and wait for the approval / rejection of template. Accordingly, the published template version's state will move to `Live` or `Rejected`. Once the version goes `Live`, you can use the template to send to your users. ## SMS fields description | Field | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Message Type | There are 3 types of message - (1) **Transactional**- Service implicit or Informative messages which are triggered corresponding to a user's action either done at the time of sending the message or based on past data. All other OTPs other than bank OTPs also fall in this category. *e.g.- delivery updates, E-commerce website OTPs etc.* (2) **Promotional**- All the marketing related messages where we have not taken any explicit consent from the user. *e.g.- messages sent to promote or sell a product* (3) **Engagement**- Service Explicit or Engagement messages which are triggered to re-engage the users back to platform like promoting new features and offers. *e.g.- new feature promotion, discount offer messages to existing customers etc.* | | Header | Header should be registered with DLT. Separate headers would be there for all the message types | | Body | SMS template added here should follow DLT template guidelines. Click here to view all DLT guidelines | Please note that to send the SMS, you will need to integrate SMS vendor with SuprSend. Please visit the 'Vendor Integration Guideline' section to see vendors list and how to integrate them. **Vendor Integration Required** 📘 Please note that to send the SMS, you will need to integrate SMS vendor with SuprSend. Please visit the 'Vendor Integration Guideline' section to see vendors list and how to integrate them. ## Adding dynamic content in SMS There will always be the case where you would be required to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending email. To send actual values to replace variables at the time of communication trigger, use one of our frontend or backend SDKs. Here is a step by step guide on how to add dynamic content in a template: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing template for any channel. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable\_name}}` If you have declared the variables and added sample data in the global `Mock data` button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of error like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the `Variables` button. To manually enter the variable name, follow the [handlerbarsjs guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below are some examples of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the `Templates` section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "3,950.00" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "2,549.00" }, { "product_name": "RayBan Sunglasses", "product_price": "7,899.00" } ], "event": { "location": { "city": "Bangalore", "state": "KA" }, "order_id": "11200123", "first_name": "Nikita" }, "product_page": "https://www.suprsend.com" } ``` To enter a nested variable, enter in the format `{{var1.var2.var3}}`. Eg. to refer to city in the example above, you need to enter `{{event.location.city}}` * If you have any space in the variable name, enclose it in square bracket `{{event.[first name]}}` * To refer to an array element, enter in format `{{var1.[index].var2}}` . Eg. to refer to `product_name` of the first element of the array `array` , enter `{{array.[0].product_name}}` At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. Eg. if there is an error in rendering Android Push template, but SMS template is successfully rendered, Android Push notification will not be triggered, but SMS notification will be triggered by SuprSend. *** # Subscribe to Object Source: https://docs.suprsend.com/docs/subscribe-to-object Dynamically add users in object subscription within a workflow. You can use this node to dynamically add recipient or actor in [object subscription](/docs/object-subscriptions) based on an event or action. If you have event-based data coming from third-party systems, you don’t need to write custom object subscription APIs in your codebase. Simply send events to SuprSend, and let the workflow handle object subscriptions. For example, when someone subscribes to a topic (like a tournament), you can automatically add them as a subscriber to the corresponding object. This ensures they receive all relevant notifications about topic-related activities without manual intervention. ### Compute and create objects at runtime You can either add users to a pre-defined object or compute object on the fly using workflow input data. While defining object, both ID and type are mandatory. Type defines the group that object belong to (example, teams, departments). Dynamic object are defined in handlebars format as `{{...}}`. Computed Object will only be created if `Create object if it doesn't exist` setting is ON. One common use case of creating object dynamically is when you need to create different objects based on user topic subscription. For example, there are multiple player tournaments happening and you want to create a separate object for each tournament. Object ID in such case can be `{{tournament_id}}` and type `tournaments`. Both object ID and type only supports following characters- \`a-z,0-9,-,\_\`. Ensure that the variable resolves to a valid format; otherwise, object creation will fail. ### Subscription properties Subscription properties are the set of variables defining relationship between a subscriber and an object. During a workflow execution, these properties can be accessed under the `recipient.subscription` namespace as `recipient.subscription.property_key`. Subscription properties are added in [JSONNET](https://jsonnet.org/ref/language.html) format. You can [read more about object subscription here](/docs/object-subscriptions). *** # APNS Push Integration Source: https://docs.suprsend.com/docs/swift-apns-push Integrate APNS Push in your Swift application ## Adding push capability 1. Inside your Target select **signing and capabilities** 2. Click on **+capabilities** and select **Push Notifications** ## Registering for push notifications AppDelegate should implement `UNUserNotificationCenterDelegate` from `UserNotifications` and then add `registerForPush` method to AppDelegate and call that method inside `application(_:didFinishLaunchingWithOptions:)`. ```swift AppDelegate.swift theme={"system"} import UserNotifications // Add this class AppDelegate: NSObject, UNUserNotificationCenterDelegate { // implement UNUserNotificationCenterDelegate func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.configure(publicKey: "PUBLIC_KEY") registerForPush() // add this return true } // add this method func registerForPush() { UNUserNotificationCenter.current().delegate = self // this willregister push delegate // ask user for permission // options = [.sound, .badge, .alert] for explicit authorization // options = [.badge, .alert, .sound, .provisional] for provisional authorization UNUserNotificationCenter.current().requestAuthorization( options: [.sound, .badge, .alert], completionHandler: { granted, error in if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } }) } } ``` ### Asking user permission Explicit authorization allows you to display alerts, add a badge to the app icon, or play sounds whenever a notification is delivered. In this type of authorization, the request is made the first time user launches your app. If the user denies the request, you can't send subsequent prompts to send the notification. Explicit authorization is default authorization method as it automatically sets alert, sound and badge as soon as the user allows this request. Provisional Authorization (Supported in iOS 12.0 and above) are sent quietly to the users —they don’t interrupt the user with a sound or banner. Also, they will not be shown when your app is in foreground. First time this type of notifications are sent, user is asked to "Keep" or "Turn off" the notifications. Further notifications continue to be sent if they click on "Keep". ## Adding delegate methods for push handling ```swift AppDelegate.swift theme={"system"} func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() Task { await SuprSend.shared.user.addPush(token) } } func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { SuprSend.shared.push.application( application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) completionHandler(.newData) } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { SuprSend.shared.push.userNotificationCenter( center, didReceive: response, withCompletionHandler: completionHandler) completionHandler() } func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { SuprSend.shared.push.userNotificationCenter( center, willPresent: notification, withCompletionHandler: completionHandler) if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { // Fallback on earlier versions completionHandler([.alert, .badge, .sound]) } } ``` ## Changes in Notification Service Extension ### Adding Notification Service Extension 1. In Xcode go to **File > New > Target**. 2. Select Notification Service Extension from the template list. 3. Then in Next popup give it any product name, select your team, select swift language and click finish. After clicking on "Finish", a folder will be created with your given product name. ### Installing SuprSend SDK in Notification Service In Xcode, go to File > AddPackages to add a new dependency. In that search bar, add suprsend-swift-sdk project github url `https://github.com/suprsend/suprsend-swift-sdk` and keep the default version settings and click `Add Package` button. In second dialog box, select your Notification Service target from dropdown and click `Add Package` button. Add the SuprSendSwift SDK to your Podfile as dependency to Notification Service Extension like below and then run `pod install`. ```ruby Podfile theme={"system"} target '' do pod "SuprSend" end ``` ### Adding code in Notification Service Paste the below code in NotificationService.swift file. Replace `YOUR_PUBLIC_KEY` with your public key. ```swift NotificationService.swift theme={"system"} import UIKit import UserNotifications import SuprSendSwift class NotificationService: SuprSendNotificationService { override func publicKey() -> String { "YOUR_PUBLIC_KEY" } } ``` ## Handling deep links By default SDK will handle only http deeplinks. If you want to handle custom deeplinks, implement `SuprSendDeepLinkDelegate` in AppDelegate class and add the below code. ```swift AppDelegate.swift theme={"system"} // implement SuprSendDeepLinkDelegate class AppDelegate: NSObject, UNUserNotificationCenterDelegate, SuprSendDeepLinkDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.configure(publicKey: "YOUR_PUBLIC_KEY") SuprSend.shared.setDeepLinkDelegate(self) // Add this registerForPush() return true } func shouldHandleSuprSendDeepLink(_ url: URL) -> Bool { print("Handling URL: \(url)") // write your linking logic here and return false return false } } ``` ## Final AppDelegate.swift file Example of `AppDelegate.swift` file with all the above code. ```swift AppDelegate.swift theme={"system"} import Foundation import SuprSend import UIKit class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, SuprSendDeepLinkDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { SuprSend.shared.enableLogging() SuprSend.shared.configure(publicKey: "Testing") SuprSend.shared.setDeepLinkDelegate(self) registerForPush() return true } func registerForPush() { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization( options: [.sound, .badge, .alert], completionHandler: { granted, error in if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } }) } func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() Task { await SuprSend.shared.user.addiOSPush(token) } } func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { SuprSend.shared.push.application( application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler ) completionHandler(.newData) } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { SuprSend.shared.push.userNotificationCenter( center, didReceive: response, withCompletionHandler: completionHandler) completionHandler() } func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { SuprSend.shared.push.userNotificationCenter( center, willPresent: notification, withCompletionHandler: completionHandler) if #available(iOS 14.0, *) { completionHandler([.banner, .badge, .sound]) } else { // Fallback on earlier versions completionHandler([.alert, .badge, .sound]) } } func shouldHandleSuprSendDeepLink(_ url: URL) -> Bool { print("Handling URL: \(url)") UIApplication.shared.open(url, options: [:], completionHandler: nil) return false } } ``` # Events and User methods Source: https://docs.suprsend.com/docs/swift-events-and-user-methods ## Trigger Events You can trigger events from client to SuprSend using `track` method. This can be used to trigger [event-based workflows](/docs/trigger-workflow#event-based-trigger). ```swift syntax theme={"system"} await SuprSend.shared.track(event: String, properties: [String: Any]?) ``` ```swift example theme={"system"} await SuprSend.shared.track(event: "test", properties: ["name": "john doe"]) ``` **Returns:** `async -> APIResponse` ## Update User Profile **Returns:** `async -> APIResponse` ### Update User channels Set user channel related information using following methods. Its recommended to use SuprSend's Backend SDK's to set user channels instead of Client SDK's. ```swift syntax theme={"system"} await SuprSend.shared.user.addEmail(String) await SuprSend.shared.user.removeEmail(String) // mobile should be as per E.164 standard: https://www.twilio.com/docs/glossary/what-e164 await SuprSend.shared.user.addSMS(String) await SuprSend.shared.user.removeSMS(String) // mobile should be as per E.164 standard await SuprSend.shared.user.addWhatsapp(String) await SuprSend.shared.user.removeWhatsapp(String) ``` ### Update User properties This is the list of available user update methods: This method will set users timezone. Timezone value should be in [IANA timezone format](https://timeapi.io/documentation/iana-timezones). ```swift syntax theme={"system"} await SuprSend.shared.user.setTimezone(String) ``` ```swift example theme={"system"} await SuprSend.shared.user.setTimezone("America/Bogota"); ``` This method will set users preferred language. Language value should be in [ISO 639-1 Alpha-2 format](https://gist.github.com/jrnk/8eb57b065ea0b098d571). ```swift syntax theme={"system"} await SuprSend.shared.user.setPreferredLanguage(String) ``` ```swift example theme={"system"} await SuprSend.shared.user.setPreferredLanguage("en"); ``` Set is used to set the custom user property or properties. If already property is already present value will be replaced. ```swift syntax theme={"system"} await SuprSend.shared.user.set(key: String, value: String) await SuprSend.shared.user.set(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.set(key: "name", value: "John Doe"); await SuprSend.shared.user.set(properties: ["name": "John Doe", "designation": "manager"]); ``` This method will remove user property. To remove channel pass `$email`, `$sms`, `$whatsapp`. ```swift syntax theme={"system"} await SuprSend.shared.user.unset(key: String) await SuprSend.shared.user.unset(keys: [String]) ``` ```swift example theme={"system"} await SuprSend.shared.user.unset(key: "wishlist"); await SuprSend.shared.user.unset(keys: ["wishlist", "$email"]); ``` This method will add a value to the list for a given property. ```swift syntax theme={"system"} await SuprSend.shared.user.append(key: String, value: String) await SuprSend.shared.user.append(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.append(key: "wishlist", value: "iphone12"); await SuprSend.shared.user.append(properties: ["wishlist": "iphone12", "cart": "Apple airpods"]); ``` This method will remove a value from the list for a given property. ```swift syntax theme={"system"} await SuprSend.shared.user.remove(key: String, value: String) await SuprSend.shared.user.remove(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.remove(key: "wishlist", value: "iphone12"); await SuprSend.shared.user.remove(properties: ["wishlist": "iphone12", "cart": "Apple airpods"]); ``` This method is similar to set method but values once set cannot be updated. ```swift syntax theme={"system"} await SuprSend.shared.user.setOnce(key: String, value: String) await SuprSend.shared.user.setOnce(properties: [String: Any]) ``` ```swift example theme={"system"} await SuprSend.shared.user.setOnce(key: "DOB", value: "1991-10-02"); await SuprSend.shared.user.setOnce(properties: ["first_login": "2021-11-02", "DOB": "1991-10-02"]); ``` Add the given amount to an existing user property. If the user does not already have the associated property, the amount will be added to zero. To reduce a property, provide a negative number as the value. ```swift syntax theme={"system"} await SuprSend.shared.user.increment(key: String, value: Int) await SuprSend.shared.user.increment(properties: [String: Int]) ``` ```swift example theme={"system"} await SuprSend.shared.user.increment(key: "login_count", value: 1); await SuprSend.shared.user.increment(properties: ["login_count": 1, "order_count": 1]); ``` Keys starting with `ss_` or `$` are reserved and will be ignored. *** # Integration Source: https://docs.suprsend.com/docs/swift-integration Integrate SuprSend SDK in your Swift project **`SuprSendSdk` is deprecated. Please migrate to `SuprSend` SDK** This documentation is for new version of iOS sdk. If you are using older version of sdk `SuprSendSdk` please refer [documentation](https://github.com/suprsend/SuprSend-iOS-SDK/tree/main/documentation) ## Installation There are two ways you can install SuprSend SDK into your app: In Xcode, go to File > AddPackages to add a new dependency. In that search bar, add suprsend-swift-sdk project github url `https://github.com/suprsend/suprsend-swift-sdk` and keep the default version settings and click `Add Package` button. In second dialog box, select your project's target from dropdown and click `Add Package` button. Add the SuprSendSwift SDK to your Podfile as `pod "SuprSend"` and run `pod install` to install the SDK. ## Integration Import SDK inside `AppDelegate.swift` and then initialize the SuprSend class inside `application(_:didFinishLaunchingWithOptions:)` method. ```swift AppDelegate.swift theme={"system"} import SuprSend SuprSend.shared.configure(publicKey: "YOUR_PUBLIC_API_KEY") ``` | Params | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------ | | publicApiKey\* | This is public Key used to authenticate API calls to SuprSend. Get it in SuprSend dashboard **ApiKeys -> Public Keys** section | Authenticate user so that all the actions performed after authenticating will be w\.r.t that user. This is mandatory step and need to be called before using any other method. This is usually performed after successful login and on reload of page to re-authenticate user. ```swift syntax theme={"system"} await SuprSend.shared.identify(distinctID: "YOUR_USER_ID", userToken: userTokenData, options: AuthenticateOptions(refreshUserToken: {oldUserToken,tokenPayload in return refreshedUserToken()})); ``` | Properties | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | distinctId\* | Unique identifier to identify a user across platform. | | userToken | Mandatory when enhanced security mode is on. This is ES256 JWT token generated in your server-side. Refer [docs](/docs/client-authentication#enhanced-security-mode-with-signed-user-token) to create userToken. | | refreshUserToken | This function is called by SDK internally to get new userToken before existing token is expired. The returned string is used as the new userToken. | **Returns:** `async -> APIResponse` #### 2.1 Check if user is authenticated This method will check if user is authenticated i.e. `distinctId` is attached to SuprSend instance. To check for userToken also pass checkUserToken flag true. ```swift syntax theme={"system"} SuprSend.shared.isIdentified(checkUserToken: true) ``` This will remove user data from SuprSend instance. This is usually called on logout action. ```swift syntax theme={"system"} await SuprSend.shared.reset() ``` **Returns:** `async -> APIResponse` ## Response Structure ```swift syntax theme={"system"} struct APIResponse { /// The status of the response (success or error). public let status: ResponseStatus /// The HTTP status code associated with the response. public let statusCode: StatusCode? /// The JSON response body. public let body: ResponseBody? /// Any error that occurred during the request. {type: string, message: string} public let error: ResponseError? } ``` ``` ``` # Preferences Source: https://docs.suprsend.com/docs/swift-preferences Preferences in your Swift application Coming soon # Design Template Source: https://docs.suprsend.com/docs/templates How to create, manage, and test templates in SuprSend. Templates are the content block of your notification. In SuprSend, content for all channels (SMS, email, chat, push etc.) is grouped under a single template group for simplified management. Your template can have 2 types of content: * **Static Content**: This remains the same for all users and contains your core message or design. * **Dynamic Content**: These are placeholders for user or tenant-specific information, such as first names, booking amounts, appointment times, and more. The variables are populated dynamically based on the data provided in your workflow or event request. - **Unified Content Management**: Content for all channels is grouped together in a single template, making it easy to manage your content in one place. - **WYSWYG editors**: Designing a template is a piece of cake with drag-and-drop and form editors. Your product managers and designers can take control of content creation without involving developers. - [Multi-Lingual support:](/docs/templates#adding-multiple-languages) You can add content for multiple languages in a single template and the user will receive notification in their preferred language - **Create white-labeled notifications for your** [tenants](/docs/tenants) **with ease**: You can create tenants for your own company and each of your tenants. Use it to dynamically change your email template styling to match the tenant's identity. - **Easy to iterate**: You can directly design and store templates on the SuprSend dashboard, decoupling templates from your code. - [Version control](/docs/templates#template-versions): Each template change is published as a new version, so you can always track historical changes. You can also monitor user engagement for each version and retain the one that performs better. Trigger a test notification on your current live version to see the content preview on your actual device before pushing it to production. - **Add / Remove channels without touching code**: To add any channel to your existing template group, you need to simply design a template for that channel on SuprSend platform and publish it. Notifications will start going through that channel, with no alterations to your existing code. ## Create Template SuprSend's template designer empowers you to create beautiful templates with easy drag-and-drop editor Add a template name and Click on '`Save`'. The new Template will be created, which you can see on the top of 'Templates' listing page. Click on the template to start editing. Select the channel that you want to edit and enable it. You can get the detailed guide to design the template for each channel in their respective documents: **Push channels:** **Chat:** ### Template versions SuprSend creates a new version every time you publish a template. This is to ensure that you have historical reference to all the changes done in your template. Helps with audit trails and understanding which template content performed better in terms of user engagement. * A **draft version** is created by default. You'll always do your changes in the draft version and publish the template once finalized. * The recent published template will become the **live version** . All your notifications will be using the live template version. * Earlier published versions will become inactive as soon as you publish a new template. You can see inactive template versions by selecting '`All`' tab from the top right side options. ### Adding dynamic content We use as the template variables language. You can learn about [here.](/docs/handlebars-helpers) 1. To add dynamic data, first add the variable data from your event and workflow request in the **mock data**. 2. Often times there is a mismatch between template variables and the data that come as part of payload of the trigger or event. Defining the JSON structure of variables before hand reduces the chances of error. 3. The variables will show as auto-suggestions while designing the template, reducing the possibility of typo errors. 4. Once declared, these variables can be used consistently across all channels for that particular template. 5. The sample values are used to send test notification and show the preview of final notification. In the above screenshot, there are other elements in the mock data like [tenant](/docs/tenants) and [batching](/docs/batch). You can check their respective documentation to know more about it. You can copy below mock data for starters: ```json json theme={"system"} { "name": "Steve", "items": [ { "item": "Jager-Smith Premium", "tracking_id": "FMPP14677458796", "delivery_date": "22 June, 2023" }, { "item": "Winget Women Cap", "tracking_id": "FMPP7734374844765", "delivery_date": "23 June, 2023" } ], "amount": "$3,249", "tracking_link": "https://www.example.com/track" } ``` ### Using variables in the templates: As a general rule, all the variables have to be entered within double curly brackets: . For URLs and links, we recommend using triple curly braces. This is to avoid escaping HTML entities in handlebars. You can also write transformations like date-time formatting, if-else statement inside your templates using [handlebar helpers](/docs/handlebars-helpers). Other than your workflow `data` variables, you can also add other type of variables in your templates like tenant properties. See a full list of possible variables here. There are slight differences in variable format and placement for different templates. Checkout each channel template documentation for reference. ## Preview and publish You can see the notification preview on the right side of your editor for most of the channels. Variables in template are replaced with the values from '**mock data'** for preview **For email**, preview option is available in the bottom left side menu **For Slack**, you can click on '`Load preview`' button to see the preview Once finalized, you can publish the template by clicking on '`Publish template`**'** button on the draft version ## Test template You can send test notifications directly from the template editor page to see how the message will appear on user's device. To send a test notification, You can find the `Test` button on the top right corner. Add the `distinct_id` of the [user](/docs/users), and click on search. It will show all the available channels for the user. Select those channels on which you'd like to test, and then click on `Trigger Test using mock data` . This will trigger a test notification. You can go to the logs in order to monitor the real-time status of your sent notification. JSON data added in the global **"Mock Data"** button will be used to render variables in the template. Make sure to add mock data for all the variables added in the template else notifications will fail. ![](https://files.readme.io/756a565-ezgif.com-video-to-gif_1.gif) \*\* Defining template in your workflow request \*\* The serves as a unique identifier for referencing a template in [workflows created through API](/docs/trigger-workflow#triggering-workflow-via-api). To copy the slug name, click on the clipboard next to the Template name. \*\* Edit template name and other details \*\* You can edit the **template description, name, and add tags** to the template by clicking on the edit icon next to the its name. * We recommend adding your template trigger logic and other relevant notes pertaining to the notification in the `description` . This is helpful for later reference and note keeping. * `Tags` are used for better organization of the templates on listing page. You can group similar templates using tags. Tags can then be used to [filter](/docs/templates#view-and-filter-your-template-list) out templates on listing page and also while [fetching templates through API](/reference/get-template-list). ## Clone template To avoid designing templates from scratch, you can clone your existing templates and design on top of it. ![](https://files.readme.io/aa55ca0-ezgif.com-video-to-gif_2.gif) ## Adding multiple languages SuprSend allows you to create notifications in multiple languages in the same template. Once the languages are added, SuprSend will pick the preferred language from user's profile and send the message as per the user's preferred language. You can add template languages using **Language** option from the top-right corner burger menu. For more details, check [steps and guidelines on adding language](/docs/multi-lingual-template). ## Archive template You can archive your unused templates by clicking on "Archive" option from the top-right corner burger menu. Templates can't be recovered once archived. ## View and filter your template list All of your active templates will be visible on the template listing page. You can filter your templates by `channel`, `tag`, `status` or just get the templates which were '**edited by you'**. Archived templates can be seen by clicking on `Archived` tab from the top right side options # Tenant Preference Source: https://docs.suprsend.com/docs/tenant-preference In this guide, learn how to manage preferences for your tenants and its users Tenants (previously named as brands) represents a segment that user belongs to. It can be organizations, teams within an organization, subsidiary companies or different product lines in the same business. If you're a B2B application or a multi-tenant SaaS product, you can use tenant preferences to allow company admins to set preference for their internal team or allow your customers to set default preferences for their users. ## Company setting preference for tenants In a multi-tenant architecture, businesses can define which notification categories and channels are applicable to different tenants. For example- if you are a SAAS platform with some paid features and some of your clients have not subscribed to the paid features. You can hide notification categories related to the paid feature for those tenants. You can disable category overall preference or set channels as opt-out that are not valid for your tenants from [tenant details page on SuprSend dashboard](https://app.suprsend.com/en/staging/tenants/default/tenant_preference). Tenant will not be able to set preferences in the hidden categories and notification will not be sent to tenant's users in hidden categories. ## Per-tenant default preference Tenants can set [default preferences](/reference/update-tenant-default-preference) for their end users and hide the categories which are not applicable to their end user. This gives tenants control to choose which notifications are sent to their users or shown to their users for setting preferences. ## Controlling preference UI for users Turning off categories or channels on tenant from [tenant preference page](https://app.suprsend.com/en/demo/tenants/default/tenant_preference) automatically removes it from user's preference view. Additionally, if tenants wish to further limit the categories visible to their end users, they can do so by setting `visible_to_subscriber` to `false` in the [default tenant preferences](/reference/update-tenant-default-preference). ## Collecting preference from tenant users You can load user preferences for a tenant by simply passing `tenant_id` in Rest APIs and [preference centre SDK](/docs/embedded-preference-centre). Hosted preference page automatically loads preferences corresponding to the tenant passed in workflow trigger. ## Triggering tenant workflows You can simply trigger tenant workflow by passing `tenant_id` in your workflow trigger. [Read more about tenant workflows here](/docs/tenant-workflows). ## Tenant level preference evaluation When a workflow is triggered, preference is evaluated for recipient and tenant before send node. Notification is sent in categories and channels where both tenant and user preferences are `opt_in`. If recipient preferences are not set in a category, default tenant preference is picked. Once user preference is set in a category, any changes in the default preference of existing categories will not effect recipient preferences. The order of precedence is always user > tenant > org (set in notification category settings). However, if company turns off notifications in a category or channel for a tenant, user will not get notification in that category even if they have kept their preference `ON` in that category in the past. User preferences can change over time and when checking a workflow run, you need to know what was user's preferences at that time. You can view this using the step-by-step debugger in [workflow executions](https://app.suprsend.com/en/demo/logs/executions/?last_n_minutes=1440). You can also track when user updated their preference by filtering on `Subscriber preference update` in [request logs](https://app.suprsend.com/en/demo/logs/api/?last_n_minutes=1440). *** # Tenant Vendor Source: https://docs.suprsend.com/docs/tenant-vendor How to setup per-tenant vendor for routing notifications through tenant vendor instead of the default company vendor. When sending notifications for your enterprise customers, not just notification styling; your clients might also want the notifications to come from their own tenant handles. For instance, in emails, the 'from address' should be from your clients' domain. Similarly, for SMS and WhatsApp, they might prefer to use their own tenant headers. Managing these different vendor configurations for all your tenants in your codebase can become complex and messy. With SuprSend's tenant vendor support, you can seamlessly route messages through your customer's channel providers without any code level changes. All you have to do is add vendor configuration for your tenants once and SuprSend will take care of routing the messages for each tenant via their vendors. > In case you don't have vendor defined for a tenant, notifications will automatically fallback to the default vendor credentials. ## How to add tenant vendors Adding a tenant vendor is quite simple. Just go to the ["Vendor" page](https://app.suprsend.com/en/staging/vendors/) on the SuprSend dashboard. Select the desired tenant from the top right side and add vendors for relevant channels. *Currently, this support is extended for Email, SMS and MS Teams Channel. We'll be expanding the functionality soon on other channels as well.* **Tenant Vendor APIs 👍** You can also use our APIs to set tenant vendors dynamically if you have a higher number of tenants or their configurations change more dynamically. The API support is in beta right now. You can email us on [support@suprsend.com](mailto:support@suprsend.com) for an early access. *** # Tenant Workflows Source: https://docs.suprsend.com/docs/tenant-workflows How to trigger workflows for tenant to apply tenant level customization in notifications. Tenants represents a segment that user belongs to. It can be organizations, teams within an organization, subsidiary companies or different product lines in the same business. In SuprSend, you can dynamically manage tenant level notification customizations. This includes the ability to [customize template design or content](/docs/tenants#using-tenant-components-in-templates), set different [notification preferences at the tenant level](/docs/tenant-preference), route notifications via [tenant vendors](/docs/tenant-vendor) or even have a distinct set of notifications for each tenant. All of this can be managed dynamically within SuprSend by defining tenant settings once and passing `tenant_id` in your event or workflow call. [Know more about how to manage tenants within SuprSend here](/docs/tenants). 📘 If you have a use case where the entire user base and notifications differ between two distinct business lines within your company, we recommend using different workspaces to manage these notifications effectively. This approach ensures clear separation and management of workflows, templates, and user data, tailored specifically to the unique requirements of each business line. ## Sending notifications for a tenant By default, all notifications (aka workflow) are triggered for default tenant (your organization). You can override tenant by passing `tenant_id` in your workflow or event instance. Passing `tenant_id` in your workflow will: * Pick tenant properties for sending custom notification content or design for the tenant (by replacing tenant variables in the template). * Pick per-tenant preferences while executing the workflow. * Override vendor details for the tenant if set. This is especially useful for cases where you are sending notifications on behalf of your customers and they want the notifications to be sent with their email domain and tenant/brand handles or you have different customer applications for sending messages on chat applications (Slack and MS Teams). Below is a sample of how you can override tenant\_id in your workflow or event call ```python Sample Workflow (Python) theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={...}, tenant_id = "_tenant_id_", idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```python Sample Event (Python) theme={"system"} from suprsend import Event distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" event_name = "product_purchased" properties = {...} } # Pass tenant_id in event instance event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties, tenant_id = "_tenant_id_") # Track event response = supr_client.track_event(event) print(response) ``` *** # Tenants Source: https://docs.suprsend.com/docs/tenants Learn what tenants stand for and how you can customize notifications for each tenant. If you handle communications to end users on behalf of your customers, you might be tasked with handling custom styling in the messages. This can be quite cumbersome if you have to maintain these customisation for every tenant in your codebase. Tenants empower you to send personalized communication on behalf of your customers using a single API. You just have to store tenant guidelines like logo, name, colors, social links and other custom properties for your customers once and then use your tenant variables in templates to dynamically render the template for each of your customer. You can programmatically create / update tenant using either one of our backend SDKs ([Python](/docs/python-tenants), [Node](/docs/node-tenants), [Java](/docs/tenants-java), [Go](/docs/tenants-go)) or through SuprSend dashboard. We'll cover the method to create tenant using SuprSend dashboard. ## Creating / Updating tenant on SuprSend platform Go to Tenants page from Side Navigation panel. On the tenants page, you'll see a default tenant created. For every organization, we create a default tenant which represents org level settings like org logo, colors, social links etc. You can create a new tenant by clicking on "New Tenant" button. This will open a form to add **`tenant id`** and **`tenant name`**. Add the required fields and click on **`Create tenant`** * **Tenant id** is a unique identifier for your tenant and can't be changed once the tenant is created. You'll have to pass tenant\_id in your workflow / event calls to render tenant level customization in your templates. (if no tenant\_id is passed, default tenant properties are picked). So, add a tenant\_id which is unique and easily identifiable for you. * **Tenant name** can be your customer company / organization name This will open tenant details page. Fill tenant details and Save changes. You can always come back and edit it again later Here are the form fields and its description | Field | Type | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Tenant ID | string of max character length of 64 characters with allowed characters (\[a-z0-9\_-] that is alphanumeric characters, \_ (underscore) and - (hyphen).) | tenant\_id is used to identify tenant in workflow and event call | | Tenant Name | single line text | Name of the company / organization | | Logo | image | Tenant logo. Used to render logo in email tenant header | | Tenant colors | 6 digit color code | Tenant color settings are mainly used while designing templates. Primary tenant color is used in button, header and footer border in tenant email template. If you don't provide any of the colors for the tenant, SuprSend will assume you want to use the default values, so color settings will automatically be set to the color settings of `default` tenant. | | Social Links | URL | URLs of social media accounts of the tenant It is used to render social media logos in tenant email footer. if the link is not passed, that logo will not be shown in the footer. | | Custom Properties | JSON | Custom properties associated with the tenant. The option to add custom properties is currently not available on the dashboard but you can update it using backend SDK or APIs | You can use [HTTP API](/reference/overview) or manage tenants using one of our backend SDKs: 1. [Update tenant using python SDK](/docs/python-tenants) 2. [Update tenant using node SDK](/docs/node-tenants) 3. [Update tenant using java SDK](/docs/tenants-java) 4. [Update tenant using go SDK](/docs/tenants-go) ## Using tenant components in templates ### 1. Ready-to-use tenant components in email You can use tenant component in email to add ready-to-use header, footer and buttons in your email template. Tenant component automatically uses tenant logo, social links and primary color to style the email template. You'll find the tenant component in right side content menu in email editor. You can add tenant component and change block type to switch between header, footer and buttons You can change the component using standard customization options like padding, background color etc. to best suit your email template. ### 2. Use Tenant variable for all channels You can add tenant variables in all channel templates as `$tenant.`. e.g., if you have to add tenant\_name in your template, you can add it as `$tenant.tenant_name`. Also, when you type `{`, tenant variable will automatically come up in auto-suggestions for your to add in the template ## Triggering notification for your Tenant After adding the tenant variables in your template, you can add tenant\_id in your workflow or event trigger to trigger notification for that tenant. This will replace tenant variables with the properties of that tenant at run time. ### 1. Adding tenant\_id in workflow trigger ```python python theme={"system"} from suprsend import Workflow # Prepare Workflow body workflow_body = {...} # Add tenant_id in workflow instance wf = Workflow(body=workflow_body, tenant_id='_tenant_id') # Trigger workflow response = supr_client.trigger_workflow(wf) print(response) ``` ```javascript Node theme={"system"} const { Workflow } = require("@suprsend/node-sdk") // Prepare Workflow body const workflow_body = {...} // Add brand_id in workflow instance const wf = new Workflow(workflow_body, {brand_id: "_brand_id_"}) // Trigger workflow const response = supr_client.trigger_workflow(wf) // returns promise response.then((res) => console.log("response", res)); ``` ```go go theme={"system"} // Create workflow body wfBody := map[string]interface{}{...} wf := &suprsend.Workflow{ Body: wfBody, BrandId: "_brand_id_", } // Call TriggerWorkflow to send request to Suprsend _, err = suprClient.TriggerWorkflow(wf) if err != nil { log.Fatalln(err) } ``` ### 2. Adding tenant\_id in event trigger ```python python theme={"system"} from suprsend import Event distinct_id = "distinct_id" # Mandatory, Unique id of user in your application event_name = "event_name" # Mandatory, name of the event you're tracking # Properties: Optional, default=None, a dict representing event-attributes properties = { "key1":"value1", "key2":"value2" } # Add tenant_id in event instance event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties, tenant_id='_tenant_id_') # Track event response = supr_client.track_event(event) print(response) ``` ```javascript Node theme={"system"} const { Event } = require("@suprsend/node-sdk"); const distinct_id = "distinct_id" // Mandatory, Unique id of user in your application const event_name = "event_name" // Mandatory, name of the event you're tracking // Properties: Optional, a dict representing event-attributes const properties = { "key1":"value1", "key2":"value2" } // Add brand_id in event instance const event = new Event(distinct_id, event_name, properties, {brand_id: "_brand_id_"}) // Track event const response = supr_client.track_event(event) response.then((res) => console.log("response", res)); ``` ```go go theme={"system"} // Add brand_id in event instance ev := &suprsend.Event{ EventName: "product_purchased", // Mandatory, name of the event you're tracking DistinctId: "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", // Mandatory, Unique id of user in your application Properties: map[string]interface{}{...}, BrandId: "_brand_id_", } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` ### Possible customizations at Tenant Level * **Custom Template Designs for Each Tenant**: You can create unique template designs for each tenant using tenant-specific properties. Refer to the [above section](/docs/tenants#using-tenant-components-in-templates) for details on how to implement this. * **Route Tenant messages from their own vendors**: You can direct messages through a tenant's designated vendors by configuring [tenant vendors](/docs/tenant-vendor) on the vendor settings page. Messages will be sent via the vendor associated with the tenant\_id provided in the trigger. If no tenant vendor is set, the system will use the default vendor settings. * **Tenant level preference setting**: Tenants can control what notifications should be sent to their associated users and the their default preference setting. It can be used for cases where admin wants to control the notifications that their teammates receive or when you are sending notifications to multiple tenant's users and tenant wants to control the notifications their users receive, on which channels and at what frequency. [Read more about tenant preferences here](/docs/tenant-preference). *** # Tenants Source: https://docs.suprsend.com/docs/tenants-go Learn how to create, update, fetch, & list tenants using Go SDK. Tenants (previously named as brands) are used for white labeling notifications, personalizing template content or capturing admin preferences for another entity/organization. Tenants are workspace-level entities and by default, a tenant with `tenant_id="default"` (representing your organization) is created your workspace. Read more about tenants [here](/docs/tenants). ## Create / Update Tenant This method will create a new tenant or update an existing tenant. ```go Request theme={"system"} tenantPayload := &suprsend.Tenant{ TenantName: suprsend.String("Tenant Name"), Logo: suprsend.String("Tenant logo url"), PrimaryColor: suprsend.String("#FFFFFF"), SecondaryColor: suprsend.String("#000000"), TertiaryColor: nil, SocialLinks: &suprsend.TenantSocialLinks{ Facebook: suprsend.String("https://facebook.com/tenant"), Tiktok: suprsend.String("https://tiktok.com/tenant"), X: suprsend.String("https://x.com/tenant") }, Properties: map[string]interface{}{ "k1": "tenant settings 1", "k2": "tenant settings 2", }, } res, err := suprClient.Tenants.Upsert(context.Background(), "__tenant_id__", tenantPayload) if err != nil { log.Fatalln(err) } log.Println(res) ``` ## Get tenant ```go Request theme={"system"} tenant1, err := suprClient.Tenants.Get(context.Background(), "tenant_id") if err != nil { log.Fatalln(err) } log.Println(tenant1) ``` ```go Response theme={"system"} { "tenant_id":"tenant_id", "tenant_name":"ABC Company", "logo":"https://logo_url.png", "timezone":"America/New_York", "blocked_channels":[ "whatsapp", "email" ], "embedded_preference_url":"https://app.suprsend.com/preferences", "hosted_preference_domain":"preferences.suprsend.com", "primary_color":"#0751E8", "secondary_color":"#e308e3", "tertiary_color":"#1EE9F1", "social_links":{ "website":"https://www.suprsend.com/", "facebook":"", "linkedin":"", "x":"", "instagram":"", "medium":"", "discord":"", "telegram":"", "youtube":"", "tiktok":"" }, "properties":{ "address":"5678 Market Street Suite 300 San Francisco, CA 94103 USA" }, "updated_at":"2025-02-27T10:21:15.235666Z" } ``` ## List tenants By default, `limit=20`. The maximum value for `limit` is `1000`. ```go Request theme={"system"} tenantsList, err := suprClient.Tenants.List(context.Background(), &suprsend.TenantListOptions{Limit: 10}) if err != nil { log.Fatalln(err) } log.Println(tenantsList) ``` ```go Response theme={"system"} { "meta":{ "count":28, "limit":20, "offset":0 }, "results":[ { "tenant_id":"tenant_01", "tenant_name":"ABC Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" }, { "tenant_id":"tenant_02", "tenant_name":"CDE Company", "logo":"https://logo.url", ... "updated_at":"2025-03-13T18:16:35.744843Z" } ... ] } ``` ## Add tenant in Workflow ```go Request theme={"system"} wf := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, TenantId: "tenant_id" } ``` ## Add tenant in Event ```go Request theme={"system"} ev := &suprsend.Event{ EventName: "__event_name__", DistinctId: "_distinct_id_", Properties: map[string]interface{}{...}, TenantId: "tenant_id" } ``` *** # Tenants Source: https://docs.suprsend.com/docs/tenants-java Learn how to create, update, fetch, & list tenants using Java SDK. Tenants (previously named as brands) are used for white labeling notifications, personalizing template content or capturing admin preferences for another entity/organization. Tenants are workspace-level entities and by default, a tenant with `tenant_id="default"` (representing your organization) is created your workspace. Read more about tenants [here](/docs/tenants). ## Create / Update Tenant This method will create a new tenant or update an existing tenant. ```java Request theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendAPIException; public class Tenants { public static void main(String[] args) throws Exception { updateTenant(); } private static Subscriber updateTenant() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); // Create Tenant data JSON String tenantId = "br-01"; JSONObject payload = new JSONObject() .put("tenant_name", "awesome tenant") .put("logo", "https://ik.imagekit.io/l0quatz6utm/suprsend/staging/media/suprsend-only-logo_c8aa27faef118418e8c5bd7b31a1cafc74e09200.png") .put("primary_color", "#ff0000") .put("secondary_color", "#0000ff") .put("tertiary_color", "#00ffff") .put("social_links", new JSONObject() .put("website", "https://www.company.com") .put("youtube", "https://www.company.com/youtube") .put("x", "https://www.company.com/x") .put("facebook", "") .put("linkedin", "") .put("instagram", "") .put("discord", "") .put("medium", "") .put("telegram", "") .put("tiktok","") ) .put("properties", new JSONObject() .put("address", "my company address") ) ; try { JSONObject res = suprClient.tenants.upsert(tenantId, payload); System.out.println(res); } catch (SuprsendException e) { System.out.println(e); } } } ``` | Field | Description | | | :-------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | `tenant_id` (mandatory) | max 64 characters and should contains alphanumeric characters(a-z, 0-9), hyphen (-) and underscode(\_). tenant\_id is case-insensitive. Suprsend will first converts tenant\_id to lowercase before storing it or doing any sort of comparison on it. | | | `tenant_name` (mandatory) | name of the tenant. | | | `colors` (primary, secondary, tertiary) | colors settings are mainly used while designing templates. If you don't provide any of the colors for the brand, SuprSend will assume you want to use the default values, so color settings will automatically be set to the color settings of `default` brand. | | | `social_links` | URLs of social media accounts of the tenant. While updating a social link, if you pass its value as null, then SuprSend will ignore it. If you really want to remove the value you must the `value= " "` (instead of null). e.g. If you want to remove social\_link url for `facebook`, you must pass `facebook= " "`. Consider this code block for the same:`{ "social_links": {"facebook": " " } }` | | | `properties` | Custom properties associated with the tenant. Update operation is upsert (new properties are added to existing one's and if key is already present, value is overridden). | | All properties of the tenant can be referred as `{{$tenant.prop}}` (handlebars) or `data["tenant"].prop` in JSONNET format. ## Get tenant ```java Request theme={"system"} String tenantId = "_tenant_id_"; JSONObject res = suprClient.tenants.get(tenantId); System.out.println(res); ``` ```java Response theme={"system"} { "tenant_id": "_tenant_id_", "tenant_name": "Tenant Name", "logo": "https://logo.url", "primary_color": "#ff0000", "secondary_color": "#00ff00", "tertiary_color": "#0000ff", "social_links": { "website": "", "facebook": "", "linkedin": "", "x": "", "instagram": "", "medium": "", "discord": "", "telegram": "", "youtube": "", "tiktok": "" }, "properties": { "prop1": "value1", "prop2": "value2" } } ``` ## List tenants By default, `limit=20`. The maximum value for `limit` is `1000`. ```java Request theme={"system"} JSONObject res = suprClient.tenants.list(); //Get list with offset = 10 and limit = 20 //JSONObject res = suprClient.tenants.list(20,10); System.out.println(res); ``` ```java Response theme={"system"} { "meta": { "limit": 20, "offset": 0, "count": 1 }, "results": [ { "tenant_id": "tenant-id", "tenant_name": "Tenant Name", "logo": "https://logo.url", "primary_color": "#ff0000", "secondary_color": "#00ff00", "tertiary_color": "#0000ff", "social_links": { "website": "", "facebook": "", "linkedin": "", "x": "", "instagram": "", "medium": "", "discord": "", "telegram": "", "youtube": "", "tiktok": "" }, "properties": { "prop1": "value1", "prop2": "value2" } } ] } ``` ## Add tenant in Workflow ```java Request theme={"system"} WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, tenantId); ``` ## Add tenant in Event ```java Request theme={"system"} Event e = new Event(distinctId, eventName, eventProps, , brandId); ``` # Testing the Template Source: https://docs.suprsend.com/docs/testing-the-template How to send a test notification from the template editor to your device for actual message preview. We have given an option on the Templates page to "Test Template". This enables you to send test notifications directly from the template editor page and see how the actual message will look on user's device. The mock data added in the global `Mock Data` button will be used to render variables in the template. Make sure to add mock data for all the variables added in the template else the notifications will fail. When [Test Mode](/docs/developer/test-mode) is enabled in your workspace, template testing will also respect Test Mode settings, ensuring test notifications are delivered to your configured test channels instead of real users. Please follow below steps to trigger test template: Open template editor page. You'll see `Test Template` button on the top right side. Add `distinct_id` of user who will receive the notification. Use this `distinct\_id` of any existing user or add a new with one of [SuprSend SDKs](/docs/users#creating-user-profile-on-suprsend). 1. Add distinct\_id in the input field and Click on `Search`. 2. This will load the list of all active channels for that user. You can deselect the channels that you want to exclude from sending the notification Notification category and Vendor settings corresponding to "Transactional" Category will be used to trigger the notification. 1. Click on `Trigger Test using Mock Data` button. This will trigger the notification 2. Once the notification is triggered, go to logs page to see the status ![](https://mintlify.s3.us-west-1.amazonaws.com/suprsend/images/docs/4421d8d-Frame_63_1.png) *** # Throttle Source: https://docs.suprsend.com/docs/throttle Put a rate limit on number of workflow executions per user in a given time frame. Throttle function allows you to limit the number of workflow executions per user in a given time frame. For instance, in monitoring and alerting systems, you can set a throttle to limit alerts to 1 every 30 minutes. This prevents IT teams from being overwhelmed by excessive alerts during maintenance windows. All triggers coming after the throttle limit is reached will be discarded and will not result in workflow trigger. Throttling is important to avoid notification fatigue and to ensure that user is not bombarded with too many notifications in a short span of time. It also helps avoid unnecessary notifications stemming from system issues or repetitive user actions. ## How throttle works You can set throttle limit in workflow settings. A typical throttle configuration comprises 2 key properties: * **Max Executions** \- Maximum number of workflow executions permitted in the time window. * **Window** \- The duration for which the throttle count is maintained. e.g., if max executions is set to 3 and the window is 15 minutes, a maximum of 3 workflows can execute within a 15-minute window. You can define window as `**d **h **m **s` . Now, on each workflow trigger, it checks for the number of workflows executed in the relative window from your trigger and skips the execution if count exceeds `Max executions`. *** # Time Window Source: https://docs.suprsend.com/docs/time-window Use time window in workflow to send notification in a given datetime range and user's timezone. Time window is used to schedule messages in a fixed time range and user's timezone. e.g., if you want to send messages to users only in their office working hours or only on weekends in their timezone. You can also define different schedule for different channels; e.g. send Inbox messages to users during their working hours and send Email out of their office hours. ## How time window works Time window introduces a wait until the time window starts and all subsequent steps in the workflow will be delayed by the same time. Let's understand this with below example: Imagine a workflow that sends alerts when a task status changes. To avoid sending alerts outside of working hours, you set the Time Window to 9:00 AM - 5:00 PM in the recipient's timezone. * **Trigger**: User updates the task status on July 22 at 9:00 PM UTC. * **Time Window**: Any day, 9:00 AM - 5:00 PM. * **Recipient’s Timezone**: Europe/London (UTC+1). In this case, the workflow will wait for 11 hours (until July 23, 9:00 AM Europe/London) before sending the notification. ## Setting up time window Time window has 3 inputs 1. **Day**: Specify the days of the week or month when the notification should be sent. Options include: * Every day * Weekdays (Monday - Friday) * Selected days of the week (e.g. Mondays and Wednesdays of the week) * Selected days of the month (e.g. 1st, 3rd, and 5th Mondays of the month or 1st - 5th day of the month). 2. **Time**: Define the time within the selected days when the notification should be sent. This is calculated based on the recipient’s timezone. 3. **Timezone:** Set the timezone for the Time specified. You can set recipient's timezone here which will be dynamically calculated for each recipient. You can set recipient timezone in recipient profile with `$timezone` key in HTTP API or `user.set_timezone()` method from your backend or Frontend SDKs. Timezones should be in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format, such as `America/New_York`. If a recipient's timezone is not set, it will default to the account timezone specified in the [SuprSend dashboard -> Account settings](https://app.suprsend.com/en/account-settings/general). If no account timezone is set, UTC (Coordinated Universal Time) will be used as the final fallback. You can also configure multiple Time Windows in `OR`. e.g., send notifications Monday to Thursday from 9:00 AM - 5:00 PM or on Friday from 9:00 AM - 1:00 PM. ## Changing recipient's timezone while they wait in a time window If a recipient’s timezone is updated while they are waiting in a Time Window, the delay will be recalculated based on the new timezone. If the adjustment results in a time past the end of the current Time Window, the delivery will be pushed to the start of the next schedule. e.g., * A user enters a Time Window set for 9:00 AM - 5:00 PM (recipient’s timezone) at 7:00 AM UTC. * The recipient's timezone is America/New\_York (UTC-5), so the delay is 7 hours until 9:00 AM (America/New\_York). * If the timezone is updated to Europe/London (UTC+1) while waiting, the delay will be recalculated to 1 hour. ## Some common notification use cases * Send transactional alerts during user's working hours to avoid disturbances. * Recommended to use in your engagement notifications to send notification to all users at same time in their timezone. * Set time window as per country compliance rules for SMS and WhatsApp: Many countries have regulations that require you to send promotional WhatsApp and text messages within specific time windows. For instance, the TCPA restricts text message delivery to between 9 AM and 8 PM in the recipient's local time zone. * Digest or batch notifications where the batch window is different from sending time. e.g., send a summary of all activities performed the previous day at 9:00 AM. Use a batch node to collect alerts within a 24-hour window and then schedule the delivery for 9:00 AM in the recipient's timezone using time window. ## Frequently asked questions Yes, one of the reasons for storing timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format was to take care of these clock shifts across the globe. Triggers before the Time Window start and during its open time (between start time and end time) will be accumulated in the batch. For instance, with a Time Window set from 9:00 AM to 5:00 PM UTC and a 2-hour batch window, triggers from 5:00 PM to 9:00 AM (pre-window open) and 9:00 AM to 11:00 AM (during batch window) will be batched together. So, if you have to create a digest of all events coming outside office hours and send it next day at office start time, you can achieve it with time window and batch node in series with batch open for 5 minutes (just to accumulate all events). *** # Translations Source: https://docs.suprsend.com/docs/translations Learn how to use translations to localize your notifications in SuprSend. import { Card, CardGroup, Steps, Tabs, Tab, Accordion, CodeGroup } from '@mintlify/components'; Translations localize the notifications you send with SuprSend. Instead of maintaining separate templates for each language, you can create a single template with translation keys that are automatically replaced with the appropriate language content based on the user's locale setting. **Key concept**: One template + multiple translation files = localized notifications for all your users. ## How translations work SuprSend handles localization behind the scenes. When a user receives a notification, it: | Step | Process | How it works | Example | | ---- | --------------------------- | ------------------------------------- | ------------------------------------ | | 1 | Detect user locale | Identifies from user profile | `es_MX` | | 2 | Find best translation file | Uses smart fallback logic | `es-MX.json` → `es.json` → `en.json` | | 3 | Replace keys | Substitutes with translated content | `{{t "welcome"}}` → `¡Bienvenido!` | | 4 | Send localized notification | Delivers in user's preferred language | Delivered in Spanish (Mexico) | All this happens automatically — no extra code on your end. Translation files must follow this structure for SuprSend's system to work correctly. ### Translation directory structure Translation files must be organized by locale codes: ```bash theme={"system"} translations/ ├── en/ # English (base language) │ ├── en.json # General translations │ └── auth.en.json # Authentication-specific translations └── en-GB/ # English (Great Britain) ├── en-GB.json # General translations └── orders.en-GB.json # Order management translations ``` **Naming convention**: SuprSend supports both underscore (`en_GB`) and hyphen (`en-GB`) formats. Internally, both map to the same locale. **Format**: `{language}.json` Contains translations for a specific language without regional variations. `en.json` (English) **Format**: `{language}_{region}.json` or `{language}-{region}.json` Contains translations for a specific language in a specific region or country. `en_GB.json` or `en-GB.json` (English for Great Britain) **Format**: `{namespace}.{language}.json` Contains translations for a specific feature or module in a specific language. `auth.en.json` (Authentication module in English) ### Fallback logic SuprSend automatically selects the best available translation for each user based on their locale. If a translation is missing, the system uses an intelligent fallback chain to ensure the notification is still localized. **Fallback order:** 1. **Exact locale match** → `es-MX.json` (Spanish – Mexico) 2. **Language-only fallback** → `es.json` (Spanish – General) 3. **Default fallback** → `en.json` (Base language) ```mermaid theme={"system"} flowchart TD Start([User Locale: es-MX
Looking for key: 'welcome']) --> Step1{Check es-MX.json
Spanish – Mexico} Step1 -->|Key Found ✓| Use1[Use es-MX translation
¡Bienvenido a México!] Step1 -->|Key Not Found ✗| Step2{Check es.json
Spanish – General} Step2 -->|Key Found ✓| Use2[Use es translation
¡Bienvenido!] Step2 -->|Key Not Found ✗| Step3{Check en.json
English – Default} Step3 -->|Key Found ✓| Use3[Use en translation
Welcome!] Step3 -->|Key Not Found ✗| Error[Display Key Name
'welcome'] style Use1 fill:#d4edda,stroke:#28a745,stroke-width:3px,color:#000 style Use2 fill:#fff3cd,stroke:#ffc107,stroke-width:3px,color:#000 style Use3 fill:#cce5ff,stroke:#007bff,stroke-width:3px,color:#000 style Error fill:#f8d7da,stroke:#dc3545,stroke-width:3px,color:#000 style Start fill:#e7f3ff,stroke:#0066cc,stroke-width:2px ``` **Best practice:** Always maintain an `en.json` file as the base language. It ensures your system always has a fallback even if locale-specific keys are missing. ### Translation key types Translation keys can be organized using two main approaches: **Simple key-value pairs:** ```json theme={"system"} { "welcome": "Welcome!", "goodbye": "See you later!" } ``` ```handlebars theme={"system"} {{t "welcome"}} ``` **Benefits:** * **Simple and straightforward** - Easy to understand and implement * **Quick to set up** - No need to plan namespaces upfront * **Perfect for small projects** - Ideal when you have limited translation keys * **Direct access** - Shorter syntax in templates **Potential conflict example:** ```json theme={"system"} { "title": "Welcome to our app", // Auth page title "title": "Order confirmation" // Order page title - CONFLICT! } ``` **Organize by feature or module:** ```json theme={"system"} { "auth:login": "Sign in to continue", "auth:logout": "Sign out", "order:confirmed": "Order confirmed", "order:shipped": "Your order has shipped" } ``` ```handlebars theme={"system"} {{t "auth:login"}} ``` **Benefits:** * **Avoid conflicts** - No duplicate key names across features * **Better organization** - Group related translations together * **Easier maintenance** - Update feature-specific translations independently * **Team collaboration** - Different teams can work on different namespaces * **Scalable structure** - Perfect for large applications with many features ## Get started Implementing translations in SuprSend requires three steps: Navigate to [Dashboard](https://app.suprsend.com) → **Developers** → **Translations** and upload JSON files. Upload translation files interface **Commit changes** to make translations live. Learn more about [managing translation files](#managing-translation-files). Set the user's locale via API using the `$locale` parameter. The locale determines which translation file SuprSend will use for that user's notifications. Use standard ISO locale codes like `en_US`, `es_ES`, `fr_FR`, etc. Add translation keys to templates using the `{{t}}` tag: ```handlebars theme={"system"} Subject: {{t "greeting"}} Body: {{t "welcome_message"}}, {{user.first_name}}! ``` **Preview your translations:** 1. **Open template editor** and add `{{t}}` tags 2. **Click Preview** (top right corner) 3. **Select different locales** from the dropdown 4. **Verify output** matches expectations Language dropdown for previewing translations 🎉 Notifications will now automatically display in each user's preferred language. ## Using translations in templates The `{{t}}` tag is the primary tool for adding translations to templates. It replaces translation keys with localized content based on the user's locale: **Basic key replacement** - Direct translation key lookup without additional parameters. ```handlebars theme={"system"} {{t "welcome"}} {{t "goodbye"}} ``` **Organized key structure** - Uses namespace prefixes to group related translations and avoid key conflicts. ```handlebars theme={"system"} {{t "auth:login"}} {{t "demo:name"}} ``` **Dynamic content injection** - Passes data from the application context into translation strings for personalized content. ```handlebars theme={"system"} {{t "greeting" name=user.first_name}} {{t "order_confirmation" order_id=order.id customer=user.name}} ``` ### Interpolation with variables Translations can be made dynamic by injecting data from the application. Variables are passed as parameters and interpolated into the translation strings: ```handlebars theme={"system"} {{t "greeting" name=user.first_name}} {{t "order_confirmation" order_id=order.id customer=user.name}} ``` **Translation file structure:** ```json theme={"system"} { "greeting": "Hello, {{name}}!", "order_confirmation": "Hi {{customer}}, your order #{{order_id}} is confirmed!" } ``` ### Pluralization SuprSend supports plural forms automatically based on the count variable: ```handlebars theme={"system"} {{t "items_count" count=cart.items.length}} ``` **Translation file:** ```json theme={"system"} { "items_count": { "zero": "No items", "one": "1 item", "other": "{{count}} items" } } ``` **Rule logic:** * `count=0` → zero * `count=1` → one * `count≥2` → other The `count` variable is built-in. Always define `zero`, `one`, and `other` forms for consistent behavior across locales. This logic adapts automatically based on locale rules. ### Combining translation with other helpers The `{{t}}` tag can be combined with other Handlebars helpers to create more dynamic content: **Text transformation** - Applies uppercase formatting to translated text. ```handlebars theme={"system"} {{t "welcome" | uppercase}} ``` **Fallback handling** - Provides a default value when translation is missing or empty. ```handlebars theme={"system"} {{default (t "name") "Guest"}} ``` **Conditional rendering** - Shows different translations based on application state or user properties. ```handlebars theme={"system"} {{#if user.is_premium}} {{t "premium_welcome"}} {{else}} {{t "standard_welcome"}} {{/if}} ``` **Iterative translation** - Applies translations to each item in a collection with item-specific data. ```handlebars theme={"system"} {{#each items}}
  • {{t "item_name" name=this.name price=this.price}}
  • {{/each}} ```
    **Email-specific data** - Uses tenant or organization data in email translations. ```handlebars theme={"system"} {{t "email_subject" company=tenant.name}} ```
    ### JSONNET support JSONNET is useful when translation behavior depends on dynamic logic — for instance, tailoring messages based on subscription tier or user status. For advanced conditional logic beyond simple Handlebars helpers, use JSONNET expressions directly in your templates: **In template:** ```handlebars theme={"system"} {{t "welcome_message" is_vip=user.is_premium tier=user.subscription_tier}} ``` **In translation file (simple):** ```json theme={"system"} { "welcome_message": "Welcome, {{tier}} member!" } ``` **For complex logic, use JSONNET in templates**: ```jsonnet theme={"system"} local message = if user.is_premium then "Welcome, VIP member!" else "Welcome!"; { subject: message, body: suprsend.t("welcome_body", {name: user.name}) } ``` JSONNET is useful for complex business logic. For most cases, simple variable interpolation and Handlebars helpers are sufficient. ### Single template architecture SuprSend uses a single template architecture with translation keys, eliminating the need for separate templates per language: ```bash theme={"system"} templates/ ├── welcome_en.html ├── welcome_es.html ├── welcome_fr.html └── welcome_de.html ``` ```bash theme={"system"} templates/ └── welcome.html translations/ ├── en.json ├── es.json ├── fr.json └── de.json ``` **Implementation:** ```handlebars theme={"system"}

    {{t "welcome_title"}}

    {{t "welcome_message" name=user.first_name}}

    ``` **Benefits:** * **Simplifies updates** - Change styling once, applies to all locales * **Ensures styling consistency** - No risk of layout differences between languages * **Reduces maintenance overhead** - Single template to maintain instead of multiple This architecture reduces template maintenance overhead and ensures consistent styling across locales. ## Managing translation files Translation file workflow: 1. **Add translated files** to the dashboard 2. **Commit changes** to make translations live 3. **Delete translations** by removing files and publishing changes SuprSend automatically maintains [version control](#version-control) for all translation files, tracking every change with timestamps and author information. To delete a translation, you'll need to publish the changes after removing the files. SuprSend automatically tracks all translation changes: ### What's tracked * **📅 Complete history** - All changes with timestamps and authors * **⏪ Rollback capability** - Revert to any previous version instantly * **📊 Audit trail** - Full compliance and debugging support * **🔍 Change comparison** - See exactly what changed between versions ### How to rollback 1. **Navigate** to translations dashboard 2. **Click** on translation file → **Version History** tab 3. **Select** version to restore → Click **Rollback** 4. **Confirm** changes take effect immediately **Rollback impact**: Rolling back immediately affects all users in that locale. Test thoroughly before rolling back. ### Version history features * **📈 Visual diff** - See line-by-line changes between versions * **👤 Author tracking** - Know who made each change * **🏷️ Version labels** - Tag important versions for easy reference * **📋 Change notes** - Add descriptions to significant updates ## Automating translation with CLI Manage your translations programmatically using SuprSend's Command Line Interface. Pull, push, and sync translation files with your local environment. For detailed CLI commands and workflows, see our [CLI translations documentation](/docs/cli-translations). ## Translation Management APIs Programmatically manage translations using SuprSend's Management APIs. Upload, delete, and manage translation files via API. For complete API reference and examples, see our [Management API translations documentation](/docs/management-api-translations). ## Best practices * 🔑 Keep keys short\*\*: `auth:login` > `authentication_login_button_text` * 🔢 Always define plural forms\*\*: `zero`, `one`, `other` for consistent behavior * 📄 Maintain `en.json`\*\* as your canonical source * 🏷️ Use translation keys everywhere\*\* — avoid raw text in templates * 🏢 **Don't translate brand names** — keep them consistent across locales ## Supported locales SuprSend supports standard ISO locale codes following the `language_COUNTRY` format: | Locale Code | Language | Country/Region | | ----------- | --------------------- | ---------------------- | | `af_ZA` | Afrikaans | South Africa | | `ar_AE` | Arabic | United Arab Emirates | | `ar_SA` | Arabic | Saudi Arabia | | `ar_EG` | Arabic | Egypt | | `az_AZ` | Azerbaijani | Azerbaijan | | `be_BY` | Belarusian | Belarus | | `bg_BG` | Bulgarian | Bulgaria | | `bn_BD` | Bengali | Bangladesh | | `bs_BA` | Bosnian | Bosnia and Herzegovina | | `ca_ES` | Catalan | Spain | | `cs_CZ` | Czech | Czech Republic | | `cy_GB` | Welsh | United Kingdom | | `da_DK` | Danish | Denmark | | `de_AT` | German | Austria | | `de_CH` | German | Switzerland | | `de_DE` | German | Germany | | `el_GR` | Greek | Greece | | `en_AU` | English | Australia | | `en_CA` | English | Canada | | `en_GB` | English | United Kingdom | | `en_IE` | English | Ireland | | `en_IN` | English | India | | `en_NZ` | English | New Zealand | | `en_US` | English | United States | | `en_ZA` | English | South Africa | | `es_AR` | Spanish | Argentina | | `es_CL` | Spanish | Chile | | `es_CO` | Spanish | Colombia | | `es_ES` | Spanish | Spain | | `es_MX` | Spanish | Mexico | | `es_PE` | Spanish | Peru | | `es_VE` | Spanish | Venezuela | | `et_EE` | Estonian | Estonia | | `eu_ES` | Basque | Spain | | `fa_IR` | Persian | Iran | | `fi_FI` | Finnish | Finland | | `fr_BE` | French | Belgium | | `fr_CA` | French | Canada | | `fr_CH` | French | Switzerland | | `fr_FR` | French | France | | `gl_ES` | Galician | Spain | | `gu_IN` | Gujarati | India | | `he_IL` | Hebrew | Israel | | `hi_IN` | Hindi | India | | `hr_HR` | Croatian | Croatia | | `hu_HU` | Hungarian | Hungary | | `hy_AM` | Armenian | Armenia | | `id_ID` | Indonesian | Indonesia | | `is_IS` | Icelandic | Iceland | | `it_CH` | Italian | Switzerland | | `it_IT` | Italian | Italy | | `ja_JP` | Japanese | Japan | | `ka_GE` | Georgian | Georgia | | `kk_KZ` | Kazakh | Kazakhstan | | `km_KH` | Khmer | Cambodia | | `kn_IN` | Kannada | India | | `ko_KR` | Korean | South Korea | | `ky_KG` | Kyrgyz | Kyrgyzstan | | `lo_LA` | Lao | Laos | | `lt_LT` | Lithuanian | Lithuania | | `lv_LV` | Latvian | Latvia | | `mk_MK` | Macedonian | North Macedonia | | `ml_IN` | Malayalam | India | | `mn_MN` | Mongolian | Mongolia | | `mr_IN` | Marathi | India | | `ms_MY` | Malay | Malaysia | | `my_MM` | Burmese | Myanmar | | `ne_NP` | Nepali | Nepal | | `nl_BE` | Dutch | Belgium | | `nl_NL` | Dutch | Netherlands | | `no_NO` | Norwegian | Norway | | `pa_IN` | Punjabi | India | | `pl_PL` | Polish | Poland | | `pt_BR` | Portuguese | Brazil | | `pt_PT` | Portuguese | Portugal | | `ro_MD` | Romanian | Moldova | | `ro_RO` | Romanian | Romania | | `ru_RU` | Russian | Russia | | `si_LK` | Sinhala | Sri Lanka | | `sk_SK` | Slovak | Slovakia | | `sl_SI` | Slovenian | Slovenia | | `sq_AL` | Albanian | Albania | | `sr_RS` | Serbian | Serbia | | `sv_SE` | Swedish | Sweden | | `sw_KE` | Swahili | Kenya | | `ta_IN` | Tamil | India | | `te_IN` | Telugu | India | | `th_TH` | Thai | Thailand | | `tr_TR` | Turkish | Turkey | | `uk_UA` | Ukrainian | Ukraine | | `ur_PK` | Urdu | Pakistan | | `uz_UZ` | Uzbek | Uzbekistan | | `vi_VN` | Vietnamese | Vietnam | | `zh_CN` | Chinese (Simplified) | China | | `zh_HK` | Chinese (Traditional) | Hong Kong | | `zh_TW` | Chinese (Traditional) | Taiwan | | `zu_ZA` | Zulu | South Africa | Don't see your locale? SuprSend supports all standard ISO 639-1 language codes and ISO 3166-1 alpha-2 country codes. Contact support if you need help with a specific locale. ## Troubleshooting Even with proper setup, issues may be encountered. Here are common problems and their solutions: **Possible causes:** * Changes not committed * Invalid JSON format * User locale not set * Key missing in fallback files **Solutions:** 1. **Check if changes are committed** - Uncommitted translations won't be used 2. **Verify file format** - Ensure valid JSON syntax 3. **Check user locale** - Confirm user profile has correct locale set 4. **Review fallback chain** - Check if key exists in fallback files **Possible causes:** * Incorrect locale format * Case-sensitive key names * Outdated version **Solutions:** 1. **Verify locale format** - Use `en_US` or `en-US`, not `en-us` 2. **Check key name** - Keys are case-sensitive (`greeting` ≠ `Greeting`) 3. **Review version history** - Ensure you're on the latest version **Possible causes:** * Incorrect syntax * Variable name mismatches * Wrong namespace format **Solutions:** 1. **Syntax check** - Ensure proper `{{t "key"}}` format 2. **Variable names** - Check variable names match translation file 3. **Namespace format** - Use `namespace:key`, not `namespace.key` # Trigger Workflow Source: https://docs.suprsend.com/docs/trigger-workflow Learn how to trigger workflows using any of the available methods. You can trigger workflows designed on SuprSend dashboard via making a [direct call](/docs/trigger-workflow#triggering-workflow-via-api) to `workflows.trigger` endpoint or via [event trigger](/docs/trigger-workflow#event-based-trigger). In SuprSend, we refer events as user-initiated actions, such as social media interactions, or system-generated events like pending payments. User needs to be created beforehand for event based triggers. [Direct API trigger](/docs/trigger-workflow#triggering-workflow-via-api) is a straightforward way to get started, as you can include recipient channel information directly in the API call and doesn't require prior user creation to initiate the notification. ## Triggering workflow via API It is a unified API to trigger workflow and doesn't require user creation before hand to trigger notification. Recommended for platforms transitioning their existing notifications to SuprSend. If you are using our frontend SDKs to configure notifications and passing events and user properties from third-party data platforms like Segment, then [event-based trigger](/docs/trigger-workflow#event-based-trigger) would be a better choice. It is a new workflow method and is available in below SDK versions (Python >= v0.11.0, Go >= v0.5.1, Node >= 1.10.0 and Java >= 0.7.0). Upgrade to the latest version if you are on older SDK versions. Here is a list of integrations that you can use to trigger workflow over API: ### Sample Payload Here is a sample payload of direct API trigger ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true, }, "recipients": [ # notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true, }, # notify object {"object_type": "teams", "id": "finance", "$skip_create": true}, ], "data": { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234", }, }, tenant_id="tenant_id1", idempotency_key="_unique_identifier_of_the_request_", ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const { Suprsend, WorkflowTriggerRequest } = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true }, "recipients": [ // notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true }, // notify object { "object_type": "teams", "id": "finance", "$skip_create": true } ], "data": { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234" } }; // Create workflow instance const w1 = new WorkflowTriggerRequest(body, { tenant_id: "tenant_id1", idempotency_key: "_unique_identifier_of_the_request_" }); // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err: = suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient * suprsend.Client) { // Create WorkflowRequest body wfReqBody: = map[string] interface {} { "workflow": "_workflow_slug_", "actor": map[string] interface {} { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true, }, "recipients": [] map[string] interface {} { // notify user { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": [] string { "abc@example.com" }, "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true, }, // notify object { "object_type":   "teams", "id": "finance", "$skip_create": true, }, }, // data can be any json/serializable map structure "data": map[string] interface {} { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234", "spend_amount": "$10", }, } w1: = & suprsend.WorkflowTriggerRequest { Body: wfReqBody, TenantId: "tenant_id1", IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err: = suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; String tenantId = "tenant_id1"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("actor", new JSONObject() .put("distinct_id", "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08") .put("name", "actor_1") .put("$skip_create", true) ) .put("recipients", new JSONArray() // notify user .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$email", Arrays.asList("abc@example.com")) .put("name", "recipient_1") .put("$preferred_language", "en") .put("$timezone", "America/New_York") .put("$skip_create", true) ) // notify object .put(new JSONObject() .put("object_type", "teams") .put("id", "finance") .put("$skip_create", true) ) ) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer _API_key_' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name": "actor_1", "$skip_create": true }, "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email": ["abc@example.com"], "name": "recipient_1", "$preferred_language": "en", "$timezone": "America/New_York", "$skip_create": true }, { "object_type": "teams", "id": "finance", "$skip_create": true } ], "data": { "first_name": "User", "invoice_amount": "$5000", "invoice_id": "Invoice-1234" } } ' ``` To prevent automatic creation of an actor, or recipient (user/object) in SuprSend (the case where they already exist in your system), you can use the `"$skip_create": true` flag. This can be applied inside the actor, individual user recipient objects, or object recipient objects. **Payload Schema** | Property | Type | Description | | ---------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `workflow` | string | Slug of designed workflow on SuprSend dashboard. You'll get the slug from workflow settings. | | `actor` (*optional*) | string / object | Includes distinct\_id and properties of the user who performed the action. You can use it for [cross-user notifications](/docs/trigger-workflow#sending-cross-user-notifications) where you need to include actor properties in notification template. Actor properties can be added as `$actor.`. | | `recipients` | array of string / array of objects | List of users who need to be notified. You can add up to 100 recipients in a workflow trigger. You can either pass recipients as an array of `distinct_id` (if user is pre-synced in SuprSend database) or [define recipient information inline](/docs/trigger-workflow#identifying-recipients-inline). To notify [object](/docs/objects), pass object\_id and type in recipient JSON. | | `data` | object | variable data required to render dynamic template content or workflow properties such as dynamic delay or channel override in send node. | | `tenant_id` | string | unique identifier of the [brand / tenant](/docs/tenants) | | `idempotency_key` | string | unique identifier of the request. We'll be returning idempotency\_key in our [outbound webhook response](/docs/outbound-webhook). You can use it to map notification statuses and replies in your system. | | `recipients[].$timezone` | string | to set recipient's timezone. Used to send notification in user's local timezone. You can pass timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | `recipients[].$preferred_language` | string | to set recipient's preferred language. This is to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py). | | `$skip_create` | boolean | Optional flag that can be used inside `actor`, or recipient payloads including both `user`, or `object`. When set to `true`, SuprSend will **not create the user or object** if it doesn’t already exist in the system. | ### Identifying recipients inline One of the benefits of using direct workflow trigger is that you can identify recipients inline. You can include recipient channel information, their channel preferences, and their user properties along with the workflow trigger. Upon triggering the workflow, the recipient will be automatically created in the SuprSend database in the background. This facilitates dynamic synchronization of your user data within SuprSend and eliminates the need for any migration efforts on your end to start sending notifications from SuprSend. You can also use recipient properties in your template as `$recipient.`. This is how the complete recipient payload with look like ```json json theme={"system"} { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "$channels":["email","inbox"], "user_prop1":"value_1", "$preferred_language":"en", "$timezone": "America/New_York" } // Object will be identified by {object_type, id}. Rest of the payload will be same as user. { "object_type": "departments", "id":"finance", "$email":["abc@example.com"], "user_prop1":"value_1" } ``` | Property | Type | Description | | ------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `distinct_id` | string | Unique identifier of the user to be notified. | | communication channels ($email, $sms, etc). | array of string | You can pass user channel information using `$` key. This will override existing channel value from the user profile and use the channel value defined in the key for notification trigger. The same channel information will also be appended to user profile in the background Refer how different communication channels can be passed [here](/docs/trigger-workflow#add-user-communication-channel) | | `$channels` | array of string / dicts | Use it to pass user's channel preference in the payload. You can always use our [in-build preference APIs](/docs/user-preferences) to maintain user notification preferences. Preferences defined within SuprSend will automatically apply with workflow trigger. By default, notifications will be sent to all channels defined in the workflow delivery node. However, if you have a scenario where user has specific channel preference for a notification (e.g. they only want to receive payment reminders via email), you can include that preference in the workflow payload. This will ensure that notifications are sent only to the channels specified in the \$channels key. The supported channel values are `email, sms, whatsapp, androidpush, iospush, slack, webpush, ms_teams`. | | `$preferred_language` | string | to set recipient's preferred language. This is to support localization in notification content. You can pass the language in `ISO 639-1 2-letter` format. [Refer all language codes here](https://github.com/suprsend/suprsend-py-sdk/blob/v0.12.0/src/suprsend/language_codes.py) . | | `$timezone` | string | to set recipient's timezone. Used to send notification in user's local timezone. You can pass timezone in [IANA (TZ identifier)](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) format. | | \* | key-value pair | You can pass other user properties to render dynamic template content in key-value pair as `"user_prop1":"value1"` . Extra properties will be set in subscriber profile (as subscriber properties) which can then be used in the template as `$recipient.`. | #### Add user communication channel ```json json theme={"system"} "$email":["user@example.com"], "$whatsapp":["+15555555555"], "$sms":["+15555555555"], "$androidpush": [{"token": "__android_push_token__", "provider": "fcm", "device_id": ""}], "$iospush":[{"token": "__ios_push_token__", "provider": "apns", "device_id": ""}], "$slack": [{ "email": "user@example.com", "access_token": "xoxb-XXXXXXXX" }] // slack using email "$slack": [{ "user_id": "U/WXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack using member_id "$slack": [{ "channel": "CXXXXXXXX", "access_token": "xoxb-XXXXXX" }] // slack channel "$slack": [{ "incoming_webhook": { "url": "https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXX" } }] // slack incoming webhook "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "conversation_id": "19:c1524d7c-a06f-456f-8abe-xxxx" }] // MS teams user or channel using conversation_id "$ms_teams": [{ "tenant_id": "c1981ab2-9aaf-xxxx-xxxx", "service_url": "https://smba.trafficmanager.net/amer", "user_id": "29:1nsLcmJ2RKtYH6Cxxxx-xxxx" }] // MS teams user using user_id "$ms_teams": [{ "incoming_webhook": { "url": "https://wnk1z.webhook.office.com/webhookb2/XXXXXXXXX" } }] // MS teams incoming webhook ``` ### Sending notification to multiple recipients Recipients in workflow call is an array of `distinct_ids` or recipient objects. You can pass up to 100 recipients in a single workflow trigger. SuprSend will internally convert it into multiple workflow triggers, one for each recipient in the array. ```json json theme={"system"} "recipients": [ { "distinct_id": "id1", "$email":["id1@example.com"], "name":"recipient_1" }, { "distinct_id": "id1", "$email":["id2@example.com"], "name":"recipient_2" } ] ---- OR ------ "recipients": ["id1","id2"] ``` We recommend you to use [lists](/docs/lists) and [broadcasts](/docs/broadcast) to send notifications to a user list larger than 1000 users. This approach allows for bulk processing within SuprSend, resulting in significantly faster delivery compared to individual workflow calls. Sending individual workflows to a large set of users may introduce delays in your notification queue and is not an optimized way of handling bulk trigger. ### Sending cross-user notifications In scenarios where you need to notify a group of users based on another user's action, such as sending a notification to the document owner when someone comments on it, you can specify the actor in your workflow call. This allows you to use actor's name or other properties in your notification template. Actor properties can be included in the template as `$actor.`. Sample template with actor and recipient properties: ```text text theme={"system"} //handlebar template Hi {{$recipient.name}}, {{$actor.name}} added {{length comments}} new comments on the {{doc_name}}. //Rendered content Hi recipient_1, actor_1 added 2 new comments on the annual IT report. ``` ```json API request theme={"system"} { "workflow": "new_comment", "actor": { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "name":"actor_1" }, "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$email":["abc@example.com"], "name":"recipient_1" } ], "data":{ "doc_name": "annual IT report", "date": "2024-01-01", "comments":["change the date","rest looks good"] } } ``` ## Event based trigger It is a cleaner way of triggering notifications where your user sync is separate and events are generated from multiple sources, backend systems, Frontend applications (user actions on the platform) or CDP platforms like Segment. Please Note that the user profile should be created beforehand for `distinct_id` passed in your event call. If user is not present, it will discard the event call. Object triggers are not currently supported in event. Please [get in touch](mailto:support@suprsend.com) if you have this requirement. Below is a sample event call to trigger payment reminder workflow: ```python Python theme={"system"} from suprsend import Event # Track Event Example distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" event_name = "Payment Pending" properties = { "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties) # Track event response = supr_client.track_event(event) print(response) ``` ```javascript Node.js theme={"system"} const { Event } = require("@suprsend/node-sdk"); // Track Event Example const distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" const event_name = "Payment Pending" const properties = { "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } const event = new Event(distinct_id, event_name, properties) // Track event const response = supr_client.track_event(event) response.then((res) => console.log("response", res)); ``` ```java Java theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.Event; public class Event { public static void main(String[] args) throws Exception { trackEvent(); } private static Subscriber trackEvent() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); String distinctId = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08"; String eventName = "Payment Pending"; JSONObject eventProps = new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000"); .put("invoice_id", "Invoice-1234"); Event e = new Event(distinctId, eventName, eventProps); // Track event JSONObject response = suprClient.trackEvent(e); System.out.println(response); } ``` ```go Go theme={"system"} ev := &suprsend.Event{ EventName: "Payment Pending", DistinctId: "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", Properties: map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/event/ \ --header 'Authorization: Bearer _API_key_' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "distinct_id": "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", "event": "Payment Pending", "properties": { "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` Here is a list of all integrations that you can use to trigger event: (Web) (Android) (App) (App) Customer Data Platform (CDP) ## Triggering workflow using google sheets Recommended for one-time notification trigger. This can be used by growth or product teams to trigger one time notifications for lead generation, sales cold messaging or to send announcements and product updates. We do not recommend sending more than 10,000 notifications using google sheets as each row in google sheet trigger converts to 1 workflow request and might take a lot of time to process. Also, since triggers via google sheets are generally promotional notifications, we recommend using one of the [promotional sub-categories](docs/notification-category#creating-sub-categories-for-preference-management) to trigger this notification. [Read more about categories and how they impact your send latencies.](/docs/notification-category) Here's a step-by-step guide on how to send notifications using google sheets: All the static content can be designed on the template, and all the variable data defined within `{{...}}` will be passed from the Google Sheet at the time of trigger. Each row in the sheet corresponds to one recipient. * `distinct_id` **column**- this is the unique identifier of the user who needs to be notified. * **Dynamic data columns**- you need to create one column each for the dynamic data (aka variables) in your template. Note that variable names are case sensitive. \ \ If this is the template content:`Hi {{name}}, your {{Event}} is scheduled at {{Schedule}}. See you there.` , you'll have to create a column for each template variable -`name` , `Event` , and `schedule` in your sheet. * **User Channels columns**- Next, create columns for user channel details. These channel columns are necessary to pass channel information that may not be present in the user profile. \ It's always a good practice to include channel information if you're unsure of its presence in the user profile. You can pass channels as `WA` for whatsapp, `Email` for email and `SMS` for SMS. \ For Whatsapp and SMS, you need to enter country code infront of the mobile number as `+917123xxxxxx` . * `SuprSend Status` **column**- Fill the value `TBT` in rows for which you want to trigger the notification. Once, the notification is triggered, the status changes to `OK` .\ 📘 TIP: Google Sheet doesn't allow to start a field with \*`+`\*\*. To enter in + format, use string function: `="+917123xxxxxx"` In the Navbar of Google Sheets, click on `Extensions` and select `Apps Script` It will open Apps Script in a new tab. Remove the default information present in the editor, and copy-paste the following in the editor. ```json Appscript theme={"system"} //Enter your workspace key, secret, template slug, workflow name & category const workspace_key = "__API_KEY__"; const workspace_secret = "__API_SECRET__"; const template_slug = "__TEMPLATE_SLUG__"; const workflow_name = "__WORKFLOW_NAME__"; const category = "promotional" // Map your column names to channels if need be // Or ensure you use following names for your columns to directly map them to channels // distinc_id for user's distinct id // $sms for user's mobile number // $email for user's email // $whatsapp for user's WhatsApp // If you have other names of your columns you can modify following two lines accordingly const channel_col_names = {"WA":"$whatsapp","Email":"$email","SMS":"$sms"}; const distinct_id_col_name = 'distinct_id' //--------- No Editing required below -----------------// function Trigger_Workflows() { var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); var headers = data[0]; for (var i = 1; i < data.length; i++) { var response = convert_row_to_payload(data[i], headers); if (response.status === "TBT") { make_api_request( response.payload, sheet.getRange(i + 1, parseInt(response.status_col) + 1) ); } } } function convert_row_to_payload(data, headers) { let status = ""; let status_col = -1; let user = { distinct_id: null, $email: [], $sms: [], $whatsapp: [], }; let payload = {}; payload.data = {}; let private_channelkeys = ["$sms", "$whatsapp", "$email", "distinct_id"]; for (var i = 0; i < headers.length; i++) { if (data[i].length !== 0) { if ( channel_col_names[headers[i]] || private_channelkeys.includes(headers[i]) ) { if (headers[i] !== "distinct_id") { if (user[channel_col_names[headers[i]]]) user[channel_col_names[headers[i]]].push(data[i]); if (user[headers[i]]) user[headers[i]].push(data[i]); } else { user[headers[i]] = data[i]; } } if (headers[i] !== "SuprSend Status") { payload.data[headers[i]] = data[i]; } else { status = data[i]; status_col = i; } } } user["distinct_id"] = payload.data[distinct_id_col_name]; //user["is_transient"] = true; //Uncomment if user is temporary payload.users = [user]; payload.name = workflow_name; payload.notification_category = category; payload.template = template_slug; return { payload: JSON.stringify(payload), status: status, status_col: status_col, }; } function make_api_request(payload, cell) { const uri = "/" + workspace_key + "/trigger/"; const url = "https://hub.suprsend.com" + uri; const md5 = MD5(payload); const now = new Date().toISOString(); const message = "POST" + "\n" + md5 + "\n" + "application/json" + "\n" + now + "\n" + uri; const byteSignature = Utilities.computeHmacSha256Signature( message, workspace_secret ); const signature = Utilities.base64Encode(byteSignature); var options = { method: "POST", contentType: "application/json", headers: { Authorization: workspace_key + ":" + signature, Date: now, }, payload: payload, muteHttpExceptions: true, }; cell.setValue("Processing..."); try { var response = UrlFetchApp.fetch(url, options); cell.setValue(response.getContentText()); } catch (error) { cell.setValue("Error : " + error); } } function onOpen() { var ui = SpreadsheetApp.getUi(); // Or DocumentApp or FormApp. ui.createMenu("SuprSend") .addItem("Trigger SuprSend Workflow", "Trigger_Workflows") .addToUi(); } function MD5(input, isShortMode) { var isShortMode = !!isShortMode; // Be sure to be bool var txtHash = ""; var rawHash = Utilities.computeDigest( Utilities.DigestAlgorithm.MD5, input, Utilities.Charset.UTF_8 ); if (!isShortMode) { for (i = 0; i < rawHash.length; i++) { var hashVal = rawHash[i]; if (hashVal < 0) { hashVal += 256; } if (hashVal.toString(16).length == 1) { txtHash += "0"; } txtHash += hashVal.toString(16); } } else { for (j = 0; j < 16; j += 8) { hashVal = (rawHash[j] + rawHash[j + 1] + rawHash[j + 2] + rawHash[j + 3]) ^ (rawHash[j + 4] + rawHash[j + 5] + rawHash[j + 6] + rawHash[j + 7]); if (hashVal < 0) { hashVal += 1024; } if (hashVal.toString(36).length == 1) { txtHash += "0"; } txtHash += hashVal.toString(36); } } // change below to "txtHash.toUpperCase()" if needed return txtHash; } ``` You'll find following information to be added in your script from SuprSend dashboard. | Data | Description | | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `api-key` | API Key for your workspace. From left navigation panel, select settings -> API Keys. | | `api-secret` | API Key for your workspace. From left navigation panel, select settings -> API Keys. | | `template-slug` | Add the template slug of the template that you want to trigger. You can copy the the template slug by clicking on copy icon next to the template name on template details page. | | `Workflow Name` | Give a name to identify your workflow. It'll help you locate the sent workflow on the workflow listing page. You can see the notification performance on **Workflow -> Analytics** page. | | `category` | Provide notification category. We recommend using promotional sub-category for sending engagement notifications. You can read more [Notification Categories here](/docs/notification-category). | After reloading, you will find a new option named "**SuprSend**" in the navigation bar. On clicking it, you will see the option to **Trigger SuprSend Workflow**. On triggering, the script will pick up all the rows which have value in the column name "**SuprSend Status**", and will make an API call to SuprSend. For the successful API call, the status will change to `OK`. You can check the status of your notification trigger on the `Logs` page. *** # Twilio Source: https://docs.suprsend.com/docs/twilio Guide to connect your Twilio account with SuprSend to send SMS notifications. ## Pre-Requisites You'll need Twilio account to complete this tutorial. You can use your existing Twilio account to integrate, or [Create a Twilio account](https://www.twilio.com/try-twilio) ## Twilio integration on SuprSend account On the SuprSend dashboard, go to vendor page from side panel and click SMS -> Twilio from the list of Vendors. This will open vendor details page as shown below: | Form Field | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nickname | You can give any name which may help you to identify this account easily | | Account SID | You will get this API Key from your Twilio dashboard (keys and tokens section). SuprSend uses this information to send SMS on your behalf via your registered Twilio account. | | Auth Token | You will get the token from your Twilio dashboard (keys and tokens section). SuprSend uses this information to send SMS on your behalf via your registered Twilio account. | | Messaging service SID | Messaging Service is the container for multiple Twilio message senders (e.g. phone numbers, WhatsApp senders). You'll have to create a messaging service and add your from: twilio number as the sender. [Refer section](/docs/twilio#how-to-get-messaging-service-sid-from-your-twilio-account) to get your messaging service SID. | | Price per notification | This is the amount you pay per SMS notification to Twilio. It helps us to calculate, estimate and optimise your cost spent on notifications. | ### How to get Account SID and Auth Token from your Twilio account Login to Twilio account and follow the below steps: **Verify phone numbers for sending through test credentials** 👍 Please note that you can only send to verified mobile numbers (referred as verified caller IDs) using Twilio account test credentials. You can add verified caller IDs from [here](https://console.twilio.com/us1/develop/phone-numbers/manage/verified) ## How to get messaging service SID from your Twilio account To send messages through Twilio account, we need a Twilio messaging service with phone number attached to it. Twilio Messaging service is a pool of sender phone numbers having the same features and configurations. It helps in 2 major use cases, 1. **Sending to candidate's around different geographical regions**. For example - if you want to send to both US and Indian numbers, you can add one sender number for US and one sender mobile number for India in the same messaging service and twilio will automatically choose the number belonging to the nearest region to send to your end users 2. **Managing the scale of your notifications**. In case of high message volumes, twilio distributes your outbound messaging traffic evenly across the phone numbers in your Messaging Service so that the delivery doesn't get affected You can refer to Twilio's excellent [documentation on messaging service](https://www.twilio.com/docs/messaging/services) to know more about it To get a messaging service SID, you'll have to purchase a twilio phone number and add it to a messaging service ## How to purchase a Twilio phone number Navigate to the "[Phone Numbers](https://console.twilio.com/us1/develop/phone-numbers)" page of the Twilio console. Then, click on "**Buy a Number**" from left side *# Phone Numbers* menu. On "Buy a Number" page: 1. Select the relevant country 2. Check the "SMS" box in capabilities section to filter for numbers with SMS capability 3. Click on **Search**. This will load the list of available numbers with SMS capability in your region and the price of each number listed in a table. 4. Select whichever available number appeals to you. Click on "Buy" button next to the mobile number to buy that number. 5. This will open a modal, scroll down and click on "Buy +1 xxx ..." button and then select, "Setup number." This will load a phone number configuration page. We don't need to change anything for this tutorial. If you are new to Twilio, your trial account is initially funded with enough money to purchase a phone number, so you won't have any expenses while completing this guide. You may be asked to comply with the regulatory requirements associated with the number-thorough regulatory [documentation](https://www.twilio.com/guidelines/regulatory) is provided by Twilio. Be sure to use your number responsibly and comply with any requirements. ### Twilio messaging service Now that we have a phone number, we need to create a Twilio Messaging Service that uses this number. Navigate to the "[Messaging -> "Services"](https://console.twilio.com/us1/develop/sms/services?frameUrl=%2Fconsole%2Fsms%2Fservices%3Fx-target-region%3Dus1)" section on [explore products](https://console.twilio.com/develop/explore) page. On "Messaging Services" page: This will open "Messaging Service Setup". Enter the value of two fields: "Friendly Name," and "Use Case". 1. Let's use `SuprSend SMS service` for `Friendly name` 2. There are several `Use Case` options. For this tutorial, we can select `Notify my users`. Next, click `Create Messaging Service` Here, add the number that we have bought as sender to this messaging service Once the number is added, Click on "Set up integration" ![](https://files.readme.io/d81eb01-twilio_add_number.gif) On "Integration" page, scroll down to add "Callback URL." Add SuprSend webhook URL here to track delivery reports on SuprSend dashboard: * Webhook URL - `https://hub.suprsend.com/webhook/twilio/sms` ![](https://files.readme.io/f2f5f51-Add_callback_URL.png) Then Click on `Complete Messaging Service Setup` to complete the setup ![](https://mintlify.s3-us-west-1.amazonaws.com/suprsend/images/docs/6bb711c-image_96.png) ## Setting callback URL in Twilio account One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. By default SuprSend Webhook URL is configured in Twilio. This will help you to track the message delivery status and failure logs on SuprSend dashboard. If you have a requirement where you would want to get the DLR reports in your backend, contact us at [support@suprsend.com](mailto:support@suprsend.com) **Vendor switching not supported** Vendor switching from Twilio to any Indian vendor is currently not supported due to DLT restrictions. So, if you are using twilio as your default vendor for any of the communication type, you'll not be able to choose a different vendor in other communication type. Though choosing a different vendor in other workspace will still be supported *** # Twilio (SMS) Source: https://docs.suprsend.com/docs/twilio-sms-errors Possible SMS delivery errors reported by Twilio and their resolutions. In this guide, we'll cover all the errors returned by the vendor when SuprSend initiates a send call to the vendor. In case of vendor error, you'll see `Trigger failed` or `Failure by Vendor` status in the third step of the logs. Hover over the failed chip to access the specific error message. | Error | How to solve? | | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Status: 400 - ApiError 21211: The 'To' number xxxx is not a valid phone number. | **Possible causes:** (1) You attempted to initiate text to the phone number which is invalid or was incorrectly formatted. Twilio accepts phone numbers in [E164 format](https://www.twilio.com/docs/glossary/what-e164): `[+] [country code] [subscriber number including area code]` (2) You tried to send a message from a Twilio phone number to itself (from and to numbers in the request are same)**Solutions:**- Verify phone number has country code attached to it. Eg. `+14155552671` (3) Check if the [country code](https://en.wikipedia.org/wiki/List_of_country_calling_codes#Alphabetical_listing_by_country_or_region) for the phone number is correct. (4) Verify that the `From` and `To` numbers in the request are not same. | | 21610: failed | **Possible cause:** The person you are trying to message has opted out of receiving messages from your Twilio phone number by responding with [opt-out keywords](https://support.twilio.com/hc/en-us/articles/223134027-Twilio-support-for-opt-out-keywords-SMS-STOP-filtering-) like `STOP`. You'll not be able to send messages to this number unless user resubscribes to the messages by texting [opt-in keywords](https://support.twilio.com/hc/en-us/articles/223134027-Twilio-support-for-opt-out-keywords-SMS-STOP-filtering-) like `START` **Solutions:**- Try [adding a preference centre](/docs/embedded-preference-centre) in your application for users to explicitly define what notifications they want to receive and on which channel, so that you don't end up losing touch with the user on the entire channel out of notification fatigue. -Try adding **multi-channel communication** so that you can reach out to users on other active channels when they disengage from SMS. You can also request the recipient to resubscribe to your messages by reaching out on other channels. -Before sending messages to a recipient, ensure they have consented to receive messages from you. Please read these guidelines to understand [messaging opt-in requirements and best practices](https://www.twilio.com/blog/ctia-messaging-principles-and-best-practices). | | 30006: undelivered | **Possible cause:** The destination number is unable to receive this message. Potential reasons could include trying to reach a landline or, in the case of short codes, an unreachable carrier. **Solution:** Use the [Lookup API](https://www.twilio.com/docs/lookup/v2-api) to determine if the number is indeed a landline. If is not, try with an alternative phone number type. | | 30003: undelivered | `To` number is unreachable. **Possible causes:**- The destination handset you are trying to reach is switched off, out of network zone or unavailable. - The device cannot receive SMS (e.g., the phone number belongs to a landline) - There is an issue with the mobile carrier**Solutions:** try sending the message later when the user will be reachable. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if SMS is unreachable. | | 21614: failed | **Possible causes:**- The number that you are trying to send message to is invalid or incorrectly formatted. Twilio accepts phone numbers in [E164 format](https://www.twilio.com/docs/glossary/what-e164): `[+] [country code] [subscriber number including area code]` - The number you provided may be a landline number. - If you are attempting to send SMS to Internet of Things (IoT) or machine-to-machine (M2M) numbers, the numbers may use a non-standard format that Twilio has not added to their number validation system yet.**Solutions:**- Verify phone number has country code attached to it. Eg. `+14155552671` - Check if the [country code](https://en.wikipedia.org/wiki/List_of_country_calling_codes#Alphabetical_listing_by_country_or_region) for the phone number is correct. - Confirm that the number you are sending to is not a landline, using the [Lookup API](https://www.twilio.com/docs/lookup/v2-api). - If you are attempting to send SMS to an IoT or M2M number, check whether the number format is different from the standard mobile numbers in that country or locality. Often, these numbers have additional digits or unusual formats which do not pass Twilio's number validation. If you believe this is the issue, please [contact Twilio Support](https://support.twilio.com/hc/en-us/requests/new) for assistance. | | Status: 401 - ApiError 20003: Authenticate | **Possible causes:** Twilio authentication credentials added in vendor form is not correct. It could happen if you are - using the wrong combination of Account SID and Auth Token or previous auth token or API Key has been deleted., - using test credentials to send notification to live users - using sub-account credentials to access master account - account is suspended or closed - extra characters or spaces in the supplied credentials - Attempted API Key is for the incorrect Twilio Region**Solution:** Verify all details mentioned above. Refer [twilio vendor integration guide](/docs/twilio) for more details. | | 30007: undelivered | **Possible cause:** Your message content was flagged or filtered (blocked) by Twilio or by the carrier. This may be done by Twilio for violating Twilio’s [Messaging Policy](https://www.twilio.com/en-us/legal/messaging-policy) or [Acceptable Use Policy](https://www.twilio.com/en-us/legal/aup), or by a wireless carrier for violating carrier rules or regulations. Examples of messaging that would be blocked by Twilio are spam, phishing, and fraud. Twilio’s filtering system is in place to protect mobile subscribers from spam or other forms of malicious or unwanted messages. **Solutions:**- Ensure your messaging use case complies with Twilio's [Messaging Policy](https://www.twilio.com/en-us/legal/messaging-policy) or [Acceptable Use Policy](https://www.twilio.com/en-us/legal/aup) - Review the information in [How Does Message Filtering Work?](https://support.twilio.com/hc/en-us/articles/223181848-How-Does-Carrier-Filtering-Work-) to understand what causes filtering. - See [How do I prevent my Twilio messages from being filtered (blocked)?](https://support.twilio.com/hc/en-us/articles/1260803966670-How-do-I-prevent-my-Twilio-messages-from-being-filtered-blocked-) for specific tips on avoiding message filtering. - If you believe your messages are compliant with Twilio and carrier policies, please collect 3 or more examples of Message SIDs that have the “undelivered” status with error 30007, and then [contact twilio Support team](https://www.twilio.com/login?g=%2Fconsole%2Fsupport%2Ftickets%2Fcreate%3F\&t=bc3ebe256e9d1f70c00f8056ead4789b1bffa201c82f27b4ae76384081399fe1). | | 30008: undelivered | **Cause:** Unknown error **Possible Solutions:**- Check that the phone you were sending to is turned on - Ensure that the phone is not roaming off network. Twilio doesn't guarantee message delivery on roaming phones. - Try sending to other phones who have the same mobile carrier (you can use our [Lookup API](https://www.twilio.com/docs/lookup/v2-api) to determine the carrier if you’re unsure). If messages to other phones go through, the issue is likely device related. Try rebooting the device or contact the mobile carrier for help. - If you are sending SMS from an alphanumeric sender ID, see if using a Twilio phone number works better. We’ve observed that certain networks may block alpha sender IDs. - If the recipient number is another Twilio number, ensure that number has an action configured for its Messaging capabilities. Choose any available option like "Webhook" or "TwiML Bin." - Try sending a shorter message to the phone, with simple content that does not include any special characters to verify whether the failure is related to concatenation or character encoding. If the issue still persists, try reaching out to [Twilio support team](https://support.twilio.com/hc/en-us/requests/new). | | Status: 400 - ApiError 21705: The Messaging Service Sid xxxx is invalid. | **Possible cause:** The messaging service SID is not valid or doesn't contain any sender. A Messaging Service requires at least one phone number, Alphanumeric Sender ID or short code to send messages. **Solution:** Add a sender to the messaging service or add the correct messaging SID. | | 30005: undelivered | `To` number you are trying to reach is unknown and may no longer exist. **Possible causes:**- The destination number you are trying to reach is unknown and may no longer exist. - The destination handset you are trying to reach is switched off, out of network zone or unavailable. - The device cannot receive SMS (e.g., the phone number belongs to a landline) - There is an issue with the mobile carrier\*\*Solutions: try sending the message later when the user will be reachable. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if SMS is unreachable. | | 30034: undelivered; 30034: failed | **Possible cause:** You are sending messages to the US using a US 10DLC number that is not associated with an approved A2P 10DLC Campaign. Messages sent to US numbers will not be delivered if they are sent from numbers that are not associated with an approved A2P 10DLC Campaign. This [guide](https://support.twilio.com/hc/en-us/articles/4418081745179-How-do-I-check-that-I-have-completed-US-A2P-10DLC-registration-) will help you determine if you have completed registration for A2P 10DLC. **Solution:** Associate your US 10DLC number with a registered A2P Campaign by adding it to the corresponding Messaging Service via the Twilio Console or API. Find out how to register using [this guide](https://support.twilio.com/hc/en-us/articles/1260801864489-How-do-I-register-to-use-A2P-10DLC-messaging-). For a step-by-step walk-through, check out [this video on resolving Error 30034](https://www.loom.com/share/2b796f0707be492ba951f33263fd3297). | | Status: 400 - ApiError 21617: The concatenated message body exceeds the 1600 character limit. | **Possible cause:** The message body exceeds maximum allowable text length of 1600 characters. Some glyphs such as Emoji, Emoticons or other special characters will be counted as multiple. **Solution:** Try reducing the text length or split it into multiple messages. | | 21612: failed | You have attempted to send to a number that is not currently reachable via Twilio SMS. **Possible causes:**- **Sender ID restrictions in the destination country**: Many countries limit which numbers, short-codes, and/or alphanumeric senders can be used in that region. [Consult the SMS guidelines for the destination region](https://www.twilio.com/en-us/guidelines/sms). -**Alphanumeric senderIDs:** If you are using an alphanumeric sender ID, the 'To' number must be in a country where alphanumeric sender IDs are supported. Certain countries require pre-registration of alphanumeric sender IDs. A list of countries where alphanumeric sender ID is supported and whether or not pre-registration is required can be found [here](https://support.twilio.com/hc/en-us/articles/223133767-International-support-for-Alphanumeric-Sender-ID). -**Number formatting:** The `To` or `From` in your message is incorrectly formatted. Twilio accepts phone numbers in [E164 format](https://www.twilio.com/docs/glossary/what-e164): \`\[+] \[country code] -**Twilio does not yet have service with the carrier** you are trying to reach. You can lookup the destination network via the [lookup api](https://www.twilio.com/docs/lookup/v2-api).**Solution:** Consult the linked documentation for each cause and update "To" and "From" number accordingly. | | 21408: failed | Permission to send an SMS has not been enabled for the region indicated by the 'To' number. **Possible cause:** You have attempted to send an SMS to a region that has not been enabled in your Twilio account's [Geo-Permissions](https://console.twilio.com/us1/develop/sms/settings/geo-permissions) settings. **Solution:** If you wish to send messages to this region, please enable the relevant permissions on your account using the [Messaging Geographic Permissions page](https://console.twilio.com/us1/develop/sms/settings/geo-permissions). | | 30410: undelivered | **Possible cause:** The messaging service SID doesn't contain any sender. A Messaging Service requires at least one phone number, Alphanumeric Sender ID or short code to send messages. **Solution:** Add a sender to the messaging service or add the correct messaging SID. | | 21704: failed | **Possible cause:** The provider used may be experiencing disruptions resulting in errors or request timeouts. Messages are failed and not retried to avoid duplicate message delivery. **Solution:** Please attempt to send messages at a later time. You can also setup [channel routing](/docs/smart-delivery) so that user gets notification on the next best channel if SMS is unreachable. | Refer to all possible twilio errors [here](https://www.twilio.com/docs/api/errors). # Unsubscribe from Object Source: https://docs.suprsend.com/docs/unsubscribe-from-object Dynamically remove users from object subscription within a workflow. You can use this node to dynamically remove recipient or actor from [object subscription](/docs/object-subscriptions) based on an event or action. If you have event-based data coming from third-party systems, you don’t need to write custom object subscription APIs in your codebase. Simply send events to SuprSend, and let the workflow handle object subscriptions. For example, when someone unsubscribes from a topic (like a tournament), you can automatically remove their subscription from the corresponding object. This ensures they receive all relevant notifications about topic-related activities without manual intervention. ### Compute objects at runtime You can either remove users from a pre-defined object or compute object on the fly using workflow input data. While defining object, both ID and type are mandatory. Type defines the group that object belong to (example, teams, departments). Dynamic object are defined in handlebars format as `{{...}}`. One common use case of creating object dynamically is when you need to compute objects based on user topic subscription. For example, there are multiple player tournaments happening and there are separate objects for each tournament. Object ID in such case can be `{{tournament_id}}` and type `tournaments`. Object subscription will only be removed if the computed object exists, else this action will fail. ### Subscription properties Subscription properties are the set of variables defining relationship between a subscriber and an object. During a workflow execution, these properties can be accessed under the `recipient.subscription` namespace as `recipient.subscription.property_key`. Subscription properties are added in [JSONNET](https://jsonnet.org/ref/language.html) format. You can [read more about object subscription here](/docs/object-subscriptions). *** # Update User Profile Source: https://docs.suprsend.com/docs/update-user-profile Update User Profile within workflow based on event or condition. Use this node to update recipient or actor profile within the workflow. Common use cases include fetching data during the workflow to update the user profile or updating the profile when a user successfully completes a step. For instance, during onboarding, when a user completes a step, you can update their `%completion` in the profile and later use it in workflow condition or template content. This functionality is especially powerful for event-based systems. If all updates, including user profile updates, are sent as events from your product or a third-party system, you can skip writing custom user update APIs in your codebase. Simply send events to SuprSend, and let the workflow handle user profile updation. ### Property JSON User properties are passed in [JSONNET](https://jsonnet.org/ref/language.html) format. You can pass static or dynamic properties. Dynamic properties can refer to all data available at the node's input, including the ones added during workflow execution. e.g., if a profile update node follows a batch, fetch, or webhook node, it can access data modified or added by those nodes. The following data types are supported: | Data Type | Referring in JSONNET | Description | | ------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Input Payload | pass as `data.` | This includes the data from your trigger payload and any data modified or added by nodes such as data transform, batch/digest, or webhook/fetch nodes before user update node. | | Actor | pass as `data["$actor"].` | Actor properties. In case of event trigger, `distinct_id` works both as actor and recipient and for inline workflow trigger, it is the `distinct_id` in actor object. | | Recipient | pass as `data["$recipient"].` | Recipient properties. It is the `distinct_id` in your event trigger or the key value defined in [override recipient](/docs/override-recipient-list) field. For inline workflow trigger, it is the `distinct_id` in recipient object. | | Tenant | pass as`data["$brand"].` | Tenant properties corresponding to the tenant\_id passed in workflow trigger. | *** # User Preference Source: https://docs.suprsend.com/docs/user-preferences Detailed guide on how to set up and capture user notification preferences in SuprSend. Preferences allow users to opt out of notification categories, choose their preferred communication channels, and set the frequency of notifications. Providing granular preferences reduces the likelihood of users disabling all notifications from your platform. In SuprSend, you can use [ready-made UI](/docs/user-preferences#controlling-what-categories-to-show-on-ui) and [APIs](/reference/overview) to manage multi-tenant preference use cases. This includes letting admins set preferences for internal teams and handle notifications for enterprise customers, where companies, customers, and end users have distinct preferences. ## How preferences work? Each user has a preference set within SuprSend, and users receive notifications in the category and channel they haven't opted out of. If a user hasn’t configured preferences for a category, the system uses [default preferences](/docs/user-preferences#set-default-preferences). Preference set contains three level of preferences: `channel_preferences` (for channel-level opt-outs), `categories`, and `opt_out_channels` within each category. In the below example, user has opted out of `email`, `invoice-ready` category and slack channel in `payment-reminder` category. ```json User Preference set theme={"system"} { "channel_preferences": [ { "channel": "email", "is_restricted": true } ], "categories": [ { "category": "invoice-ready", "preference": "opt_out" }, { "category": "payment-reminder", "preference": "opt_in", "opt_out_channels": [ "slack" ] } ] } ``` ## Set up preference categories in SuprSend ### Create notification category To set up notification preferences, start by creating notification categories. Instead of creating a separate category for each trigger, group similar notifications to keep categories more user-friendly. You can also add sections to organize your categories more effectively. > Don't add too many categories as it can drive users to use channel level opt-outs. You can update notification categories from the [Developers → Preference categories](https://app.suprsend.com/en/staging/developers/preference-categories) page. Add categories under any of the three predefined root categories: * **System**: For critical system notifications like OTPs, verification, and authentication. Users can't opt out of this category. * **Transactional**: Notifications related to user transactions on your platform, such as payment confirmations, delivery statuses, and comments. The default preference for this category opts users in, but you can change it at the individual category level. * **Promotional**: Marketing notifications like newsletters, announcements, deals, and discounts. The default preference for this category opts users out, but you can change it at the individual category level. ### Set default preferences If the user hasn't set preferences in a category, the system sends notifications based on default preferences. Default preferences are set in the notification category. The tenant can override the default preferences for their users, either through the [tenant preference API](/reference/update-tenant-default-preference) or from the tenant details page. You can set following default preferences at category level: 1. **On**: opt-in on all channels 2. **Off**: opt-out on all channels 3. **Can't Unsubscribe**: Use this to prevent users from completely opting out of critical notification categories, such as anomaly alerts or important transactions (e.g., completed trades, payments). Channel `opt-out` of does not block notifications from mandatory channels in this category. ### Map category to notifications Once set, publish your preference categories to see the latest categories in workflow. You can then map these categories when configuring your workflow for preferences to take effect. ## Capture user preferences You can capture user preference choice using an out-of-the-box hosted subscription page or by embedding a preference center within your product. ### Hosted Preference Page Once you publish preference categories, SuprSend automatically generates a dedicated webpage for collecting out-of-the-box user preferences. Users can set channel-specific preferences from the hosted page. e.g., if you add it to your email, the page displays only the relevant preference categories for email, and the user’s selections save for email. Include it in your templates using `{{$hosted_preference_url}}`. > This page currently hosts on the SuprSend domain, but you can reach out if you'd prefer it hosted on your own domain. ### Embed in your product You can load the preference interface inside your product using the ready UI code. SDKs exist in the languages below. Update your product preference page link on the tenant page and render it in templates using `{{$embedded_preference_url}}`. ### Channel specific unsubscription options Additionally to the preference center within SuprSend, communication channels provide their own opt-out options, which SuprSend manages internally. Gmail empathizes with the need to add an unsubscribe URL in email headers when sending bulk emails (5,000+ emails/day). Most email providers expect you to add your own unsubscription page or offer a basic all-or-nothing opt-out option. You can add `{{$hosted_preference_url}}` here to load the SuprSend hosted preference page from the email header. Companies also give option for users to load the Inbox preferences as a separate page within their Inbox or give a link to redirect users to Preference center in their product. For mobile push notifications, users typically manage their preferences through the app settings. The categories set in your workflow also pass as push categories. If you set preference categories, the system automatically reflects them in the user's app settings, loading similar preference controls. Users generally unsubscribe from Short Message Service (SMS) by replying "STOP." In SuprSend, the system marks the channel as inactive in the user profile upon receiving this response. ### Controlling what categories to show on UI It is always a good practice to show only the categories that are relevant to the user. There are 2 ways to achieve this: 1. **Turning off category visibility for all users of a tenant**: In a multi-tenant setup, if tenants or admins decide what categories their users should see, you can control this via the `visible_to_subscriber: false` flag in default [tenant preferences](/reference/update-tenant-default-preference). 2. **Setting custom preference UI for each user**: Use tags to show categories based on user roles, departments, or teams. These tags can then be used to filter categories in preference center. Tags can be added to categories and sections directly from the SuprSend Console → Preference Page. When a tag is assigned at the section level, it automatically applies to all categories under that section—so filtering by a section tag also filters its child categories. Tags Within Preference Categories You can filter categories using the tags query parameter in the API. This can be a simple tag match (e.g. `tags=tag1`) or a more advanced filter using logical operators. Supported operators: | Operator | Operand Datatype | Description | Example | | -------- | ---------------- | ----------------------------------------------------- | ----------------------------------- | | `exists` | boolean | Returns categories where any tag is set | `tags={"exists":true}` | | `not` | string | Excludes categories that have the specified tag | `tags={"not":"admin"}` | | `or` | array | return categories that have any of the mentioned tags | `tags={"or":["sales","marketing"]}` | | `and` | array | return categories that have all the mentioned tags | `tags={"and":["sales","manager"]}` | You can combine these operators for nested filtering like `tags={"or":[{"and":["sales","manager"]},{"and":["marketing","associate"]}]}`. ## Preference evaluation at runtime When a workflow triggers, SuprSend evaluates preferences for each recipient before each delivery node. If recipient preferences aren't set, the system picks the default preference setting. Once the user sets a preference in a category, changes to the default preference of existing categories won't affect recipient preferences. If you are triggering notification for a tenant, [tenant default preference](/docs/tenant-preference) setting takes precedence over default preference set at category level. The order of precedence is always `user > tenant > org` (set in notification category settings). However, if you turn off notifications in a category from the [tenant page](https://app.suprsend.com/en/demo/tenants), users will not receive notifications in that category. User preferences can change over time, and when checking a workflow run, you need to know what user preferences were at that time. You can view this using the step-by-step debugger in [workflow executions](https://app.suprsend.com/en/demo/logs/executions/?last_n_minutes=1440). You can also track when user updated their preference by filtering on `Subscriber preference update` in [request logs](https://app.suprsend.com/en/demo/logs/api/?last_n_minutes=1440). *** ## Frequently Asked Questions You can create sub-categories for different digest schedules or set the digest schedule in the user profile and pass a dynamic schedule in the workflow digest node. An option to set the digest schedule directly on your preference page will be available soon. You can manage this with tenant preferences. In the SuprSend system, each tenant represents an organization, and the administrator sets which categories to send to their internal team using the [tenant preference API](/reference/update-tenant-default-preference). Changing the default preference for a category doesn't affect users who have already made changes to that category. For categories where users haven't made any changes, the preferences update according to the new default settings. You can turn off categories for tenants from the tenant page on the SuprSend console. Turning off the preference for a category automatically removes it from the tenant preference APIs and UI view. To further apply this to the tenant's users, set `visible to subscriber` to `false` in the default tenant preferences to hide the category from the tenant's end users. *** # Users Source: https://docs.suprsend.com/docs/users What does users stand for and how to manage user profiles in SuprSend Users represent a person who will receive the communication. SuprSend identifies a user by a unique identifier distinct\_id and creates a profile for each user identified by this id. All the channel information like email id, phone number, and push tokens are attached to the user profile which in turn is used to trigger communication on the given channels ### Benefits of messaging users in SuprSend If you're used to sending notifications via single-channel APIs, the idea of storing user data in a messaging platform such as SuprSend may sound odd to you. Here are a few reasons why we store user data in SuprSend: 1. **Multi-channel notifications:** When you're triggering notifications on a single channel, you can pass the recipient's email address or phone number when you trigger the message. In a multi-channel platform like SuprSend, that would mean passing all the channel information every time you trigger a notification. By storing the channel data with SuprSend, you can update a user's channel information once, then reference them via their `distinct_id` from that point on. We take care of the rest. 2. **User properties in notification templates:** With SuprSend, you can also add custom properties to the user profile using one of our client-side SDKs. These properties can be used as a variable in templates and used to send dynamic content to the users based on their properties 3. **Notify the User sequentially with** [Smart Delivery](/docs/smart-delivery) Rather than sending your notifications to a user on all channels, you can notify the user sequentially on the best channel (which could be different for each user), till the time user interacts with a notification, and do not send subsequent notification. ### Creating user profile on SuprSend You can create user profile on SuprSend platform via SDKs or third-party connectors like segment. one of our Backend or Front SDKs: #### Via SDK You can either use [HTTP API](docs/update-user-profile) or our backend SDKs to create user profile from your backend systems or use Client side SDK to directly sync users from your mobile or web applications. **Available Backend SDK:** **Available Client side SDK:** 1. (for web applications) 1. (for mobile applications) 1. (for mobile ) 1. (for mobile applications) #### Via third-party connectors You can use connectors to directly sync users from your third-party data tracking platforms. *** # Validate Trigger Payload Source: https://docs.suprsend.com/docs/validate-workflow-payload Validate the data passed to workflow API or event properties using JSON schemas to catch payload mismatch errors at API level. ## Why Payload Validation Matters When you trigger a workflow, you pass data (payload) that is used to resolve workflow variables and populate dynamic content in templates. If the payload does not include all the variables expected in the workflow, the execution may fail at different stages. This can cause: * Hard-to-debug errors (logged at multiple workflow steps). * Inconsistent behavior (e.g., first notification fails but subsequent steps succeed). * Incorrect or incomplete notifications being sent. To avoid these issues, you can **validate the payload against a JSON Schema at the API level**.\ If the incoming payload does not conform to the schema, the trigger endpoint immediately returns an error response with detailed validation information. *** ## Creating a JSON Schema In SuprSend, a schema is created as an independent entity and then **linked to a workflow or event** for validation. You can create a schema in two ways: * **From UI** - [Developers -> Schemas](https://app.suprsend.com/en/staging/developers/schema) Page * **Using Management API** via the [Create Schema](/reference/upsert-schema) endpoint Schemas follow the standard [JSON Schema specification.](https://json-schema.org/draft/2020-12/schema). ### Example Schema: Order Placement Workflow This schema enforces the following rules: * `order_id` is **required** and must be a string. * `amount` is optional, but if provided, it must be an integer. ```json theme={"system"} { "type": "object", "$schema": "https://json-schema.org/draft/2020-12/schema", "required": ["order_id"], "properties": { "order_id": { "type": "string" }, "amount": { "type": "integer" } }, "additionalProperties": true } ``` With this schema linked: * If `order_id` is missing, not a string, or if `amount` is not an integer → API call fails. * Otherwise → workflow executes as expected. ## Linking Schema to Workflow There are two types of workflow triggers, so schema linking differs accordingly: * **API Trigger** - Link schema directly to the workflow in the Trigger Step → Schema section. * **Event Trigger** - Link schema to the linkedEvent from the Event Details page. * If multiple events are used in a trigger, the schema linked to each event is applied individually. * If an event has no linked schema, payload for that event are not validated, and the workflow executes normally. Both actions are supported via UI and using Management API in [workflow](/reference/create-update-workflow) or [event](/reference/create-event) upsert methods. ## Error Logging Schema validation errors are logged in two places: * **Immediate API Response** – returned directly to the client making the trigger call. * **[Request Logs](https://app.suprsend.com/en/staging/logs/requests?last_n_minutes=1440)** – stored for later inspection on SuprSend dashboard. If you run a test trigger from the UI, validation errors are displayed directly in the trigger modal. With schema validation, you can guarantee that workflows always start with valid, expected data — reducing runtime errors and preventing incorrect notifications. # Vendor Fallback Source: https://docs.suprsend.com/docs/vendor-fallback Guide to setup a fallback vendor to send notification when primary fails to deliver notification. We understand that ensuring notification delivery can be challenging, especially when you are dependent on third-party vendors to route your messages to the end user. Factors such as provider limitations, network failures, and service outages can disrupt the delivery of your notifications. To ensure that your messages get delivered promptly and reliably, we have added the support for vendor fallback. With vendor fallback, you can add multiple vendors to the fallback list and If one provider fails, your messages will automatically be rerouted through another provider, minimizing the risk of message delivery failures. It is essential for system notifications like OTPs, password reset mails and some of the transactional use cases like payment confirmation, stock market alerts etc. ## How Vendor Fallback works? Vendor Fallback logic comprises of 3 major components 1. **Vendor Priority List**: The list of vendor configurations to be tried in the order of their priority. 2. **Fallback time**: The time within which the notification should be routed to the next vendor if the first vendor fails to deliver the message 3. **Fallback Rule**: What are the cases in which fallback would happen. There are 2 cases in which fallback happens currently: 1. if delivery fails, fallback happens immediately 2. if delivery report is not received within fallback time, fallback happens after fallback time is over **Success Metric closes workflow and vendor fallback** 👍 There can be cases when the notification is delivered to the user but the vendor fails to send delivery report in the fallback period. This may lead to sending duplicate notification to the user. We solved this in vendor fallback method using success metric. Now, success metric not only stops [channel routing](/docs/smart-delivery), it also works as an indication that the user has received the message and stops vendor fallback. ## Example use case Let's take an example of OTP verification text (SMS) with the following condition: * Primary vendor- Messagebird * Fallback 1 vendor- Twilio * Fallback within- 30 secs (Fallback to Twilio if Messagebird fails to deliver SMS in 30 secs) * Success Metric- "OTP Verification" (do not fallback to Twilio if "OTP verification" event is received within 30 secs) In this case, if Messagebird fails to deliver the SMS within 30 secs of triggering the notification and the "OTP Verification" event is not received, SMS will be routed through Twilio. ## Setting vendor fallback from SuprSend dashboard Go to [SuprSend dashboard -> "Vendor Settings"](https://app.suprsend.com/en/production/vendors/sms/system) page and click on **Edit List**. You can add fallback rule for above example like this: You'll find all the saved vendor configurations in **"Unused Vendor Configuration"**. Enable it to add it in the fallback list. Once enabled, you can drag and drop to change the priority of vendors Save the changes after editing. *** # Overview Source: https://docs.suprsend.com/docs/vendors Learn about vendor management in SuprSend to send multi-channel notifications. You can use SuprSend to send multi-channel notifications with a single API. You just have to add your vendor details on SuprSend [vendor page](https://app.suprsend.com/en/staging/vendors/androidpush/fcm-androidpush) and your communication channel will be active. SuprSend has done integration with multiple vendors in each communication category. If you want us to add a new provider to this list, please let us know by writing us at [support@suprsend.com](mailto:support@suprsend.com). ## Add vendor details based on workspaces You may have different vendor accounts for staging and for production. Hence, we have given an option to add vendors based on your workspaces. Make sure that you add vendor details for each workspace, else communications will fail to deliver. ## Add vendors based on communication category In some of the communication categories like email and sms. We have given an option to add vendors based on communication category. At SuprSend, we have defined 3 types of communication categories: System, Transactional and Promotional. You can read more about these categories [here](/docs/notification-category). You can add different vendors for these categories, or have different accounts from the same vendor, depending on your need. ## Sandbox mode You don't have to add your vendor details in Sandbox environment. For ease of testing, we have integrated SuprSend's vendor accounts in Sandbox environment. Verified Channels in Sandbox ### Channel limits in Sandbox To ensure quality testing and prevent spam, notifications in Sandbox have the following limits: * **Email**: Up to 5 email addresses * **SMS**: Up to 5 phone numbers * **WhatsApp**: Up to 5 phone numbers * **Slack & Teams**: Currently unrestricted *** # Wait Until Source: https://docs.suprsend.com/docs/wait-until Learn to use Wait Until node in workflow to halt until a condition or max time is met. Wait Until is a conditional branch which halts the workflow until either the branch condition is met or the maximum wait duration is reached. It is most used for cases where sequential notifications need to be sent to users based on certain conditions, such as payment or task reminders. ## How wait until works? Wait Until has 2 branches- **Condition** and **Max time**. The user proceeds through the branch that is satisfied first. 1. [Condition:](/docs/wait-until#conditions) User proceeds through this path when a condition is met, such as an event being triggered, user properties, or the event properties that initiated the workflow. In case of payment reminder, condition could be the event trigger of `user completing payment`. 2. [Max time:](/docs/wait-until#max-time) This is the maximum time the user can wait for the condition to satisfy until the next step should be executed. If a person reaches the maximum wait time without achieving any other conditions, they'll progress through this branch. You can incorporate **multiple Wait Until** nodes in sequence to create a multi-step notification journey, where each notification is sent based on a specific condition being met. ## Conditions You can add conditions on event properties, user properties, and action / event performed by user. It's important to note that conditions are evaluated when the workflow execution reaches "Wait Until" node, meaning any events performed before this point will not satisfy the condition branch. Except for max time branch, all other branches are condition branches. To setup a condition, you can select the type of the condition and the expression defining the condition. ### Type: Event is performed Here, the branch condition satisfies when a given event is received during the wait time. This type of condition is ideal for scenarios like reminders, where you want to avoid sending a reminder if the necessary action is completed within the wait time. You can define the event name and apply conditions on event properties to filter and identify the exact event associated with the workflow. For instance, in a booking reminder scenario, If a user has multiple bookings, you can match the booking ID of the cancellation event with the booking ID of the original event to ensure the correct reminder is cancelled. ### Adding filter on event properties Property condition is constructed as key, operator and value. You can add multiple conditions separated by `AND`, `OR`. 1. **Key:** It is the variable key in your wait until event properties. 2. **Operator:** you can use any of the below operators to compare key and value: | Operator | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `==` | Key **is equal to** value. This is a case-sensitive check. | | `!=` | Key **is not equal to** value. This is a case-sensitive check. | | `>` | Key **is greater than** value. Can be applied to integers, float values, or epoch timestamps. | | `>=` | Key **is greater than or equal to** value. Can be applied to integers, float values, or epoch timestamps. | | `<` | Key **is less than** value. Can be applied to integers and float values. | | `<=` | Key **is less than or equal to** value. Can be applied to integers and float values. | | `contains` | Key should be a substring or a list item in an array. | | `not contains` | Key should not be a substring or match any list item in an array. | | `is empty` | Evaluates to `true` if the key is missing, an empty string, or has a `null` value. | | `is not empty` | Key should be present and should not be an empty string or `null` value. | | `intersects` | Evaluates to true if **any value** in the left array matches **any value** in the right array. Useful for checking overlaps between arrays. | | `not intersects` | Evaluates to true if **no values** in the left array match any values in the right array. It could be used in case of checking or filtering out any overlaps between arrays. | 3. **Value:** Value can be fixed (static value) or dynamic (evaluated dynamically from workflow data). #### Fixed values Fixed values can be added as: 1. **string**- enclose within double inverted commas as `"string"` 2. **number**- `1`, `1.2` 3. **boolean**- `true`, `false` #### Dynamic values Dynamic values are evaluated based on the data available at the node input along with actor, recipient or tenant properties. Refer below table for types of dynamic values and their respective syntax. | Data Type | Description | Referring this property in condition | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | **Input Payload** | Includes data from your trigger payload and any data modified or added by nodes such as data transform, batch/digest, or webhook/fetch nodes before the branch node. | Directly specify as `key` with no prefix | | **Actor** | Actor properties. In the case of an event trigger, `distinct_id` works as both actor and recipient. For an inline workflow trigger, it is the `distinct_id` in the actor object. | Add as `$actor.` | | **Recipient** | Recipient properties. It is the `distinct_id` in your event trigger or the key value defined in [override recipient](https://docs.suprsend.com/docs/override-recipient-list) field. For an inline workflow trigger, it is the `distinct_id` in the recipient object. | Add as `$recipient.` | | **Tenant** | Tenant properties. These include all properties of the `tenant_id` in your workflow trigger. | Add as `$brand.` | ### Adding conditions on multiple events You can add condition on multiple events separated by `OR` operator. Evaluation will pass if any of the conditions separated by `OR` is true. ## Max time This branch will be executed if none of the condition branches are satisfied. This is where you'll add your send nodes in case of reminder workflows. You can either add a [Fixed delay](/docs/wait-until#fixed-delay) or [Dynamic delay](/docs/wait-until#dynamic-delay) in max time. Dynamic delays are computed using data in your event properties and can vary for each user. A good example of dynamic delay could be reminders where frequency is set by the user. ### Fixed delay Fixed delay is defined in your workflow form as `**d **h **m **s` and it adds a fixed delay for all users. Some examples of fixed delay are: * Sending multiple payment or activity reminders at predetermined intervals. For instance, sending three payment reminders spaced 2 days apart from the last due date. * Implementing conditional sends across multiple channels. e.g., sending an approval notification via Inbox and scheduling an email to be sent one hour later if the approval is not received. [Smart channel routing](/docs/smart-delivery) is a better approach to solve this use case. ### Dynamic delay In case of dynamic delay, delay duration is computed using the data from your event properties. Dynamic delays are helpful for reminders where the schedule is dictated by the user or when notification needs to be sent before the event or task due date. Imagine you need to send task completion reminders, where users can specify a reminder frequency, such as every 6 hours until the task is finished. This frequency can differ for each task and user. Dynamic wait times effortlessly adapt to these unique preferences. You can add duration key as a [JQ-expression](https://jqlang.github.io/jq/manual/). Below are some examples of how to add duration key in JQ format: 1. General format for duration key at parent level is `.duration_key` 2. If the duration key is a nested event property key like shown below, enter it in the format `.reminder.frequency` ```json theme={"system"} { "reminder": { "frequency": "6h" } } ``` Your duration key variable can be computed to either: * An ISO-8601 timestamp (e.g. 2024-03-02T20:34:07Z) which must be a datetime in the future, or * A relative duration unit, which can be * an integer like `50`, considered as duration in seconds. * an interval string defined as `**d **h **m **s`, where d = day, h = hour, m = minutes and s = seconds When the duration key specified is missing, or resolves to an invalid value, workflow execution will stop and corresponding error will be logged. # Customization options Source: https://docs.suprsend.com/docs/web-components-customisations How to customize the styling, CSS, and layout of the Inbox Feed to match your product’s design in non-React websites. ## Customising tenant By default `tenantId` prop is set to `default` tenant. If you use multi-tenant architecture and want to fetch inbox notifications of other tenant, you can pass `tenant_id` prop. ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { tenantId: "suprsend" } }; ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { tenantId: "suprsend" } }; ``` If you are passing `tenant_id` in feed, make sure to pass scope key while creating [userToken](https://docs.suprsend.com/docs/client-authentication#2-creating-signed-user-jwt-token) else 403 error will be thrown due to scope mismatching. ## Adding tabs You can pass `stores` prop to add multiple tabs in feed. For each tab you can write your own logic to get notifications based on tags, notification categories and notification status like read, archived. Read more about it [here](https://docs.suprsend.com/docs/multi-tabs). ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { stores: [ { storeId: "All", label: "all" }, { storeId: "Archived", label: "archived", query: { archived: true } }, ], }, }; ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { stores: [ { storeId: "All", label: "all" }, { storeId: "Archived", label: "archived", query: { archived: true } }, ], }, }; ``` ## Disable pagination By default, feed supports infinite scrolling type pagination to get older notifications when you scroll through notifications list. If you want to remove infinite scroll, that is stop getting previous page notifications, you can set `pagination=false`. ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { pagination: false } }; ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { pagination: false } }; ``` ## Enable dark mode ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { themeType: "dark/light" } }; ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { themeType: "dark/light" } }; ``` | Light Theme | Dark Theme | | ---------------------------------------------- | ---------------------------------------------- | | ![](https://files.readme.io/bb3ea43-image.png) | ![](https://files.readme.io/e4b8764-image.png) | ## Custom notification card click handler Clicking on notification card will open the link if `Action URL` field is present in triggered inbox template. If you want to override this with your custom callback function you can pass `notificationClickHandler`. ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { notificationClickHandler: { (notificationData: IRemoteNotification) => { console.log("notification clicked", notificationData.n_id); } } } } ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { notificationClickHandler: { (notificationData: IRemoteNotification) => { console.log("notification clicked", notificationData.n_id); } } } } ``` ## Custom action button click handlers Clicking on action buttons in notification card will open link if you provide the URL on action button fields in triggered inbox template. If you want to override this with your custom action button callback functions you can pass `primaryActionClickHandler` and `secondaryActionClickHandler`. ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { primaryActionClickHandler = { (notificationData: IRemoteNotification) => { console.log('primary action button clicked', notificationData.n_id) } } secondaryActionClickHandler = { (notificationData: IRemoteNotification) => { console.log('secondary action button clicked', notificationData.n_id) } } } } ``` ```javascript Feed theme={"system"} suprsendConfig = { feed: { primaryActionClickHandler = { (notificationData: IRemoteNotification) => { console.log('primary action button clicked', notificationData.n_id) } } secondaryActionClickHandler = { (notificationData: IRemoteNotification) => { console.log('secondary action button clicked', notificationData.n_id) } } } } ``` ## Popover position Popover component is wrapper around NotificationFeed component and will be opened on click of bell icon. By default popover is shown at bottom, that is the inbox notifications popup list will be shown at bottom of the bell icon. Example: Feed needs to be shown at bottom of left sidebar above profile icon, in that case on click of bell icon you would want to show notifications popover at right side. ```javascript Inbox theme={"system"} suprsendConfig = { inbox: { popperPosition: "top / bottom / left / right" } } ``` ## Customising CSS styles You can customize the CSS of the provided components by passing `theme` prop. You can pass the styles for the following components: ```typescript Inbox theme={"system"} interface ITheme { bell ? : { height ? : number | string; width ? : number | string; color ? : string; }; badge ? : React.CSSProperties; header ? : { container ? : React.CSSProperties; headerText ? : React.CSSProperties; markAllReadText ? : React.CSSProperties; } tabs ? : { color ? : string; unselectedColor ? : string; bottomColor ? : string; badgeColor ? : string; badgeText ? : string; }; notificationsContainer ? : { container ? : React.CSSProperties; noNotificationsText ? : React.CSSProperties; noNotificationsSubtext ? : React.CSSProperties; loader ? : { color ? : string }; }; notification ? : { container ? : React.CSSProperties & { borderBottom ? : string | number; readBackgroundColor ? : string; unreadBackgroundColor ? : string; hoverBackgroundColor ? : string; }; pinnedIcon ? : { height ? : number | string; width ? : number | string; color ? : string; }; pinnedText ? : React.CSSProperties; headerText ? : React.CSSProperties; bodyText ? : React.CSSProperties & { blockquoteColor ? : string; tableBorderColor ? : string; linkColor ? : string; }; unseenDot ? : React.CSSProperties; avatar ? : React.CSSProperties; createdOnText ? : React.CSSProperties; subtext ? : React.CSSProperties; expiresText ? : React.CSSProperties & { expiringBackgroundColor ? : string; expiringColor ? : string; }; actions ? : Array < { text ? : React.CSSProperties; container ? : React.CSSProperties & { hoverBackgroundColor ? : string; }; } > ; actionsMenuIcon ? : { hoverBackgroundColor ? : string; height ? : number | string; width ? : number | string; color ? : string; }; actionsMenu ? : React.CSSProperties; actionsMenuItem ? : React.CSSProperties & { hoverBackgroundColor ? : string; }; actionsMenuItemIcon ? : { height ? : number | string; width ? : number | string; color ? : string; }; actionsMenuItemText ? : React.CSSProperties; }; } ``` ```typescript Feed theme={"system"} interface INotificationFeedTheme { header ? : { container ? : React.CSSProperties; headerText ? : React.CSSProperties; markAllReadText ? : React.CSSProperties; } tabs ? : { color ? : string; unselectedColor ? : string; bottomColor ? : string; badgeColor ? : string; badgeText ? : string; }; notificationsContainer ? : { container ? : React.CSSProperties; noNotificationsText ? : React.CSSProperties; noNotificationsSubtext ? : React.CSSProperties; loader ? : { color ? : string }; }; notification ? : { container ? : React.CSSProperties & { borderBottom ? : string | number; readBackgroundColor ? : string; unreadBackgroundColor ? : string; hoverBackgroundColor ? : string; }; pinnedIcon ? : { height ? : number | string; width ? : number | string; color ? : string; }; pinnedText ? : React.CSSProperties; headerText ? : React.CSSProperties; bodyText ? : React.CSSProperties & { blockquoteColor ? : string; tableBorderColor ? : string; linkColor ? : string; }; unseenDot ? : React.CSSProperties; avatar ? : React.CSSProperties; createdOnText ? : React.CSSProperties; subtext ? : React.CSSProperties; expiresText ? : React.CSSProperties & { expiringBackgroundColor ? : string; expiringColor ? : string; }; actions ? : Array < { text ? : React.CSSProperties; container ? : React.CSSProperties & { hoverBackgroundColor ? : string; }; } > ; actionsMenuIcon ? : { hoverBackgroundColor ? : string; height ? : number | string; width ? : number | string; color ? : string; }; actionsMenu ? : React.CSSProperties; actionsMenuItem ? : React.CSSProperties & { hoverBackgroundColor ? : string; }; actionsMenuItemIcon ? : { height ? : number | string; width ? : number | string; color ? : string; }; actionsMenuItemText ? : React.CSSProperties; }; } ``` ```typescript Toast theme={"system"} interface ToastNotificationCardTheme { avatar ? : React.CSSProperties; headerText ? : React.CSSProperties; bodyText ? : React.CSSProperties & { color ? : string; blockquoteColor ? : string; tableBorderColor ? : string; linkColor ? : string; }; container ? : React.CSSProperties; } ``` ## Internationalization `locale` can be used to change language of feed. We support translations for below languages internally. If you want to use other languages that are not supported by us or to override strings of existing languages, you can pass `translations` object. * `en` - [English](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/en.ts#L5) (default) * `fr` - [French](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/fr.ts#L5) * `de` - [German](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/de.ts#L5) * `es` - [Spanish](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/es.ts#L5) * `ar` - [Arabic](https://github.com/suprsend/suprsend-react-core/blob/main/src/i18n/languages/ar.ts#L5) ```javascript Inbox theme={"system"} suprsendConfig = { locale: "fr" }; suprsendConfig = { translations: { "notifications": "All Notifications", "markAllAsRead": "Mark Read All" } }; ``` ```javascript Feed theme={"system"} suprsendConfig = { locale: "fr" }; suprsendConfig = { translations: { "notifications": "All Notifications", "markAllAsRead": "Mark Read All" } }; ``` ```typescript Translations Object theme={"system"} interface ITranslations { notifications?: string; markAllAsRead?: string; noNotificationsTitle?: string; noNotificationsDescription?: string; pinned?: string; markAsUnread?: string; markAsRead?: string; archive?: string; expiresIn?: string; minute?: string; minutes?: string; hour?: string; hours?: string; day?: string; days?: string; week?: string; weeks?: string; month?: string; months?: string; year?: string; years?: string; } ``` # Integration Source: https://docs.suprsend.com/docs/web-components-integration How to integrate SuprSend inbox/feed components in Angular, Vue, VanillaJS, and other non-React frameworks. **End of Support for [@suprsend/web-inbox](https://github.com/suprsend/suprsend-web-inbox). Migrate to `@suprsend/web-components`** > We have upgraded authentication of inbox from HMAC to JWT as it is more secure. Please migrate to newer SDK if you are on old one. There are 2 ways in which you can implement inbox functionality: * **Drop-in components:** Pre-built UI with many customizable options which require minimal effort to build. * **Headless implementation:** For more advanced use cases where you want to build UI/UX from scratch. This guide help you integrate drop-in components in your non-react frameworks (angular, vuejs, vanillajs etc). If you want to build your own UI (headless) instead of using drop-in components please refer [docs](https://docs.suprsend.com/docs/js-inapp-feed). ## Integration ### Integrate using script tag This integration is used in Vanillajs, Django, Laravel, ruby etc where npm is not used. ```html theme={"system"}
    ``` ### Integrate as npm package This integration is used in framework based applications like angular, vuejs etc. ```bash theme={"system"} npm install @suprsend/web-components@latest ``` ```javascript theme={"system"} import { initSuprSend, clearSuprSend } from "@suprsend/web-components"; // for dropin inbox with bell
    // for feed without bell as a fullscreen notification etc
    const suprsendConfig = { distinctId: "YOUR_DISTINCT_ID", publicApiKey: "YOUR_PUBLIC_API_KEY", userAuthenticationHandler: ({ response }) => { console.log("User Authentication Response", response); }, }; initSuprSend(suprsendConfig) // for creating instance and rendering component console.log("Instance created but user authentication pending", window.suprsend) ``` **NOTE:** If you are using `suprsend-feed`, specify height for the container for infinite scroll to work properly. ```javascript theme={"system"} const suprsendConfig = { distinctId: "YOUR_DISTINCT_ID", publicApiKey: "YOUR_PUBLIC_API_KEY", feed: { theme: { notificationsContainer: { container: { height: "100vh" } } }, // add this to specify height }, }; ``` ## Removing instance Components will be removed automatically if you navigate away from the page (on unmounting). If you want to remove them manually, you can use below methods. ```javascript Using script tag theme={"system"} window.suprsend.clearSuprSend(); // clears instance and remove all components window.suprsend.clearSuprSendInbox(); // unmount only inbox component window.suprsend.clearSuprSendFeed(); // unmount only feed component ``` ```javascript Using npm package theme={"system"} import { clearSuprSend, clearSuprSendInbox, clearSuprSendFeed, } from "@suprsend/web-components"; clearSuprSend(); // clears instance and remove all components clearSuprSendInbox(); // unmount only inbox component clearSuprSendFeed(); // unmount only feed component ``` ## Updating configuration dynamically ```javascript theme={"system"} window.suprsend.updateSuprSendConfig(config: IUpdateSuprSendConfigOptions); // refresh userToken, change locale, translations dymanically window.suprsend.updateInboxConfig(config: IInbox); window.suprsend.updateFeedConfig(config: IFeed); window.suprsend.updateToastConfig(config: IToastNotificationProps); ``` ## Accessing other instance methods SDK internally calls `new SuprSend()` when you call `initSuprSend()` then you can access instance using `window.suprsend.client`. This instance has methods like [preferences](https://docs.suprsend.com/docs/js-preferences), [webpush](https://docs.suprsend.com/docs/js-webpush), [event and user updates](https://docs.suprsend.com/docs/js-events-and-user-methods). ```javascript theme={"system"} // example methods window.suprsend.client.isIdentified(); window.suprsend.client.user.addEmail(email: string); window.suprsend.client.track(event: string, properties?: Dictionary) window.suprsend.client.webpush.registerPush(); window.suprsend.client.user.preferences.getPreferences(args?: {tenantId?: string}); ``` ## Config options To customise SuprSend components you can pass config object. ```typescript Config Options theme={"system"} interface ConfigProps { publicApiKey: string; distinctId ? : unknown; userToken ? : string; host ? : string; initOnLoad ? : boolean; // pass false if you don't want to initialise instance just after loading script refreshUserToken ? : (oldUserToken: string, tokenPayload: Dictionary) => Promise < string > ; vapidKey ? : string; swFileName ? : string; userAuthenticationHandler ? : ({ response: ApiResponse }) => void; inbox ? : IInbox; // inbox config options feed ? : IFeed; // feed config options toast ? : IToastNotificationProps; // toast config options shadowRoot?: ShadowRoot; //shadowRoot reference } ``` ```typescript Inbox Config Options theme={"system"} interface IInbox { tenantId?: string; stores?: IStore[] | null; host?: { socketHost?: string; apiHost?: string; }; pageSize?: number; pagination?: boolean; theme?: ITheme; themeType?: ThemeType; popperPosition?: Placement; hideAvatar?: boolean; showUnreadCountOnTabs?: boolean; // hiding unread count in multi tab setup hideToast?: boolean; // by default toast is shown on new notification. To stop it pass false headerIconUrl?: string; // icon url to be shown on right side of mark all as read button on header headerIconClickHandler?: () => void; // on the click of above mentioned icon this callback is called notificationClickHandler?: (notification: IRemoteNotification) => void; primaryActionClickHandler?: (notification: IRemoteNotification) => void; secondaryActionClickHandler?: (notification: IRemoteNotification) => void; } ``` ```typescript Feed Config Options theme={"system"} interface IFeed { tenantId?: string; pageSize?: number; stores?: IStore[] | null; host?: { socketHost?: string; apiHost?: string; }; pagination?: boolean; showUnreadCountOnTabs?: boolean; // hiding unread count in multi tab setup hideAvatar?: boolean; themeType?: ThemeType; theme?: INotificationFeedTheme; hideToast?: boolean; // by default toast is shown on new notification. To stop it pass false hideFeed?: boolean; // useful if you dont want to show feed but only show toast notif on new notification headerIconUrl?: string; // icon url to be shown on right side of mark all as read button on header headerIconClickHandler?: () => void; // on the click of above mentioned icon, this callback is called notificationClickHandler?: (notification: IRemoteNotification) => void; primaryActionClickHandler?: (notification: IRemoteNotification) => void; secondaryActionClickHandler?: (notification: IRemoteNotification) => void; } ``` ```typescript Toast Config Options theme={"system"} interface IToastNotificationProps { position?: ToastPosition; // "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" duration?: number; // milliseconds toast should be shown default to 3sec hideAvatar?: boolean; // hide avatar on toast notification themeType?: ThemeType; // dark or light mode theme?: ToastNotificationCardTheme; // to customise css of toast notification } ``` | Parameter | Description | | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | publicApiKey | Public API Key is mandatory field without which error will be thrown by SuprSendProvider. You can get this from [SuprSend Dashboard](https://app.suprsend.com/en/staging/developers/api-keys). | | distinctId | Unique identifier to identify a user across platform. If a value is passed SDK will create user and authenticate user. If null value is passed authenticated user's instance data will be cleared in your application, kind of logout. | | [userToken](https://docs.suprsend.com/docs/client-authentication) | Mandatory when enhanced security mode is on. This is ES256 JWT token generated in your server-side. Refer [docs](https://docs.suprsend.com/docs/client-authentication) to create userToken. | | refreshUserToken | This function is called by SDK internally to get new userToken before existing token is expired. The returned JWT token string is used as the new userToken. | | userAuthenticationHandler | This callback will be called after authenticating user internally when you pass distinctId field to give you back the response of user creation API call. | | host | Customise the host url. | | vapidKey | This key is needed only if you are implementing WebPush notifications. You can get it in SuprSend Dashboard --> Vendors --> WebPush | | swFileName | This key is needed only if you are implementing WebPush notifications and want to customise default `serviceworker.js` file name with your own service worker file name. | | shadowRoot | Shadow root reference to render components inside shadow dom | For further component specific customisations please refer to the [docs](https://docs.suprsend.com/docs/web-components-integration#config-options). # Web Push Source: https://docs.suprsend.com/docs/web-push-quick-start Quick start guide to set up & send Web Push notifications using SuprSend SDK in your website. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Integrate webpush in your website Integrate [SuprSend Web SDK](/docs/integrate-java-sdk) and [webpush vendor](/docs/js-webpush) in your website. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period of 30 days. 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
    ### Identify user to attach push token to their profile Call this method as soon as you know the identity of user, that is after login authentication. Pass the id that you use as internal user identifier (UUID, email or numeric code). You'll use this same id in [recipient field](/docs/web-push-quick-start#5-trigger-the-workflow) to trigger the notification. ```javascript Javascript theme={"system"} suprsend.identify(_distinct_id_); //Sample suprsend.identify("291XXXXX-62XX-4dXX-b2XX"); suprsend.identify("johndoe@gmail.com"); ``` #### Call reset to clear user data on log out Don't forget to call reset on user logout. If not called, user id will not reset and multiple tokens and channels will get added to the user\_id who logged in first on the device. ```javascript Javascript theme={"system"} suprsend.reset(); ``` ### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger): You can trigger these events from your frontend application or from your backend systems, depending on the use case. (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * Template in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our Whatsapp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. Learn how to design [Webpush](/docs/web-push-template) template here. ![](https://files.readme.io/e5b9db7-template_edit.gif) 3\. **Functional nodes (Optional)**: These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow.[Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '`Text`' button in your workflow editor or **"Commit"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**: End user who would be notified in the workflow run. Recipient is uniquely identified by`distinct_id`within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or[create user profile](/docs/users#creating-user-profile-on-suprsend)first for event based trigger. 2. **Data or Event Properties**: This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow)
    **Sample payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys ](https://app.suprsend.com/en/sandbox/developers/api-keys). Push channel will be updated in user profile as soon as you identify the user in [step 4](/docs/web-push-quick-start#4-identify-user-to-attach-push-token-to-their-profile), so you can just pass the `distinct_id` and data in the trigger. ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ``` ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java Java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Web Push Template Source: https://docs.suprsend.com/docs/web-push-template How to design Webpush template with customisation options to add action buttons and image. ## Design Template You can design template with a simple form editor tool. You can add variables with `Handlebarsjs` language. You can check how the message will look in the preview section on the right side. Once designed, you can save the web push notification template by clicking on Save Draft. When you are ready, you can Publish Draft by providing a name to the version. This will become the Live version, and will be used whenever the associated workflow is triggered. ## Web Push notification fields description | Field | Description | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Title | Small message text box. Note that this field will be displayed in single line only, and very long content can get curtailed. Use `handlebarsjs` to add variables. | | Large Icon | The small icon will show up to the right of the notification text. SuprSend puts your organisation logo as default in the large icon, which you can set from 'Organisations Tab'. This will be shown by default and cannot be changed. | | Message | Large message text box. Use `handlebarsjs` to add variables. | | Image | *Optional* Recommended banner filetypes are `PNG`, `JPG`, `JPEG`. SuprSend will auto-scale your image so that it doesn't get cropped. | | Action URL | Provide a URL where a user will go when he clicks on the push notification. Use `handlebarsjs` to add variables. | | Action Buttons | *Optional* Enter up to 3 Button names and URL. You can use variable names using handlebarsjs in both action name and URL. You can give your android deeplink URL as well. The action button name color is picked up from your organisation settings. You cannot change button color in a template once it is created. | ## Adding dynamic content in Web Push There will always be the case where you would be required to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending push. To send actual values to replace variables at the time of communication trigger, use one of our frontend or backend SDKs. Here is a step by step guide on how to add dynamic content in web push: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing the web push template. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable_name}}` If you have declared the variables in the global 'Variables' button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of error like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the `Variables` button. To manually enter the variable name, follow the [handlerbarsjs guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below are some examples of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the `Templates` section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "3,950.00" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "2,549.00" }, { "product_name": "RayBan Sunglasses", "product_price": "7,899.00" } ], "event": { "location": { "city": "Bangalore", "state": "KA" }, "order_id": "11200123", "first_name": "Nikita" }, "product_page": "https://www.suprsend.com" } ``` 1. To enter a nested variable, enter in the format`{{var1.var2.var3}}`. Eg. to refer to city in the example above, you need to enter`{{event.location.city}}` 2. To refer to an array element, enter in format`{{var1.[index].var2}}`. Eg. to refer to`product_name`of the first element of the array`array`, enter`{{array.[0].product_name}}` 3. If you have any space in the variable name, enclose it in square bracket`{{event.[first name]}}` You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: 1. Make sure you have entered the variable name and the sample value in the `Variables` button. 2. Make sure you have entered the correct variable name in the template, as per the`handlebarsjs`guideline. **What happens if there is variable mismatch at the time of sending?** At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. Eg. if there is an error in rendering Web Push template, but email template is successfully rendered, Web Push notification will not be triggered, but email notification will be triggered by SuprSend. *** # Webhook Source: https://docs.suprsend.com/docs/webhook Use webhook node to notify an external API endpoint such as a CRM or chat platform. Webhook is an HTTP API request call to notify an endpoint such as your CRMs, send chat notifications via platforms like Facebook Messenger, or call custom vendors. You can club it with branches to route notifications via different services based on user properties like region, city etc. Any data returned in the API request response is appended to the response\_key in your webhook function and then merged with the input payload. ## Configuring Webhook node In a Webhook node, you have to define the endpoint, query params and headers. You can add both static and dynamic values in all request fields except response key. All Static values are added within `"static value"` and dynamic data is referred as `data.key`. | Field | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Method\* | Supported methods- `GET`, `POST`, `PUT`, `PATCH`, `DELETE` | | Endpoint\* | A valid URL endpoint. Add static URL as `"https://static_url"` and dynamic URL as `{{data.url_endpoint}}`. Data for dynamic URL will be picked from trigger payload. You can also combine static and dynamic part as `"https://domain" + {{data.path}}`. | | Params | Query Params to pass in the request. Similar to endpoint, you can pass both static and dynamic value in query params. | | Header | Any header to be passed in the request can be added as key-value pair with key being the header type and value as header value. You can use it to pass signing key. | | Response key | Response of your webhook request is appended against response key and merged in the workflow payload. Data is merged at parent level if response key is not set. | ### Adding variables in Webhook node Fetch node supports JSONNET rendering language. You can add workflow trigger payload variables as `data.` and internal suprsend data as `data["$brand"].`. Here's a list of variables supported in workflow engine: | Variable | Description | JSONNET format | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | Input Payload | data passed in your event or workflow trigger. | `data.key` | | Actor | properties of actor in your workflow trigger. In case of event, distinct\_id works both as actor and recipient. | `data["$actor"].key` | | Recipient | properties of recipient in your workflow trigger. In case of event, distinct\_id works both as actor and recipient, unless override recipient is set in workflow trigger settings. In case of override recipient, properties of overridden recipient is picked. | `data["$recipient"].key` | | Tenant | properties of the tenant passed in workflow trigger. | `data["$brand"].key` | ### Request execution When executing a webhook function, SuprSend expects the following from your service: * The response to the request is one of: 200 OK, 201 Created, or 204 No Content. * If the request response contains data, it's encoded as JSON and can be decoded into a map/dictionary/hash. * The response to the request takes no longer than 5 seconds for SuprSend to receive. ### Merging response to workflow input If you have added a response key in your setting. The response is first appended to the response key and then merged to the input workflow data. The response is merged at parent level if response key is empty. *The merged data result from a webhook function step then becomes the global trigger data for all subsequent steps in the workflow run.* Here's how webhook function data output would look like with response key and without response key ```json json theme={"system"} // node input data { "name":"old_name", "location":{ "city":"old_city" } } //Fetch function response { "org":"new_org", "location":{ "state":"new_state" } } //Merged data with response key = webhook_response { "name":"old_name", "location":{ "city":"old_city" }, "webhook_response":{ "org":"new_org", "location":{ "state":"new_state" } } } //Merged data with empty response key { "name":"old_name", "org":"new_org", "location":{ "state":"new_state" } } ``` *** # What is SuprSend? Source: https://docs.suprsend.com/docs/what-is-suprsend Learn about SuprSend and how you can use it to power multi-channel product notifications. SuprSend has all the features set which enable you to send notifications in a reliable and scalable manner, as well as take care of end-user experience, thereby eliminating the need to build any notification service in-house. ## Benefits of using SuprSend as your notification stack: * You do not have to do any vendor integrations for channels in your code. You can easily add/remove/prioritise vendors and channels from your SuprSend account, * You can design powerful templates for all channels together and manage them from a single place, * You can leverage powerful features to experiment fast with notifications as well as take care of end user experience without writing a single line of code. ## Introduction to Workflows Communications are made up of multiple components - trigger, logic, content, variables, target user, channels, vendors, etc. Typical communication solutions have one or more components intertwined with each other. SuprSend solves communications from a different and more powerful approach, which we call **Workflows.** At SuprSend, all the constituent components are decoupled from each other, making it modular in nature. The components can come from any source. All these components are configured as nodes in Workflows, where the processing happens for delivery and optimisation. This allows Workflows to handle any complexity possible in your communication use cases. ## How do you trigger notifications? You can trigger notifications in one of the two ways: 1. Send events to SuprSend from your frontend clients (android app, website, etc) via SuprSend Client SDK, and create a Workflow on SuprSend platform to trigger notification on an event. 2. Create workflow and trigger notification from your backend itself using an omni-channel HTTPS API method, or you can use our Backend SDK. All the other components (like vendors, templates, optimisation, scaling, etc.) are created and managed on SuprSend platform. You can check the 'Core Concepts' section that lists down the components used in the platform, so you can navigate the platform and use all the features with ease. ## SuprSend APIs You can try out SuprSend APIs from our [Postman collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) *** # Whatsapp Cloud API Source: https://docs.suprsend.com/docs/whatsapp-cloud-api Integration guide to setup whatsapp notifications on meta using Whatsapp Cloud API. ## Pre-Requisites **Create Meta Developers account** * Log in to your Facebook account. * Click [here](https://developers.facebook.com/) and Get Started. After completing the verification and accepting terms, you should be able to access [Facebook developers dashboard]() ## Set up WhatsApp business account on Meta On the [developer’s console](https://developers.facebook.com/apps) create an app by clicking on **`Create app`** button, then select **`Other`** on the next screen of "What do you want your app to do?" Select app type - **"Business"** Add App name and create app with your personal details Now that your app is created you need to add "`WhatsApp`" product, scroll down and click "`Set up`" on WhatsApp. You will be redirected to the WhatsApp get started page, here you can "create a business account" or use an existing one. Click on `Continue`. You'll land on the app dashboard. On the [App dashboard](https://developers.facebook.com/apps), select "`API Setup`" from the left navigation menu. Here you'll see a **`temporary access token`**, **`Phone number ID`** and a curl to send the message. Add your personal phone number in the "to" field to send a test message on your mobile number. The phone\_number\_id is from a Facebook test phone number which cannot be used in production. Scroll down to step 5 on the API page. To add your business phone number, Click on "Add phone number" and add your business information to register the number Add your business phone number. This will be the number from which your business communication will go through. If you are using an existing WhatsApp number, [refer doc](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started/migrate-existing-whatsapp-number-to-a-business-account) to migrate the business number. Follow the steps and do not forget to add a payment method to avoid any errors. Once the phone number is added, copy the `Phone number ID` and `WhatsApp Business Account ID` corresponding to this phone number shown in Step 1 of API Setup Because the testing access token only last 24 hours, we need to create a token that can last forever: * Sign into the [Meta Business Suite](https://business.facebook.com/). * Locate your business account in the top-left dropdown menu and click its Settings (gear) icon. Click Business settings. * Select **Users -> System users** from the left navigation panel * Add SuprSend user and give it "`Employee`" access * Select the user and Click on "Assign Assets." Assign Full control to the new user. This will allow us to send messages on the behalf of this user through your WhatsApp cloud account. * Now, we'll have to assign the system user to our WhatsApp account. * To do this from WhatsApp settings page go to WhatsApp accounts -> select your account. Now on the right panel there will be an option to**Assign People**, from there assign the System User that we created above to your account. After it's assigned you'll be able to see in **People** section as shown in the image below. Go back to the system users page and select the recently created system user from the list. Then click the Generate new token button. Select your app, token expiry (**never**), and then on the bottom screen select permissions `whatsapp_business_management` and `whatsapp_business_messaging`. **Copy the Generated Token** Click on generate token and copy the token value, please note this value will not be accessible later, so do store it someplace safe. You have now successfully setup your WhatsApp business account. Change the App mode to live to start sending notification to your production users. We will need App ID associated with your WhatsApp business account. You can either get it from the developers page as shown in screenshot above, to the left of App Mode. Alternatively, go to [https://developers.facebook.com/apps](https://developers.facebook.com/apps), here you can see all the apps registered with your meta account. Copy the App ID from the App integrated with SuprSend. ## Configure webhook in Meta account for automated template approval and DLR tracking One of the platform advantage of using SuprSend as a central communication system is that it shows notification analytics for all channels in your SuprSend account together. To configure webhook URL, copy the URL from the vendor page and add it on your app dashboard (WhatsApp -> Configuration). Set verify token as `572bcee9-4558-45ee-bbbb-fac5cb08766d` on webhook fields-> manage button, subscribe to all events mentioned. ## Add vendor configuration on SuprSend On the SuprSend dashboard, go to vendor page from side panel and click WhatsApp -> WhatsApp Cloud API from the list of Vendors. This will open vendor details page as shown below: | Form Field | Obligation | Description | | -------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Nickname** | *mandatory* | You can give any name which may help you to identify this account easily | | **Phone number ID** | *mandatory* | Phone number ID linked to your WhatsApp business number. You can get this from [App dashboard](https://developers.facebook.com/apps) (WhatsApp -> API Setup). [Refer above step](/docs/whatsapp-cloud-api#step-4-add-a-valid-business-phone-number) for more details. | | **Access token** | *mandatory* | Access token for authenticating the WhatsApp API request. You can generate a new access token by following the [step above](/docs/whatsapp-cloud-api#step-5-generate-access-token). | | **WhatsApp Business Account ID** | *mandatory* | WABA ID linked to your WhatsApp business number. This will be needs to automate the template approval process for your account. You can get this from [App dashboard](https://developers.facebook.com/apps) (WhatsApp -> API Setup). [Refer above step](/docs/whatsapp-cloud-api#step-4-add-a-valid-business-phone-number) for more details. | | **App ID** | *mandatory* | App ID linked to your WhatsApp business account. This is used to auto approve your WhatsApp templates. You can get this from [App dashboard](https://developers.facebook.com/apps). Refer step 7 for more details. | | **Price per notification** | *optional* | This is the amount you pay per WhatsApp notification to Meta. It helps us to calculate, estimate and optimise your cost spent on notifications. | ## Migrate existing phone number to Whatsapp Cloud API If your WhatsApp business number is already registered with another BSP and you want to migrate it to WhatsApp cloud API, [follow this documentation](https://developers.facebook.com/docs/whatsapp/business-management-api/guides/migrate-phone-to-different-waba/) for migration. *** # Whatsapp Source: https://docs.suprsend.com/docs/whatsapp-quick-start Set up guide to send Whatsapp notifications via SuprSend. ### Create SuprSend account Simply [signup](https://auth.suprsend.com/sign-up) on SuprSend to create your account. If you already have your company account setup, ask your admin to invite you to the team. ### Start testing in Sandbox workspace Your SuprSend account includes three default workspaces: Sandbox, Staging, and Production. You can switch between them from the top navigation bar, and create additional workspaces if needed. 1. **Sandbox** * **Demo Workspace** with pre-configured vendors for quick exploration and POC. * Includes a sample workflow, a sample user with your registered email and pre-configured channels for quick testing. * Limitation: Available for a trial period and Whatsapp notifications can be sent only to verified phone numbers (to prevent spam). 2. **Staging** * **Development workspace** used to test notification flows before pushing it to production. * You can enable [Test Mode](/docs/developer/test-mode) to safely test notification flows without delivering to real users. In Test Mode, notifications is delivered only to designated internal testers. You can also set up a catch-all channel to redirect all notifications intended for non-test users. 3. **Production** * **Live workspace** for syncing your actual product users and running production workflows. * We do not recommend making changes directly in your production workspace as it might disrupt your live notifications.
    ### Create a workflow Workflow houses the automation logic of your notification. Each workflow starts with a trigger, processes the defined logic, and sends one or more messages to the end user. You can create a workflow from SuprSend dashboard by clicking on **`+ Create workflow`** button on the [workflows tab](https://app.suprsend.com/en/sandbox/workflows). To design a workflow, you need: 1. **A Trigger point**- Trigger initiates the workflow. You can initiate it * [Using the direct workflow API](/docs/trigger-workflow#triggering-workflow-via-api), where you can include recipient channel information, preferences, and actor details directly in the trigger. * [By emitting an event](/docs/trigger-workflow#event-based-trigger) (note: the recipient needs to be pre-created for event-based triggers). 2. **Delivery node**- Delivery Nodes represent the channels where users will receive notifications. You can use: * [multi-channel](/docs/delivery-multi-channel) nodes, to send messages across multiple channels, * [smart channel routing](/docs/smart-delivery), to notify users sequentially rather than bombarding them on all channels at once (though it’s generally better to use). * Template in delivery node contains the content of the notification. You can add both static and dynamic content sourced from user properties or trigger payloads. We use handlebars as our WhatsApp templating language. You can add dynamic content as `{{var}}`. Add trigger data in the **mock** to get variable auto-suggestions during editing. Ensure to publish the template before using it in a workflow. [Learn more about how to design WhatsApp template here](/docs/whatsapp-template) ![](https://files.readme.io/e5b9db7-template_edit.gif) 3. **Functional nodes (Optional)** These are the logic nodes in the workflow. You can use it to add delay, batch multiple notifications in a summary or add conditional branches in the workflow. [Check out all workflow nodes here.](/docs/delay) ### Trigger the workflow You can trigger a test workflow directly from dashboard by clicking on '**`Test`**' button in your workflow editor or **"`Commit`"** changes to trigger it from your code. We follow Git like versioning for workflow changes, so you need to commit your changes to trigger new workflow via the API. You can [check all methods of triggering workflow here](/docs/trigger-workflow). To trigger a workflow, you need: 1. **Recipient**: End user who would be notified in the workflow run. Recipient is uniquely identified by`distinct_id`within SuprSend and must have the relevant channel identity set in their profile. You can define recipient inline in case of API based trigger or [create user profile first](/docs/users#creating-user-profile-on-suprsend) for event based trigger. In Sandbox environment, a sample user with your registered email ID is pre-created for testing. You can always add more users or edit existing user profile from subscriber page on UI. 2. **Data or Event Properties**: This will be used to render dynamic content in the template (added in template mock) or variables in the workflow configuration. We'll be triggering the workflow with direct API trigger for quick testing. You can [check all trigger methods here.](/docs/trigger-workflow) **Sample payload for API based trigger** You can get workspace key, secret or API Key for trigger from [Settings tab -> API Keys](https://app.suprsend.com/en/sandbox/developers/api-keys) ```curl curl theme={"system"} curl --request POST \ --url https://hub.suprsend.com/trigger/ \ --header 'Authorization: Bearer __api_key__' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$whatsapp":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } ' ``` ```python python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$whatsapp":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } }, idempotency_key = "_unique_identifier_of_the_request_" ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```javascript node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "_workflow_slug_", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$whatsapp":["+12135555444"], "name":"recipient_1" } ], "data":{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } const w1 = new WorkflowTriggerRequest(body, { idempotency_key: "_unique_identifier_of_the_request_"}) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_secret_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "_workflow_slug_", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$whatsapp": []string{"+12135555444"}, "name":"recipient_1", }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234", "spend_amount": "$10", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, IdempotencyKey: "_unique_identifier_of_the_request_", } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` ```java java theme={"system"} package test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import suprsend.Suprsend; import suprsend.SuprsendException; import suprsend.WorkflowTriggerRequest; public class Workflow { public static void main(String[] args) throws Exception { WorkflowTrigger(); } private static void WorkflowTrigger() throws SuprsendException, UnsupportedEncodingException { Suprsend suprClient = Helper.getClientInstance(); // payload JSONObject body = getWorkflowBody(); String idempotencyKey = "_unique_request_identifier"; WorkflowTriggerRequest wf = new WorkflowTriggerRequest(body, idempotencyKey, tenantId); // JSONObject resp = suprClient.workflows.trigger(wf); System.out.println(resp); } private static JSONObject getWorkflowBody() { JSONObject body = new JSONObject() .put("workflow", "__workflow_slug__") .put("recipients", new JSONArray() .put(new JSONObject() .put("distinct_id", "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09") .put("$whatsapp", Arrays.asList("+12135555444")) .put("name", "recipient_1") )) .put("data", new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000") .put("invoice_id", "Invoice-1234") ); return body; } } ``` ### Check notification logs You can view the status of any sent notification under the Logs tab. Logs are organized in the following order: * **Requests**: Captures all API/SDK requests sent to SuprSend from your backend or frontend. You can see the input payload and request response here. * **Executions**: Workflow executions are logged here. You can click on a log entry to open the step-by-step workflow debugger * **Messages**: All delivery nodes (including webhooks) are tracked here along with their message status (delivered, seen, clicked). Message preview for delivered notifications will also be available soon. ### Test with your WhatsApp vendor You have to bring your own vendor to setup WhatsApp notification in your staging and production workspaces. However, you can test your workflows in Sandbox where sample vendors are pre-added. You can clone workflows from one workspace to another. All you have to do is fill in the vendor form for your respective vendor and you are good to go. ### Push to Production In SuprSend, each environment is isolated, meaning workflows, users, and vendors are configured separately in testing and production workspaces. Follow this [go live checklist](/docs/go-live-checklist) to setup things in production once you are done testing. *** # Whatsapp Template Source: https://docs.suprsend.com/docs/whatsapp-template How to design whatsapp template using form editor. This section is a step-by-step guide on how to design and publish WhatsApp notification template. ## Design template You can design the template with a simple form editor tool. You can add variables with `Handlebarsjs` language. You can see how the message will look in the preview section on the right side. Once designed, save the WhatsApp template by clicking on the `Save Draft` button. When you are ready, you can `Publish Draft` by providing a name to the version. This will create a version in `Pending Approval` state. WhatsApp requires a template approval process, where every template has to be submitted to WhatsApp for approval, where WhatsApp reviews and either Approves or Rejects the message. SuprSend handles the WhatsApp approval process for you. All you have to do is create a template on SuprSend while following [WhatsApp template guidelines](/docs/whatsapp-template-guidelines), and we'll send an email to you as soon as WhatsApp approves / rejects the template. Based on the approval status, the published template version's state will move to `Live` or `Rejected`. Once the version goes `Live`, you can use the template to send messages to your users. ## WhatsApp fields description | Field | Description | | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Template Category** | Category of the template as defined by WhatsApp. Choose the category which is most relevant for your message content. e.g. - if you are sending a message informing the user about his/her doctor appointment, select the category as *Appointment Update*. In case you are not able to find the relevant category for your message, select *Alert Update* | | **Type** | Type of the message template - `MEDIA/TEXT`. You can select one of the options. | | **Header** (Type - TEXT) | Header of the message shown in bold in your WhatsApp message Small message text box. You can add up to 60 characters in this field*Emojis are not supported in header* | | **Header -> Media Type** (Type - MEDIA) | Media type of the header - Document(.pdf) / Image (.jpg, .png) / Video (.mp4). You can select one the media types based on the type of content that you want to add in the message | | **Header -> Media File URL** (Type - MEDIA) | Add the Public URL of the document that you want to send. You can add dynamic URL by adding variables in the URL link, like this -[http://s3.amazonaws.com/\{\{url\_params}}](http://s3.amazonaws.com/%7B%7Burl_params%7D%7D) or `\{\{url\_link}}` | | **Header -> Document Name** (Type - MEDIA) | *Valid only for media type - Document (.pdf)* This is the name of the document that will be visible to your user. Will be shown as "Untitled" if not added. You can add variable in media file as \{\{file\_name}} | | **Body** | Large message text box. Can add multi-line texts. Use handlebarsjs to add variables. | | **Footer** | Small message text box. You can add up to 60 characters in this field*Variables are not supported in footer* | | **Buttons** | Button type to be added - Call to Action / Quick Reply. Select **"None"** if you don't require buttons | | **Action Buttons** | There are 2 types of action buttons that can be added: 1. **Call Phone Number Button**- To initiate a Call Action. 2. **Visit Website Button**- To redirect users to a website. Add the URL where a user will go when they click on this button Only one variable is allowed in "Website URL" at the end of the URL link, like this - [www.suprsend.com/\{\{page}}](http://www.suprsend.com/%7B%7Bpage%7D%7D) | | **Quick Reply Buttons** | You can add up to 3 quick reply buttons to take user input.*Variables or emojis are not allowed in quick reply button* | **Vendor Integration Required** Please note that to send the WhatsApp, you will need to integrate WhatsApp vendor with SuprSend. Please visit the 'Vendor Integration Guideline' section to see vendors list and how to integrate them. ## How to format WhatsApp messages WhatsApp allows you to format text inside your messages. Use below options to format the text. **Other formatting like HTML tags or markdown will not work** for formatting the content | Text Format | Method | Description | | ----------------- | ---------------- | ---------------------------------------------------------------------------- | | **Italic** | \_text\_ | To *italicize* your message, place an underscore on both sides of the text | | **Bold** | \*text\* | To **bold** your message, place an asterisk on both sides of the text | | **Strikethrough** | \~text\~ | To ~~strikethrough~~ your message, place a tilde on both sides of the text | | **Monospace** | \`\`\`text\`\`\` | To `monospace` your message, place three backticks on both sides of the text | ## Adding dynamic content in the template There will always be the case where you would require to add dynamic content to a template, so as to personalise it for your users. To achieve this, you can add variables in the template, which will be replaced with the dynamic content at the time of sending the message. You'll need to pass these while triggering the communication from one of our frontend or backend SDKs. Here is a step-by-step guide on how to add dynamic content in Inbox: If you are at this stage, it is assumed that you have declared the variables along with sample values in the global **Mock data** button. To see how to declare variables before using them in designing templates, refer to [this section in the Templates documentation](/docs/templates#adding-dynamic-content). Once the variables are declared, you can use them while designing the android push template. We support `handlebarsjs` to add variables in the template. As a general rule, all the variables have to be entered within double curly brackets: `{{variable_name}}` If you have declared the variables in the global 'Mock data' button, then they will come as auto-suggestions when you type a curly bracket `{`. This will remove the chances of errors like variable mismatch at the time of template rendering. Note that you will be able to enter a variable name even when you have not declared it inside the 'Variables' button. To manually enter the variable name, follow the [handlerbarsjs guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below is an example of how to enter variables in the template design. For illustration, we are using the same sample variable names that we declared in the 'Templates' section: ```json json theme={"system"} { "array": [ { "product_name": "Aldo Sling Bag", "product_price": "$50" }, { "product_name": "Clarles & Keith Women Slipper, Biege, 38UK", "product_price": "$39" }, { "product_name": "RayBan Sunglasses", "product_price": "$120" } ], "event": { "location": { "city": "San Francisco", "state": "California" }, "order_id": "11200123", "first_name": "Joe" }, "product_page": "https://www.suprsend.com" } ``` 1. To enter a nested variable, enter in the format `{{var1.var2.var3}}`. Eg. to refer to city in the example above, you need to enter `{{event.location.city}}` 2. To refer to an array element, enter in format `{{var1.[_index_].var2}}. Eg. to refer to ` product\_name `of the first element of the array` array `, enter ` `\{{array.[0].product_name}}` 3. If you have any space in the variable name, enclose it in square bracket `{{event.[first name]}}` You will be able to see the sample values in the Preview section, as well as in the Live version when you publish a draft. If you cannot see your variable being rendered with the sample value, check one of the following: 1. Make sure you have entered the variable name and the sample value in the `Variables` button. 2. Make sure you have entered the correct variable name in the template, as per the `handlebarsjs` guideline. At the time of sending communication, if there is a variable present in the template whose value is not rendered due to mismatch or missing, SuprSend will simply discard the template and not send that particular notification to your user. Please note that the rest of the templates will be sent. Eg. if there is an error in rendering Android Push template, but email template is successfully rendered, Android Push notification will not be triggered, but email notification will be triggered by SuprSend. *** # Whatsapp Template Guidelines Source: https://docs.suprsend.com/docs/whatsapp-template-guidelines Guidelines and allowed content for whatsapp template approval. Message templates are specific message formats that businesses use to send out notifications or customer care messages to people that have opted in to notifications. Messages can include appointment reminders, delivery information, issue resolution and payment updates. ## Template guidelines Please consider the following guidelines to accelerate the template approval process: * Make your message template name clear. Instead of using a name like "template\_014" use "bus\_ticket\_details" * Remember that someone outside of your business will be reviewing your templates. Providing more clarity gives reviewers context around how the template will be used. * Think about how your template sounds when read out loud. It should not sound promotional and avoid use of marketing language use of exclamation marks etc. * Review your templates once before submitting. Make sure there are no spelling or grammatical errors and variables are added in the correct format (two curly brackets on either side) and that no variables are repeated. * If you need to write a message template to re-open the 24-hour window, we would suggest starting with some mention of the previous conversation thread. e.g. “I'm sorry that I wasn't able to respond to your concerns yesterday but I’m happy to assist you now. If you’d like to continue this discussion, please reply with ‘yes’.” or “I was able to do some follow-up based on our previous conversation, and I’ve found the answer to your question about our refund policy. If you’d like to continue our conversation, please say ‘yes’.” ## Template rejections You cannot use WhatsApp as a channel to attempt to get users to re-engage with your product and/or resurrect churned users. If one or more of your templates have been rejected, it may have been for one of the following reasons: **Advertising, marketing, or promotional messages are not permitted.** Some examples of this include the following: * Offering **coupon codes** and/or free gifts. * Sales, discounts, promotions, product recommendations, offers including recurring content (e.g: *timely information, a newsletter, any sort of subscription, a catalog*) * **Upselling or Cross-selling** e.g. “*Here is your boarding pass, with seat assignment and gate information. If you would like to save 10% on your in-flight dinner, order your meal through our app.*" * **Re-engagement:** e.g. *“your friend commented on your photo”, “your friend shared a new playlist” “top tweets from people you follow”* * **App downloads:** e.g. “*Download our mobile app to pay bills/recharges*”, etc. * User takes action with a business that results in a notification for another user e.g. Company X: A buys a gift card for B, Company X notifies B , Company X can't send a notification asking a user to share a promotion to a group in order for everyone to receive that promotion * **Reminders or alerts** that a user may have indicated interest in seeing e.g. Price drops, back in stock, points expiration - frequent reminders for a variety of things with the primary purpose of sending promotions / sale alerts / etc. * **Cold call messages** e.g. “*Is now a good time to talk?*”, “*Thank you for your interest, can we speak now?”, “I tried contacting you but you weren't available. When are you free?*”, etc. * Sending a **survey or poll to collect data** e.g. “*Hi, we're interested in knowing how you feel about certain food groups. Do you mind participating in a survey?*” * Inclusion of certain words or phrases that make the message template **promotional** (even though the content of your template may be fine) WhatsApp does not approve message templates with floating parameters (,that is lines with just parameters and no text). In the below example, we're referring to `{{3}}` and `{{4}}` as the floating parameters. ```javascript Javascript theme={"system"} --- TICKET NO: {{1}} PASSENGER NAME: *{{2}}* --- {{3}} - {{4}} ``` ## Incorrect formatting Some examples of this include the following: * Message templates with spelling mistakes will be rejected. * Make sure to use parameters like `{{1}}`, `{{2}}`, etc. and include the correct number of curly brackets (,that is 2 on the left side of the number and 2 on the right side of the number) ## Template containing potentially abusive or threatening content Some examples of this include the following: * Message templates that threaten customers with a legal course of action will be rejected. * Message templates that threaten to add customers to a WhatsApp group with their friends and family to shame them if they don't pay back their loans will be rejected. *** # Workflow Source: https://docs.suprsend.com/docs/workflows Understand what is workflow and how to design, test, trigger and track workflow log. Workflow acts as the notification powerhouse where you define the logic and stitch individual pieces together, like [user](/docs/users), [template](/docs/templates), [vendor](/docs/vendors), and [preferences](/docs/user-preferences), to deliver notifications to the end user. A workflow comprises a series of steps that, when performed in sequence, form the notification journey. There exists primarily four types of workflow steps, referred to as workflow nodes in SuprSend: [Trigger](/docs/trigger-workflow) node initiates the workflow and delivers the first notification. You can trigger a workflow through an event call or a direct API call to SuprSend. Events fall into three categories: **1. External actions performed by users on your platform**, such as `post like` or `new comment`. To track these actions, integrate the [frontend SDK](/docs/integrate-javascript-sdk) into your product or use the [backend SDK](/docs/integrate-python-sdk) or [HTTP API](/reference/overview). **2. User entering or leaving a list**, such as when `users unsubscribe from a topic`. These events trigger automatically when using SuprSend-managed lists and configuring list events through [list tracking](/docs/lists#configure-list-events). **3. Internal trigger from backend logic**, based on data or state changes like `shipment out for delivery` or `job closed`. Functions represent logical steps in a workflow—such as [delay](/docs/delay), [batch](/docs/batch) to combine notifications into one summarized message, or implementing conditional waits. Split the workflow into parallel flows where the user follows one branch based on a condition. e.g., the [wait until](/docs/wait-until) branch holds the user until they meet the required condition. Delivery Nodes represent the final send steps that deliver notifications to users. Templates define the content of each notification. You can send notifications through a [single channel](/docs/delivery-single-channel), [multi-channel](/docs/delivery-multi-channel), or apply [channel routing](/docs/smart-delivery) for sequential delivery across multiple channels. ## Create workflow In SuprSend, you can group multi-channel and multi-stakeholder notifications in a single workflow, by considering the workflow as a sequence of notifications. One example of a multi-channel, multi-stakeholder workflow involves sending a series of payment reminders to a company's users. * The system sends the first reminder to the account admins via email. * The second reminder reaches both the admins and the finance team of the company—first via Inbox, then via email if the notification isn’t seen on Inbox. Creating a single workflow to track the journey linked to a trigger helps simplify tracking the entire flow—e.g., understanding how many users saw the first notification, how many dropped off after the second, which channel performs best, and more. Fewer workflow **Test your workflows in staging workspace before deploying to production:** Design your workflows in the staging workspace first and trigger a test workflow before making it live for your production users. Once your workflow is well tested and working, [clone](/docs/design-workflow#cloning-a-workflow) it to the production workspace. ### Notification categories Each workflow connects to a notification category, which helps group related workflows. Users can manage their preferences across these groups. For instance, you can group all shipment-related messages under the category `Delivery updates`, allowing users to turn off email alerts and receive them only via push notifications. This preference setting automatically applies to all workflows under the category `Delivery updates`. Learn more about [notification categories here](/docs/notification-category). ### Version control for workflows The system first saves all workflow changes in a draft version and only makes them live after you commit the updates. This approach lets you confidently update workflows without affecting any that currently run in production. ## Trigger workflow Workflows created on SuprSend dashboard can [trigger via an event call](/docs/trigger-workflow#event-based-trigger) or via a [direct API call](/docs/trigger-workflow#triggering-workflow-via-api). For transactional use cases such as One-Time Password (OTP), authentication, and verification notifications—where you must provide dynamic user channel information or sensitive data at the time of sending—you can include all data and recipient information in a single API request. This method doesn’t require creating a user beforehand to trigger the notification, making it ideal for migrating existing notifications to SuprSend. Below is an example payload that demonstrates a dynamic workflow setup for an OTP notification. ```python Python theme={"system"} from suprsend import Event from suprsend import WorkflowTriggerRequest supr_client = Suprsend("_workspace_key_", "_workspace_secret_") # Prepare workflow payload w1 = WorkflowTriggerRequest( body={ "workflow": "login_otp", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms": ["+15555555555"] } ], # variable data in your template "data":{ "OTP": "1234" } } ) # Trigger workflow response = supr_client.workflows.trigger(w1) print(response) ``` ```node Node theme={"system"} const {Suprsend, WorkflowTriggerRequest} = require("@suprsend/node-sdk"); const supr_client = new Suprsend("_workspace_key_", "_workspace_secret_"); // Prepare workflow payload const body = { "workflow": "login_otp", "recipients": [ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms": ["+15555555555"] } ], "data":{ "OTP": "1234" } } const w1 = new WorkflowTriggerRequest(body) // Trigger workflow const response = supr_client.workflows.trigger(w1); response.then(res => console.log("response", res)); ``` ```go Go theme={"system"} package main import ( "log" suprsend "github.com/suprsend/suprsend-go" ) // Initialize SDK func main() { suprClient, err := suprsend.NewClient("_workspace_key_", "_workspace_key_") if err != nil { log.Println(err) } _ = suprClient triggerWorkflowAPI(suprClient) } func triggerWorkflowAPI(suprClient *suprsend.Client) { // Create WorkflowRequest body wfReqBody := map[string]interface{}{ "workflow": "login_otp", "recipients": []map[string]interface{}{ { "distinct_id": "0gxxx9f14-xxxx-23c5-1902-xxxcb6912ab09", "$sms": []string{"+15555555555"}, }, }, // # data can be any json / serializable python-dictionary "data": map[string]interface{}{ "OTP": "1234", }, } w1 := &suprsend.WorkflowTriggerRequest{ Body: wfReqBody, } // Call Workflows.Trigger to send request to Suprsend resp, err := suprClient.Workflows.Trigger(w1) if err != nil { log.Fatalln(err) } log.Println(resp) } ``` It's a new workflow method available in the SDK versions below (Python >= v0.11.0, Go >= v0.5.1, and Node >= 1.10.0). Upgrade to the latest version if you're using an older SDK. Below is a sample event call to trigger the payment reminder workflow. You can trigger these events by integrating one of the frontend or backend SDKs, or by integrating with your Customer Data Platform (CDP) like [Segment](/docs/segment) and mapping the events passed through these platforms as your workflow trigger. ```python Python theme={"system"} from suprsend import Event # Track Event Example distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" event_name = "Payment Pending" properties = { "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } event = Event(distinct_id=distinct_id, event_name=event_name, properties=properties) # Track event response = supr_client.track_event(event) print(response) ``` ```javascript Node.js theme={"system"} const { Event } = require("@suprsend/node-sdk"); // Track Event Example const distinct_id = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08" const event_name = "Payment Pending" const properties = { "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } const event = new Event(distinct_id, event_name, properties) // Track event const response = supr_client.track_event(event) response.then((res) => console.log("response", res)); ``` ```java Java theme={"system"} import org.json.JSONObject; import suprsend.Suprsend; import suprsend.Event; public class Event { public static void main(String[] args) throws Exception { trackEvent(); } private static Subscriber trackEvent() throws SuprsendException { Suprsend suprsendClient = new Suprsend("_workspace_key_", "_workspace_secret_"); String distinctId = "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08"; String eventName = "Payment Pending"; JSONObject eventProps = new JSONObject() .put("first_name", "User") .put("invoice_amount", "$5000"); .put("invoice_id", "Invoice-1234"); Event e = new Event(distinctId, eventName, eventProps); // Track event JSONObject response = suprClient.trackEvent(e); System.out.println(response); } ``` ```go Go theme={"system"} ev := &suprsend.Event{ EventName: "Payment Pending", DistinctId: "0fxxx8f74-xxxx-41c5-8752-xxxcb6911fb08", Properties: map[string]interface{}{ "first_name": "User", "invoice_amount": "$5000", "invoice_id":"Invoice-1234" } } // Send event to Suprsend by calling .TrackEvent _, err = suprClient.TrackEvent(ev) if err != nil { log.Fatalln(err) } ``` **User profile should be created in SuprSend for passing in event call:** Please note that you must create the user profile beforehand for the `distinct_id` passed in your event call. If the user isn't present, the event call discards it. You can also configure your one-time notifications using google sheets. Refer step-by-step guide to [configure workflows using google sheets here](/docs/trigger-workflow#triggering-workflow-using-google-sheets). ## Track / Debug Workflow run For each workflow run, you'll see a detailed log capturing the state and response of each workflow step as they're sent. You can refer to [error guides](/docs/error-guides) to understand the errors and see how to resolve them. ## Analyze notification performance SuprSend provides comprehensive analytics to track the performance of your notifications. You can track delivery, seen, and clicks across all channels in a single graph, identify the best-performing channel, and see how users interact with the notification. You can also retrieve the notification data in your data warehouse for internal analysis and reporting using the [S3 connector](/docs/amazon_s3). **Configure SuprSend webhook in vendor dashboard to track delivery data** 👍 Ensure to include the SuprSend webhook callback URL in your vendor dashboards for WhatsApp, Short Message Service (SMS), and email. This enables tracking of delivery, seen, and click statuses for display in logs and analytics. For real-time updates to your system, you can also add your webhook endpoint as an [outbound webhook](/docs/webhook) in SuprSend. SuprSend processes data received from your end-vendors in a standard format, eliminating the need to adapt your system for different vendor data structures. ## Per-Tenant workflow Tenants represent a segment that a user belongs to. These can include organizations, teams within an organization, subsidiary companies, or different product lines within the same business. While SuprSend doesn’t directly associate workflows with tenants, you can dynamically pass `tenant_id` in your trigger to send a notification for a tenant. This picks the properties corresponding to that tenant for custom notification content and considers per-tenant preferences when executing the workflow. [Read more about tenant workflows here](/docs/tenant-workflows). *** # Add Subscription Source: https://docs.suprsend.com/reference/add-object-subscription POST /v1/object/{object_type}/{id}/subscription/ API to add subscribers (users/child objects) to a given object, to notify subscribers when workflow is triggered on the parent object. # Add Users to Draft List Source: https://docs.suprsend.com/reference/add-subscribers-to-draft-list POST /v1/subscriber_list/{list_id}/version/{version_id}/subscriber/add API to add users in the draft list by passing its `version_id` returned in [Start Sync](/reference/start-sync) API response. # Add Users to List Source: https://docs.suprsend.com/reference/add-user-to-list POST /v1/subscriber_list/{list_id}/subscriber/add API to add users to the list by passing their distinct_ids. # Authentication Source: https://docs.suprsend.com/reference/authentication Learn how to authenticate HTTP API requests. SuprSend uses API Keys to authenticate requests. You can get the API Keys from [SuprSend dashboard -> Developers -> API Keys](https://app.suprsend.com/en/staging/developers/api-keys) page. API Keys are unique to your workspace. This is done to keep your testing and production workspace separate and safeguards against accidentally sending wrong notification to your production users during testing. API Keys are confidential to your account and can only be generated in your SuprSend dashboard. To add an extra layer of security, we guarantee that any API Key generated is displayed only once to the user at the time of key generation and is not stored anywhere apart from your codebase. ## Generating API Keys To generate API Key, go to [SuprSend dashboard -> Developers -> API Keys](https://app.suprsend.com/en/staging/developers/api-keys) page and click on `Generate API Key` button. * Next, give your API Key a relevant name and click on '`Create and Save`'. * You'll now see the generated API Key. Copy it and store it somewhere safe in your codebase. For security purposes, API Key can be seen only once at the time of generation. You'll not be able to view the API Key afterwards. Once copied, you can close the modal by clicking on "Done." **Signing API Requests** To authorize your API request, pass your API Key in the `Authorization` header with the contents of the header being “Bearer XXX” where XXX is your API Secret Key. Also, pass content type as `application/json` **Example Header:** ```curl theme={"system"} Authorization: Bearer _APIKey_ Content-Type: application/json ``` # Building with LLMs Source: https://docs.suprsend.com/reference/building-with-llm Use large language models (LLMs) to accelerate how you integrate SuprSend into your applications Whether you’re working with AI-assisted editors such as Cursor, Windsurf, or VS Code with Copilot, SuprSend provides dedicated tooling and resources to help LLMs understand and interact with your notification infrastructure more effectively. ## Plain text docs Every page in the SuprSend documentation can be accessed in plain text format by appending a `.md` extension. For example, this page is available at `building-with-llms.md`. Plain text pages are especially useful when feeding documentation into an LLM, making it easier for the model to guide you during integration. We also provide [llms.txt](https://docs.suprsend.com/llms.txt) and [llms-full.txt](https://docs.suprsend.com/llms-full.txt) endpoints, which outline how AI agents and tools can systematically access the plain text versions of our docs. ## SuprSend MCP Server SuprSend ships an MCP server that exposes SuprSend’s core building blocks—workflows, users, tenants, objects, and preferences—to LLMs and AI agents via the **Model Context Protocol (MCP)**. With the SuprSend MCP server, you can: * Generate and refine code using natural language through AI-powered editors and agent tools. * Query SuprSend documentation directly to resolve integration and setup questions faster. * Build integrations quickly by allowing AI agents to call SuprSend tools without writing custom API wrappers. * Trigger and test workflows interactively using plain-language prompts. The MCP server connects seamlessly with any MCP-compatible client or agent platform. Learn more in our [MCP Server docs](/reference/mcp-overview). # Bulk Update Source: https://docs.suprsend.com/reference/bulk-update-user-preference PATCH /v1/bulk/user/preference/ Bulk API to update channel and category-level notification preferences for multiple users in one request. # Authentication Source: https://docs.suprsend.com/reference/cli-authentication Set up authentication for the SuprSend CLI using service tokens. ## Get Your Service Token 1. Log in to your [SuprSend dashboard](https://app.suprsend.com) 2. Go to **Account Settings → Service Tokens** 3. Create a new service token or copy an existing one ## Authentication Methods The CLI resolves authentication using this priority order: | Priority | Method | When to Use | Duration | | -------- | -------------------- | ------------------------------------------------------------------------- | ------------- | | 1 | Environment Variable | Running the CLI inside **CI/CD pipelines, Docker containers and servers** | Session-based | | 2 | Command-Line Flag | Best for **one-off commands, local testing, debugging** | One-time | | 3 | Active Profile | For local development or self hosting where you have more than 1 server | Persistent | ### Environment Variable **Best for development environments, CI/CD jobs, or scripts.**\ Define your token once per session, and all CLI commands in that script will use it automatically. ```bash theme={"system"} export SUPRSEND_SERVICE_TOKEN="your_service_token_here" ``` ### Command-Line Flag **Best for running ad-hoc commands locally or for testing or debugging with different credentials.**
    Pass the token inline without modifying your environment. We don't recommend this method since flags can appear in shell history or process listings. ```bash theme={"system"} suprsend workflow list --service-token "your_service_token_here" ``` ### Active Profile **Best for local development or self-hosted setups where you have more than one server.**
    Profiles let you save your configuration once, so you don’t need to re-export tokens in every session. You can create multiple profiles and simply switch the active profile when needed. ```bash theme={"system"} suprsend profiles add --name eu-server --token "your_token_here" suprsend profiles use eu-server ``` *** ## Security best practices * **Do not commit tokens** in scripts, repositories, or configuration files. * Prefer **environment variables or profiles** over command-line flags, since flags can appear in shell history or process listings. * **Rotate service tokens regularly** and follow principle of least privilege and restrict permissions to the minimum required. ### Rotation Strategy **1. Scheduled Rotation:** Implement a scheduled rotation of service tokens (e.g. every 6 months) to reduce the risk of long-term exposure. **2. Ad-Hoc Rotation:** Rotate tokens immediately if you suspect that a token has been compromised or if it has been inadvertently exposed. # Enable Autocompletion Source: https://docs.suprsend.com/reference/cli-autocompletion Set up shell autocompletion for SuprSend CLI commands and flags Autocomplete commands and flags with the press of tab to avoid typos and for ease of use. ## Shell Setup This script depends on the 'bash-completion' package. If it is not installed already, you can install it via your OS's package manager. ### Setup completions for current shell session To load completions in your current shell session, run: ```bash bash theme={"system"} source <(suprsend completion bash) ``` ### Persistent setup for all sessions Execute once and autocompletion will for apply for all subsequent sessions. ```bash bash theme={"system"} # Linux suprsend completion bash > /etc/bash_completion.d/suprsend # macOS suprsend completion bash > $(brew --prefix)/etc/bash_completion.d/suprsend ``` You will need to start a new shell for this setup to take effect. ```bash bash theme={"system"} suprsend completion bash ``` If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once: ```bash zsh theme={"system"} echo "autoload -U compinit; compinit" >> ~/.zshrc ``` ### Setup completions for current shell session To load completions in your current shell session, run: ```bash zsh theme={"system"} source <(suprsend completion zsh) ``` ### Persistent setup for all sessions Execute once and autocompletion will for apply for all subsequent sessions. ```bash zsh theme={"system"} # Linux suprsend completion zsh > "${fpath[1]}/_suprsend" # macOS suprsend completion zsh > $(brew --prefix)/share/zsh/site-functions/_suprsend ``` You will need to start a new shell for this setup to take effect. ```bash zsh theme={"system"} suprsend completion zsh [flags] ``` ### Setup completions for current shell session ```bash fish theme={"system"} suprsend completion fish | source ``` ### Persistent setup for all sessions Execute once and autocompletion will for apply for all subsequent sessions. ```bash fish theme={"system"} suprsend completion fish > ~/.config/fish/completions/suprsend.fish ``` You will need to start a new shell for this setup to take effect. ```bash fish theme={"system"} suprsend completion fish [flags] ``` ### Setup completions for current shell session ```powershell pwsh theme={"system"} suprsend completion powershell | Out-String | Invoke-Expression ``` ### Persistent setup for all sessions To load completions for every new session, add the output of the above command to your powershell profile. ```powershell pwsh theme={"system"} suprsend completion powershell >> $PROFILE ``` You will need to start a new shell for this setup to take effect. ```powershell pwsh theme={"system"} suprsend completion powershell [flags] ``` *** ## Flags | Flag | Description | | ------------------- | ------------------------------------------- | | `--no-descriptions` | Disable completion descriptions | | `-h, --help` | Show help for the specific shell completion | ## Verify your setup You can load autocompletion by pressing tab on your keyboard ```bash theme={"system"} suprsend suprsend workflow suprsend -- ``` # Commit Categories Source: https://docs.suprsend.com/reference/cli-category-commit Commit categories to make them live in the workspace This command will make preference category changes live. You don't need to run this command if you already used the `--commit=true` flag with the [`category push`](/reference/cli-category-push) command. ## Syntax ```bash theme={"system"} suprsend category commit [flags] ``` ## Flags | Flag | Description | Default | | ------------------------- | ------------------------------------- | --------- | | `-h, --help` | Show help for the command | – | | `--commit-message string` | Commit message describing the changes | – | | `-w, --workspace string` | Workspace to commit categories to | `staging` | ## Example ```bash theme={"system"} # Commit categories in staging workspace (default) suprsend category commit # Commit categories in production workspace suprsend category commit --workspace production # Commit categories with a descriptive message suprsend category commit --commit-message "Added new preference categories" ``` # List Categories Source: https://docs.suprsend.com/reference/cli-category-list List all preference categories in your workspace. ## Syntax ```bash theme={"system"} suprsend category list [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | ------------------------------------- | --------- | | `-h, --help` | Show help for the command | – | | `-m, --mode string` | Mode of preference Categories to list | `live` | | `-w, --workspace string` | Workspace to list categories from | `staging` | ## Example ```bash theme={"system"} # List categories in staging workspace (default) suprsend category list # List categories in production workspace suprsend category list --workspace production # List categories in draft mode suprsend category list --mode draft ``` # Commands and Flags Source: https://docs.suprsend.com/reference/cli-category-overview Reference for managing preference categories via SuprSend CLI. [Categories](/docs/user-preferences) are used to manage notification preferences in SuprSend. You can use CLI command to manage categories - list, push, pull, and commit. ## Syntax ```bash theme={"system"} suprsend category [command] ``` ## Commands | Command | Description | | --------------------------------------------------- | -------------------------------------------------------------------- | | [`category list`](/reference/cli-category-list) | List categories in a workspace | | [`category pull`](/reference/cli-category-pull) | Pull categories from SuprSend to local directory or server | | [`category push`](/reference/cli-category-push) | Push categories from local directory or server to SuprSend workspace | | [`category commit`](/reference/cli-category-commit) | Commit categories to make them live | ## Inherited Global Flags This command also supports [Global Flags](/reference/cli-global-flags), such as: * `-s, --service-token` – Service token for authentication (default: `$SUPRSEND_SERVICE_TOKEN`) * `-v, --verbosity` – Log level (debug, info, warn, error, fatal, panic) (default `info`) * `--config` – Config file path (default: `$HOME/.suprsend.yaml`) * `-n, --no-color` – Disable color output (default: `$NO_COLOR`) # Pull Categories Source: https://docs.suprsend.com/reference/cli-category-pull Fetch categories from SuprSend workspace to local directory This action does not delete existing categories; instead, it overwrites categories with the same slug or creates new ones.

    By default, pulled categories are stored in the `suprsend/category/` directory, but you can change the destination using `-d, --dir` flag. We recommend specifying a directory when working with multiple production or staging workspaces. ## Syntax ```bash theme={"system"} suprsend category pull [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | --------------------------------- | --------------------- | | `-h, --help` | Show help for the command | – | | `-d, --dir string` | Output directory for categories | `./suprsend/category` | | `-m, --mode string` | Mode to pull categories from | `live` | | `-w, --workspace string` | Workspace to pull categories from | `staging` | ## Example ```bash theme={"system"} # Pull all live categories to default directory suprsend category pull # Pull categories into custom directory path suprsend category pull --dir categories # Pull categories from production workspace suprsend category pull --workspace production # Pull draft categories suprsend category pull --mode draft ``` # Push Categories Source: https://docs.suprsend.com/reference/cli-category-push Push categories from local directory to SuprSend Categories are pushed in draft state until committed.\\ You can either pass the --commit=true flag to push and commit categories in a single step, or commit them later using the [`category commit`](/reference/cli-category-commit) command to make them live. \\ Until committed, changes remain inactive and will not be reflected in your application or workflow settings. ## Syntax ```bash theme={"system"} suprsend category push [flags] ``` ## Flags | Flag | Description | Default | | ----------------------------- | ---------------------------------------------------- | -------------------- | | `-h, --help` | Show help for the command | – | | `-c, --commit string` | Commit the categories | `true` | | `-m, --commit-message string` | Commit message describing the changes | – | | `-d, --dir string` | directory path where pulled categories will be saved | `suprsend/category/` | | `-w, --workspace string` | Workspace to push categories to | `staging` | ## Example ```bash theme={"system"} # Push categories from default directory suprsend category push # Push categories from custom directory suprsend category push --dir categories # Push categories to production workspace suprsend category push --workspace production # Push categories with commit message suprsend category push --commit=true --commit-message "Update notification preferences" ``` *** ### Frequently Asked Questions You can view detailed logs by enabling debug mode `export DEBUG=true` in your terminal. In most cases, this happens when an existing preference category is being overridden by the latest push and that category is already linked to a workflow. When this occurs, the changes cannot be committed to **live mode**. The updated categories will still appear in **draft** mode, but you’ll need to update or unlink the workflows before committing the changes live. # List Events Source: https://docs.suprsend.com/reference/cli-event-list List all events in your workspace. ## Syntax ```bash theme={"system"} suprsend event list [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | ---------------------------------- | --------- | | `-h, --help` | Show help for the command | – | | `-l, --limit int` | Limit the number of events to list | `20` | | `-f, --offset int` | Offset into the list of events | `0` | | `-w, --workspace string` | Workspace to list events from | `staging` | ## Example ```bash theme={"system"} # List events in staging workspace (default) suprsend event list # List events in production workspace suprsend event list --workspace production # List events with limit and offset suprsend event list --limit 10 --offset 5 ``` # Commands and Flags Source: https://docs.suprsend.com/reference/cli-event-overview Reference for managing events in the SuprSend CLI. [Events](/docs/events) are used to trigger workflows in SuprSend. You can use CLI command to manage events and link [schema](/reference/cli-schema-overview) definitions to them - list, push, pull. ## Syntax ```bash theme={"system"} suprsend event [command] ``` ## Commands | Command | Description | | ----------------------------------------- | ---------------------------------------------------------------- | | [`event list`](/reference/cli-event-list) | List events in a workspace | | [`event push`](/reference/cli-event-push) | Push events from local directory or server to SuprSend workspace | | [`event pull`](/reference/cli-event-pull) | Pull events from SuprSend to local directory or server | *** ## Inherited Global Flags This command also supports [Global Flags](/reference/cli-global-flags), such as: * `-s, --service-token` – Service token for authentication (default: `$SUPRSEND_SERVICE_TOKEN`) * `-v, --verbosity` – Log level (debug, info, warn, error, fatal, panic) (default `info`) * `--config` – Config file path (default: `$HOME/.suprsend.yaml`) * `-n, --no-color` – Disable color output (default: `$NO_COLOR`) # Pull Events Source: https://docs.suprsend.com/reference/cli-event-pull Fetch events from SuprSend workspace to local directory Pull events from a SuprSend workspace into your local directory. This action does not delete existing events; instead, it overwrites events with the same name or creates new ones.

    By default, pulled events are stored in the `suprsend/event/` directory, but you can change the destination using the -d flag. We recommend specifying a directory when working with multiple production or staging workspaces. ## Syntax ```bash theme={"system"} suprsend event pull [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | ----------------------------- | ---------------- | | `-h, --help` | Show help for the command | – | | `-d, --dir string` | Directory to pull events to | `suprsend/event` | | `-w, --workspace string` | Workspace to pull events from | `staging` | ## Example ```bash theme={"system"} # Pull all events to default directory suprsend event pull # Pull events into custom directory path suprsend event pull --dir dev-environment/events # Pull events from production workspace suprsend event pull --workspace production ``` # Push Events Source: https://docs.suprsend.com/reference/cli-event-push Push events from local directory to SuprSend ⚠️ Important: Always sync [Schema](/reference/cli-schema-push) changes before pushing Events. If the schema referenced in an event is not available in the target workspace, it will result in validation errors during sync. ## Syntax ```bash theme={"system"} suprsend event push [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | ----------------------------- | ---------------- | | `-h, --help` | Show help for the command | – | | `-d, --dir string` | Directory to push events from | `suprsend/event` | | `-w, --workspace string` | Workspace to push events to | `staging` | ## Example ```bash theme={"system"} # Push events from default directory suprsend event push # Push events from custom directory suprsend event push --dir events # Push events to production workspace suprsend event push --workspace production ``` # Global Flags Source: https://docs.suprsend.com/reference/cli-global-flags Reference for global flags available in the SuprSend CLI. These flags can be used with any command. ## Available Flags | Flag | Default | Env Var | | ---------------------------- | ---------------------- | ------------------------ | | `--config string` | `$HOME/.suprsend.yaml` | – | | `-h, --help` | – | – | | `-n, --no-color` | Disabled | `NO_COLOR` | | `-o, --output string` | `pretty` | – | | `-s, --service-token string` | – | `SUPRSEND_SERVICE_TOKEN` | | `-v, --verbosity string` | `info` | – | | `-w, --workspace string` | `staging` | – | ## Syntax and Example Usage ### --config **string**\ Path to configuration file. This is where your [profiles](/reference/cli-profile-overview) and global settings will be saved. Defaults to `$HOME/.suprsend.yaml`. ```bash theme={"system"} suprsend --config /path/to/config.yaml workflow list ``` ### --help, -h Show help information for any command. ```bash theme={"system"} suprsend --help suprsend -h suprsend workflow --help suprsend workflow -h suprsend workflow list --help suprsend workflow list -h ``` ### --no-color, -n By default, output is colored.
    You can disable colors when piping results to scripts, log files, or CI systems where ANSI codes may cause issues. This can also be set globally via the `NO_COLOR` environment variable. ```bash theme={"system"} suprsend --no-color workflow list suprsend -n workflow list # Set globally via environment variable export NO_COLOR=1 ``` ### --output, -o **string**\ Default - `pretty`.
    Output format: `pretty`, `yaml`, or `json`.
    Use `json` or `yaml` when piping results to other tools or scripts, and `pretty` for human-friendly interactive use. ```bash theme={"system"} suprsend workflow list --output yaml suprsend workflow list --output json | jq '.[].name' ``` ### --service-token, -s **string**\ For locally overriding the authentication token.
    It is not recommended to use this flag in scripts or CI systems, since it will be visible in shell history or process listings. Instead, use [profile](/reference/cli-profile-overview) or set as environment variable `SUPRSEND_SERVICE_TOKEN`. ```bash theme={"system"} suprsend workflow list --service-token "your_token_here" suprsend workflow list -s "your_token_here" # Set globally via environment variable export SUPRSEND_SERVICE_TOKEN="your_token_here" ``` ### --verbosity, -v **string**\ Default - `info`.
    Log level (in increasing order of severity): `debug < info < warn < error < fatal < panic`. Each level includes logs from all levels below it.
    Use higher levels like `warn` or `error` in scripts for cleaner logs, and `debug` during troubleshooting to see detailed request and response information. ```bash theme={"system"} suprsend workflow list --verbosity debug suprsend workflow list -v debug # You can also set debug mode globally via environment variable export DEBUG=true ``` ### --workspace, -w **string**\ Target workspace for commands. Defaults to `staging`. ```bash theme={"system"} suprsend --workspace production workflow list suprsend -w production workflow list ``` # Installation Source: https://docs.suprsend.com/reference/cli-installation Steps to install the SuprSend CLI ## Installation ### Homebrew (Recommended) **Best for developers on macOS or Linux** who want the easiest installation and upgrade path with package manager support. ```bash theme={"system"} brew tap suprsend/tap brew install --cask suprsend ``` ### Binary Releases **Best for Windows users, servers or CI/CD environments** where you need a pre-compiled binary without relying on package managers. Download from [GitHub Releases](https://github.com/suprsend/cli/releases). ```bash theme={"system"} # Linux/macOS chmod +x suprsend sudo mv suprsend /usr/local/bin/ # Windows: Extract suprsend.exe to your PATH ``` ### Source Build **Best for contributors or advanced users** who want the latest unreleased features by building from source. Works on macOS, Linux, and Windows with Go installed. To build SuprSend CLI from source, follow these steps: 1. Ensure you have Go installed on your system (version 1.20 or later). 2. Clone the repository: ```bash theme={"system"} git clone https://github.com/suprsend/cli.git cd cli/cmd/suprsend ``` 3. Build the binary: ```bash theme={"system"} go build -o suprsend ``` 4. The binary will be created in the current directory. You can move it to a location in your PATH for easy access: ```bash theme={"system"} sudo mv suprsend /usr/local/bin/ ``` ## Verify Installation After installation, you can use CLI by running `suprsend` command. For example: ```bash theme={"system"} suprsend --help suprsend version ``` When you run `suprsend version`, you should see the latest version (currently **v1.0.0**). If you see an older version, you may need to update your installation. *** ## Community Contribution We welcome community contributions to improve the SuprSend CLI. If you’d like to add features, fix bugs, or improve documentation: * Visit the [SuprSend CLI GitHub repository](https://github.com/suprsend/cli) * Fork the repo and create a feature branch * Open a Pull Request with a clear description of your changes and we'll review and merge it. # CLI Overview Source: https://docs.suprsend.com/reference/cli-intro Introduction to SuprSend CLI for managing notification infrastructure from the command line. **Beta Feature** - The SuprSend CLI is under active development. Commands and flags may change, and new functionality will be added over time. If you encounter issues or need additional commands, please open an issue on GitHub or contribute directly — the project is open source. The **SuprSend CLI** is a powerful command-line interface that enables developers to setup CI/CD pipeline for notification changes and manage, automate, and sync SuprSend assets across multiple workspaces. With a few commands, you can handle workflows, schemas, events, and even integrate directly with AI agents. *** ## Benefits of using SuprSend CLI Developers often prefer CLI as it offers **speed, automation, and flexibility**. Instead of writing boilerplate code or clicking through multiple screens on UI, you can run a single command or script to perform repeatable actions—ideal for modern DevOps and automation. By using the CLI, you can: * **Automate with CI/CD Deployment** – Release notification changes through feature or bugfix branches, just like any other piece of code: version it, test it, and deploy it. * **Work with assets locally** – Create, edit, and commit workflows, schemas, and translation files locally. * **Version Control Changes** – Pull all assets locally and track them in Git for maintaining release history. * **Enforce Approval gates for production releases** - Setup strict checks that all changes on production go through approval process so that nothing goes live without check. *** ## What You Can Do with SuprSend CLI * **Manage Assets** – List, pull, push, and commit notification workflows, schemas, preference categories, and events. * **Sync Across Workspaces** – Transfer assets between workspaces for multi-environment setups. * **AI Agent Integration** – Start an MCP server from the CLI and connect SuprSend with AI agents or developer copilots. *** ## Available Commands ### Profile Manage [objects](/docs/objects): create, update, list, and promote across workspaces. Also, see Object Management APIs. | Command | Description | | ------------------------------------------------- | ---------------------------- | | [`profile list`](/reference/cli-profile-list) | List profiles in a workspace | | [`profile add`](/reference/cli-profile-add) | Add a new profile | | [`profile modify`](/reference/cli-profile-modify) | Modify an existing profile | | [`profile remove`](/reference/cli-profile-remove) | Remove a profile | | [`profile use`](/reference/cli-profile-use) | Switch to a specific profile | ### Workflow Manage [workflows](/docs/workflows): create, update, enable, disable, and promote across workspaces. Also, see Workflow Management APIs. | Command | Description | | ----------------------------------------------------- | ------------------------------------------- | | [`workflow list`](/reference/cli-workflow-list) | List workflows in a workspace | | [`workflow push`](/reference/cli-workflow-push) | Push local workflows to SuprSend | | [`workflow pull`](/reference/cli-workflow-pull) | Pull workflows from SuprSend to local files | | [`workflow enable`](/reference/cli-workflow-enable) | Enable a workflow | | [`workflow disable`](/reference/cli-workflow-disable) | Disable a workflow | ### Schema Manage [schemas](/docs/validate-workflow-payload): create, update, commit, reset, and promote across workspaces. Also, see Schema Management APIs. | Command | Description | | -------------------------------------------------------- | ------------------------------------------- | | [`schema list`](/reference/cli-schema-list) | List schemas in a workspace | | [`schema push`](/reference/cli-schema-push) | Push local schemas to SuprSend | | [`schema pull`](/reference/cli-schema-pull) | Pull schemas from SuprSend to local files | | [`schema commit`](/reference/cli-schema-commit) | Commit a schema to make it live | | [`generate-types`](/reference/cli-schema-generate-types) | Generate type definitions from JSON schemas | ### Event Manage [events](/docs/events): create, update, list, and promote across workspaces. Also, see Event Management APIs. | Command | Description | | ----------------------------------------- | ---------------------------------------- | | [`event list`](/reference/cli-event-list) | List events in a workspace | | [`event push`](/reference/cli-event-push) | Push local events to SuprSend | | [`event pull`](/reference/cli-event-pull) | Pull events from SuprSend to local files | ### Category Manage [categories](/docs/notification-category): create, update, list, and promote across workspaces. Also, see Category Management APIs. | Command | Description | | --------------------------------------------------- | -------------------------------------------- | | [`category list`](/reference/cli-category-list) | List categories in a workspace | | [`category pull`](/reference/cli-category-pull) | Pull categories from SuprSend to local files | | [`category push`](/reference/cli-category-push) | Push local categories to SuprSend | | [`category commit`](/reference/cli-category-commit) | Commit categories to make them live | ### Sync Manage [sync](/reference/cli-sync): sync assets between workspaces. | Command | Description | | ----------------------------- | ------------------------------ | | [`sync`](/reference/cli-sync) | Sync assets between workspaces | # List MCP Tools Source: https://docs.suprsend.com/reference/cli-mcp-list-tools List all tools supported by the SuprSend MCP server for agent integration. List all available MCP tools that can be used by AI agents. This subcommand retrieves all available tools from the MCP tool registry, formats the response into a structured format with tool type, name, and description for each tool across the four main categories. ## Syntax ```bash theme={"system"} suprsend start-mcp-server list-tools [flags] ``` ## Tools The MCP server provides the following tools organized by category: ### Documentation | Tool Name | Description | | ---------------------- | ------------------------------------------------------ | | `documentation.search` | Enables querying SuprSend documentation | | `documentation.fetch` | Fetch the full documentation content for the given uri | ### Object Management | Tool Name | Description | | ----------------------------- | --------------------------------------------------------------------- | | `objects.get` | Enables querying object information | | `objects.upsert` | Enables upserting object information | | `object.get_subscriptions` | Enables querying subscriptions of an object | | `object.upsert_subscriptions` | Enables upserting subscription to an object | | `objects.get_preferences` | Enables querying object preferences (also within a specific category) | | `objects.update_preferences` | Enables updating a specific category preference for an object | ### Tenant Management | Tool Name | Description | | ---------------------------- | ------------------------------------------------------- | | `tenants.get` | Enables querying tenant information | | `tenants.upsert` | Enables upserting tenant information | | `tenants.update_preferences` | Enables updating category preference for a tenant | | `tenants.get_preferences` | Enables querying all categories preference for a tenant | ### User Management | Tool Name | Description | | ------------------------- | ----------------------------------------------------------- | | `users.get` | Enables querying user information | | `users.upsert` | Enables upserting user information | | `users.get_preferences` | Enables querying user preferences (also within a category) | | `user.update_preferences` | Enables updating a specific category preference for an user | ```bash theme={"system"} suprsend start-mcp-server list-tools [flags] ``` ## Options | Flag | Description | | ------------ | ------------------------- | | `-h, --help` | Show help for the command | ## Examples ```bash Basic Usage theme={"system"} # List all available MCP tools suprsend start-mcp-server list-tools # List tools with default output format suprsend start-mcp-server list-tools --output pretty ``` ```bash Advanced Usage theme={"system"} # List tools with JSON output suprsend start-mcp-server list-tools --output json # List tools with YAML output suprsend start-mcp-server list-tools --output yaml # List tools with verbose output suprsend start-mcp-server list-tools --verbosity debug ``` # Start MCP Server Source: https://docs.suprsend.com/reference/cli-mcp-server Initialize the MCP server for SuprSend platform integration. Start the MCP server to enable AI agents and automated systems to interact with SuprSend platform functionality. This command creates an MCP server with tool, resource, and prompt capabilities, registers all available tools from the four categories, and supports both stdio and SSE transport mechanisms for communication with AI agents. ## Syntax ```bash theme={"system"} suprsend start-mcp-server [flags] ``` ## Options | Flag | Description | Default | | ------------------------ | ------------------------------------------------------ | ------- | | `-T, --tools string` | Types of tools to load (`all` or comma-separated list) | `all` | | `-t, --transport string` | Transport to use (`stdio` or `sse`) | `stdio` | | `-h, --help` | Show help for the command | – | ## Examples ```bash Basic Usage theme={"system"} # Start MCP server with all tools using stdio transport suprsend start-mcp-server # Start MCP server with default configuration suprsend start-mcp-server --transport stdio ``` ```bash Advanced Usage theme={"system"} # Start MCP server with specific tools using SSE transport suprsend start-mcp-server --tools "users,tenants" --transport sse # Start MCP server with SSE transport for web applications suprsend start-mcp-server --transport sse # Start MCP server with custom tool selection suprsend start-mcp-server --tools "workflows,schemas" --transport stdio ``` # Add Profile Source: https://docs.suprsend.com/reference/cli-profile-add Create a new profile configuration in your SuprSend CLI for managing multiple accounts and workspaces. ## Syntax ```bash theme={"system"} suprsend profile add [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `-h, --help` | Show help for the command | – | | `--name string` | Name of the profile (required) | – | | `--service-token string` | Service token (required). You can get service token from [SuprSend dashboard → Account Settings → Service Tokens](https://app.suprsend.com/en/account-settings/service-tokens) section. | – | | `--base-url string` | Base URL | [https://hub.suprsend.com/](https://hub.suprsend.com/) | | `--mgmnt-url string` | Management URL | [https://management-api.suprsend.com/](https://management-api.suprsend.com/) | ## Examples ```bash theme={"system"} # Add a new profile with required fields suprsend profile add --name staging --service-token your-service-token # Add profile with custom API endpoints suprsend profile add \ --name production \ --service-token your-service-token \ --base-url https://hub.suprsend.com/ \ --mgmnt-url https://management-api.suprsend.com/ ``` # List Profile Source: https://docs.suprsend.com/reference/cli-profile-list List all configured SuprSend CLI profiles and their current status. List all configured profiles in your local configuration file. You can also check and edit profiles from config file. Default file path is `$HOME/.suprsend.yaml`. ## Syntax ```bash theme={"system"} suprsend profile list ``` # Modify Profile Source: https://docs.suprsend.com/reference/cli-profile-modify Update an existing profile in the SuprSend CLI configuration ## Syntax You can modify a profile by providing the flags directly in the command or by passing individual field values in interactive mode. ```bash theme={"system"} suprsend profile modify [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | --------------------------------- | ---------------------------------------------------------------------------- | | `-h, --help` | Show help for the command | – | | `--name string` | Name of the profile to modify | – | | `--service-token string` | New service token for the profile | – | | `--base-url string` | New Hub API endpoint | [https://hub.suprsend.com/](https://hub.suprsend.com/) | | `--mgmnt-url string` | New Management API endpoint | [https://management-api.suprsend.com/](https://management-api.suprsend.com/) | ## Example ```bash theme={"system"} # Update service token for a profile suprsend profile modify --name my-profile --service-token new-token # token is mandatory, other fields are optional (just pass the flags you want to modify) suprsend profile modify \ --name production-profile \ --token new-prod-token \ --base-url https://hub.suprsend.com/ \ --mgmnt-url https://management-api.suprsend.com/ ``` # Commands and Flags Source: https://docs.suprsend.com/reference/cli-profile-overview Reference for managing profiles in the SuprSend CLI. Profiles allow you to switch between multiple accounts or servers (if you are using a self-hosted solution) easily without repeatedly entering credentials. ## Commands | Command | Description | | ------------------------------------------------- | ------------------------------- | | [`profile list`](/reference/cli-profile-list) | List all configured profiles | | [`profile add`](/reference/cli-profile-add) | Add a new profile configuration | | [`profile modify`](/reference/cli-profile-modify) | Modify an existing profile | | [`profile remove`](/reference/cli-profile-remove) | Remove a profile configuration | | [`profile use`](/reference/cli-profile-use) | Switch to a specific profile | *** ## Inherited Global Flags This command also supports [Global Flags](/reference/cli-global-flags), such as: * `--config` – Config file (default: `$HOME/.suprsend.yaml`) * `-n`, `--no-color` – Disable color output * `-v`, `--verbosity` – Log level (debug, info, warn, error, fatal, panic) (default "info") # Remove Profile Source: https://docs.suprsend.com/reference/cli-profile-remove Delete unused profile from Local configuration file. ## Syntax ```bash theme={"system"} suprsend profile remove [flags] ``` ## Flags | Flag | Description | Default | | --------------- | ---------------------------------------- | ------- | | `-h, --help` | Show help for the command | – | | `--name string` | Name of the profile to remove (required) | – | ## Example ```bash theme={"system"} suprsend profile remove --name unused-profile ``` # Use Profile Source: https://docs.suprsend.com/reference/cli-profile-use Set the active profile to be used in all subsequent commands ## Syntax ```bash theme={"system"} suprsend profile use [flags] ``` ## Flags | Flag | Description | Default | | --------------- | ---------------------------------------- | ------- | | `-h, --help` | Show help for the command | – | | `--name string` | Profile name to set as active (required) | – | ## Example ```bash theme={"system"} suprsend profile use --name default ``` # CLI Quick Start Source: https://docs.suprsend.com/reference/cli-quick-setup Get started with SuprSend CLI for streamlined development workflows ## Quick Setup Install the SuprSend CLI using your preferred package manager: ```bash npm theme={"system"} npm install -g @suprsend/cli ``` ```bash yarn theme={"system"} yarn global add @suprsend/cli ``` ```bash pnpm theme={"system"} pnpm add -g @suprsend/cli ``` ```bash curl theme={"system"} curl -fsSL https://cli.suprsend.com/install.sh | sh ``` Authenticate with your SuprSend workspace: ```bash theme={"system"} suprsend auth login ``` Enable command autocompletion for a smoother development experience: ```bash bash theme={"system"} # For bash suprsend completion bash >> ~/.bashrc source ~/.bashrc ``` ```bash zsh theme={"system"} # For zsh suprsend completion zsh >> ~/.zshrc source ~/.zshrc ``` ```bash fish theme={"system"} # For fish suprsend completion fish >> ~/.config/fish/completions/suprsend.fish ``` Pull your existing workflows and sync your project: ```bash theme={"system"} # Pull all workflows from your workspace suprsend workflow pull # Initialize sync for your project suprsend sync ``` This will create a `suprsend` directory with your workflows, schemas, and configurations. ## Try a Complete Workflow Let's walk through a typical development workflow: ```bash theme={"system"} suprsend sync ``` Create a new workflow file locally: ```bash theme={"system"} # Create a new workflow suprsend workflow create --name "welcome-email" --template "email" ``` Test the workflow with sample data: ```bash theme={"system"} # Test with sample user data suprsend workflow test --workflow-id "welcome-email" --user-id "test-user" ``` Push your changes to production: ```bash theme={"system"} suprsend workflow push --workflow-id "welcome-email" suprsend workflow enable --workflow-id "welcome-email" ``` ## Key Developer Features The SuprSend CLI is designed with developers in mind, offering several advantages. For a complete reference of all available commands and options, see the [CLI Reference](/reference/cli-intro). Clean, parseable output perfect for CI/CD pipelines Fine-tune output detail with verbosity flags Switch between different workspaces and environments Generate TypeScript types from your schemas Multiple output formats: JSON, YAML, and pretty for different use cases Perform bulk operations efficiently # Commit Schema Source: https://docs.suprsend.com/reference/cli-schema-commit Make schema changs live in a SuprSend workspace. Commit a draft schema to live status in your SuprSend workspace. Until a draft is committed, your latest changes remain inactive and are not used for notification validation.
    You do not need to run this command separately if you already used the `--commit=true` flag with the [`schema push`](/reference/cli-schema-push) command. ## Syntax ```bash theme={"system"} suprsend schema commit [flags] ``` **Arguments:** * `` - Slug of the schema to commit (required) ## Flags | Flag | Description | Default | | ----------------------------- | ------------------------------------- | ------- | | `-h, --help` | Show help for the command | – | | `-m, --commit-message string` | Commit message describing the changes | – | ## Example ```bash theme={"system"} # Commit a schema in staging workspace to live status suprsend schema commit my-schema # Commit schema in production workspace with commit message suprsend schema commit notification-schema --workspace production --commit-message "Commit message" ``` # Generate Types Source: https://docs.suprsend.com/reference/cli-schema-generate-types Generate strongly-typed programming language interfaces from all enabled SuprSend schemas in a workspace. The `generate-types` command converts all enabled SuprSend schemas from a specified workspace into strongly-typed programming language interfaces. This command processes all enabled schemas with meaningful content from the workspace, not just a single schema. ## Syntax ```bash theme={"system"} suprsend generate-types [flags] ``` **Arguments:** * `` - Path to the output file where types will be generated (required) **Supported File Types:** We currently support `.ts`, `.go`, `.py`, `.kt`, `.swift` files. Additional extensions like `.java` and `.dart` may also be supported. ## Options | Flag | Description | Default | | ---------------------- | ------------------------------------------ | ------- | | `-h, --help` | Show help for the command | – | | `--build-flags string` | Flags to generate types in a certain way | – | | `--mode string` | Mode of schema to fetch (currently unused) | `live` | ## Examples ```bash Basic Usage theme={"system"} # Generate TypeScript types from all enabled schemas in staging workspace suprsend generate-types types.ts # Generate Go types from all enabled schemas in staging workspace suprsend generate-types types.go ``` ```bash Advanced Usage theme={"system"} # Generate types from production workspace suprsend generate-types types.ts --workspace production # Generate Go types with custom build flags suprsend generate-types types.go --build-flags "flag1,flag2" # Generate Python types from staging workspace suprsend generate-types types.py --workspace staging # Generate Kotlin types suprsend generate-types types.kt --workspace production # Generate Swift types suprsend generate-types types.swift --workspace staging ``` # List Schemas Source: https://docs.suprsend.com/reference/cli-schema-list List all notification schemas in your workspace. ## Syntax ```bash theme={"system"} suprsend schema list [flags] ``` ## Flags | Flag | Description | Default | | --------------------- | ------------------------------------- | -------- | | `-h, --help` | Show help for the command | – | | `-l, --limit int` | Limit the number of schemas to list | `20` | | `-m, --mode string` | Mode of schemas to list (draft, live) | `live` | | `-f, --offset int` | Offset the number of schemas to list | `0` | | `-o, --output string` | Output Style (pretty, yaml, json) | `pretty` | ## Example ```bash theme={"system"} # List schemas in staging workspace (default) suprsend schema list # List schemas in production workspace suprsend schema list --workspace production # List schemas in draft mode, with limit and offset suprsend schema list --limit 10 --offset 5 --mode draft # List schemas in JSON format and pipe to jq suprsend schema list --output json | jq '.[].slug' ``` # Commands and Flags Source: https://docs.suprsend.com/reference/cli-schema-overview Reference for managing schemas in the SuprSend CLI. [Schema](/docs/validate-workflow-payload) define the payload structure and validation rules for workflow and event trigger. You can use CLI command to manage schemas - list, pull, push, commit. ## Syntax ```bash theme={"system"} suprsend schema [flags] ``` ## Commands | Command | Description | | ----------------------------------------------- | --------------------------------------------- | | [`schema list`](/reference/cli-schema-list) | List schemas in a workspace | | [`schema pull`](/reference/cli-schema-pull) | Pull schemas from workspace to local | | [`schema push`](/reference/cli-schema-push) | Push local schemas to workspace | | [`schema commit`](/reference/cli-schema-commit) | Commit a schema to make it draft changes live | ## Inherited Global Flags This command also supports [Global Flags](/reference/cli-global-flags), such as: * `-w, --workspace` – Workspace to use (default: `staging`) * `-s, --service-token` – Service token for authentication (default: `$SUPRSEND_SERVICE_TOKEN`) * `-v, --verbosity` – Log level (debug, info, warn, error, fatal, panic) (default `info`) * `--config` – Config file path (default: `$HOME/.suprsend.yaml`) * `-n, --no-color` – Disable color output (default: `$NO_COLOR`) # Pull Schemas Source: https://docs.suprsend.com/reference/cli-schema-pull Download schemas from a SuprSend workspace in your local directory or server Pull schemas from a SuprSend workspace into your local directory or server. This action does not delete existing schemas; instead, it overwrites schemas with the same slug or creates new ones.

    By default, pulled schemas are stored in the `suprsend/schema/` directory, but you can change the destination using the -d flag. We recommend specifying a directory when working with multiple production or staging workspaces. ## Syntax ```bash theme={"system"} suprsend schema pull [flags] ``` ## Flags | Flag | Description | Default | | ------------------- | ------------------------------------- | ----------------- | | `-h, --help` | Show help for the command | – | | `-d, --dir string` | Directory to pull schemas | `suprsend/schema` | | `-m, --mode string` | Mode of schemas to pull (draft, live) | `live` | | `-g, --slug string` | Slug to pull individual schema | – | ## Example ```bash theme={"system"} # Pull schemas from staging workspace (default) suprsend schema pull # Pull schemas from production workspace suprsend schema pull --workspace production # Pull individual schema by passing it's slug suprsend schema pull --slug my-schema # Pull schemas into custom directory path suprsend schema pull --dir schemas # Pull draft schemas from production suprsend schema pull --mode draft --workspace production ``` # Push Schemas Source: https://docs.suprsend.com/reference/cli-schema-push Upload schema files from your local directory or server to SuprSend workspace. This command reads and validates schema files locally before pushing them to the target workspace, where they are created in draft mode. To commit schemas while pushing, include the `--commit=true` flag.
    Any schema that fails validation will be skipped and not pushed to the workspace. ## Syntax ```bash theme={"system"} suprsend schema push [flags] ``` ## Options | Flag | Description | Default | | ----------------------------- | ------------------------------------------------------- | ----------------- | | `-h, --help` | Show help for the command | – | | `-c, --commit string` | Commit the schemas (--commit=true) | `true` | | `-m, --commit-message string` | Commit message describing the changes for --commit=true | – | | `-d, --dir string` | Directory for schemas pull to | `suprsend/schema` | | `-g, --slug string` | Slug of schema to push | – | ## Example ```bash theme={"system"} # Push schemas to staging workspace (default) suprsend schema push # Push schemas to production workspace suprsend schema push --workspace production # Push individual schema by passing it's slug suprsend schema push --slug my-schema # Push schemas and commit them suprsend schema push --commit=true --commit-message "Commit message" ``` *** 🚨 **Push and Commit Behavior**: * **Schemas not showing up in live mode?** → Check if they're linked to workflows * **Fix**: Update your workflow files to include the new schemas, or remove the workflow links # Sync Assets Source: https://docs.suprsend.com/reference/cli-sync Merge assets from workspace to another workspace Clone assets between different workspaces. This command fetches assets from the source workspace, validates them, and pushes them to the destination workspace. Assets failing validation are not pushed to target workspace. ## Syntax ```bash theme={"system"} suprsend sync [flags] ``` ## Flags | Flag | Description | Default | | ------------------------ | ----------------------------------------------------------------- | ------------ | | `-a, --assets string` | Assets to sync (`all`, `workflow`, `schema`, `event`, `category`) | `all` | | `-f, --from string` | Source workspace (required) | `staging` | | `-m, --mode string` | Mode to sync assets (`draft`, `live`) | `live` | | `-t, --to string` | Destination workspace (required) | `production` | | `-d, --directory string` | custom directory path to sync to locally save pulled assets | `suprsend` | | `-h, --help` | Show help for the command | – | ## Example ```bash theme={"system"} # Sync all assets from staging to production suprsend sync # Sync all assets between custom workspaces suprsend sync --from dev-staging --to dev-production # Sync workflows from staging to production suprsend sync --assets workflow # Sync draft schemas from staging to production suprsend sync --assets schema --mode draft ``` # Disable Workflow Source: https://docs.suprsend.com/reference/cli-workflow-disable Deactivate a workflow in a SuprSend workspace to stop processing notifications. ## Syntax ```bash theme={"system"} suprsend workflow disable [flags] ``` **Arguments:** * `` - Slug of the workflow to disable (required) ## Example ```bash theme={"system"} # Disable workflow in default workspace suprsend workflow disable my-workflow # Disable workflow in another workspace suprsend workflow disable user-signup --workspace production ``` # Enable Workflow Source: https://docs.suprsend.com/reference/cli-workflow-enable Enable a workflow in a SuprSend workspace to start processing notifications. All pushed workflows are enabled by default. Workflows have to be live to be enabled. ## Syntax ```bash theme={"system"} suprsend workflow enable [flags] ``` **Arguments:** * `` - Slug of the workflow to enable (required) ## Example ```bash theme={"system"} # Enable workflow in staging workspace suprsend workflow enable my-workflow # Enable workflow in another workspace suprsend workflow enable user-signup --workspace production ``` # List Workflows Source: https://docs.suprsend.com/reference/cli-workflow-list List workflows in a workspace ## Syntax ```bash theme={"system"} suprsend workflow list [flags] ``` ## Flags | Flag | Description | Default | | --------------------- | ---------------------------------------- | -------- | | `-h, --help` | Show help for the command | – | | `-l, --limit int` | Number of workflows to list | `20` | | `-f, --offset int` | Starting position for pagination | `0` | | `-m, --mode string` | Workflow mode (`live` or `draft`) | `live` | | `-o, --output string` | Output format (`pretty`, `json`, `yaml`) | `pretty` | ## Example ```bash theme={"system"} # List live workflows in staging workspace suprsend workflow list # List with custom limit and workspace suprsend workflow list --limit 50 --workspace production # List draft workflows suprsend workflow list --mode draft # piping JSON output to jq suprsend workflow list --output json | jq '.[].slug' ``` # Commands and Flags Source: https://docs.suprsend.com/reference/cli-workflow-overview Reference for managing workflows in the SuprSend CLI. [Workflows](/docs/workflows) define the notifications logic in SuprSend. You can use CLI command to manage workflows - list, push, pull, enable, disable. ## Syntax ```bash theme={"system"} suprsend workflow [command] ``` ## Commands | Command | Description | | ----------------------------------------------------- | ----------------------------------------------- | | [`workflow list`](/reference/cli-workflow-list) | List workflows in a workspace | | [`workflow push`](/reference/cli-workflow-push) | Push workflows from local directory to SuprSend | | [`workflow pull`](/reference/cli-workflow-pull) | Pull workflows from SuprSend to local directory | | [`workflow enable`](/reference/cli-workflow-enable) | Enable a workflow | | [`workflow disable`](/reference/cli-workflow-disable) | Disable a workflow | *** ## Inherited Global Flags This command also supports [Global Flags](/reference/cli-global-flags), such as: * `-s, --service-token` – Service token for authentication * `-v, --verbosity` – Logging level * `--config` – Config file path * `-n, --no-color` – Disable color output * `-w, --workspace string` – Workspace to use (default: `staging`) # Pull Workflows Source: https://docs.suprsend.com/reference/cli-workflow-pull Fetch workflows from SuprSend workspace to local directory or server Pull workflows from a SuprSend workspace into your local directory or server. This action does not delete existing workflows; instead, it overwrites workflows with the same slug or creates new ones.

    By default, pulled workflows are stored in the `suprsend/workflow/` directory, but you can change the destination using the -d flag. We recommend specifying a directory when working with multiple production or staging workspaces. ## Syntax ```bash theme={"system"} suprsend workflow pull [flags] ``` ## Flags | Flag | Description | Default | | ------------------- | ------------------------------------------------- | ------------------- | | `-h, --help` | Show help for the command | – | | `-g, --slug string` | Pull individual workflow by passing it's slug | – | | `-d, --dir string` | Custom directory path for saving pulled workflows | `suprsend/workflow` | | `-m, --mode string` | Workflow mode to pull (`live` or `draft`) | `live` | ## Example ```bash theme={"system"} # Pull all live workflows to current directory suprsend workflow pull # Pull workflows into custom directory path suprsend workflow pull --dir dev-environment/workflows # Pull draft workflows from production suprsend workflow pull --mode draft --workspace production # Pull individual workflow by passing it's slug suprsend workflow pull --slug welcome-email ``` # Push Workflows Source: https://docs.suprsend.com/reference/cli-workflow-push Push workflows from local directory or server to SuprSend ⚠️ Important: Always sync [Schema](/reference/cli-schema-push) and [Category](/reference/cli-category-push) changes before pushing Workflows. If the schema or category referenced in a workflow is not available in the target workspace, it will result in validation errors during sync. Pushed workflows are saved in draft version. Make sure to pass `--commit=true` flag to commit the workflows while pushing it. ## Syntax ```bash theme={"system"} suprsend workflow push [flags] ``` ## Flags | Flag | Description | Default | | ----------------------------- | ------------------------------------------------------- | ------------------- | | `-h, --help` | Show help for the command | – | | `-c, --commit string` | Commit the workflows (--commit=true) | `true` | | `-m, --commit-message string` | Commit message describing the changes for --commit=true | – | | `-d, --dir string` | Output directory for workflows | `suprsend/workflow` | | `-g, --slug string` | Slug of the workflow to push | – | ## Example ```bash theme={"system"} # Push workflows from default directory suprsend workflow push # Push workflows from custom directory suprsend workflow push --dir dev-environment/workflows # Push individual workflow by passing it's slug suprsend workflow push --slug welcome-email # Push workflows and commit them suprsend workflow push --commit=true --commit-message "Commit message" ``` *** 🚨 **Push and Commit Behavior**: * **New workflow not showing up?** → Push without commit first, then commit separately * **Changes not appearing in live mode?** → Check if workflows are linked to other workflows * **Fix**: Update your workflow files to include all dependencies, or remove the links # Commit Preference Category Source: https://docs.suprsend.com/reference/commit-category PATCH /v1/{workspace}/preference_category/commit/ Commit draft changes to the preference category to make them live # Commit Schema Source: https://docs.suprsend.com/reference/commit-schema PATCH /v1/{workspace}/schema/{schema_slug}/commit/ Commit a draft schema to make it live. Changes will start to take effect as soon as it's live. # Commit Workflow Source: https://docs.suprsend.com/reference/commit-workflow PATCH /v1/{workspace}/workflow/{slug}/commit/ Commit a workflow to make the draft version live. # Create Event Source: https://docs.suprsend.com/reference/create-event POST /v1/{workspace}/event/ Add events and their linked schemas in SuprSend # Create a List Source: https://docs.suprsend.com/reference/create-list POST /v1/subscriber_list/ API to create / manage lists to send notification to a bulk list of users. # Create or Update Preference Category Source: https://docs.suprsend.com/reference/create-update-category POST /v1/{workspace}/preference_category/ Use this API to set preference categories to be used in workflow or to show on user preference page. ⚠️ This API call will **override existing categories** with whatever is passed in the request body. Recommended approach is to first fetch the current categories using [GET method](/reference/get-category), modify the response as needed, and then send it back in the request body.
    You can read more about preference categories [here](/docs/notification-category).

    Note: Changes will only be pushed if `"validation_result":{"is_valid":true}` in the response. General reasons for validation failure are: * There are multiple categories with the same slug * Some subcategories have been deleted in this version, on which active workflows are configured * You've not passed all three categories in the request body - system, transactional, promotional

    # Create / Update Objects Source: https://docs.suprsend.com/reference/create-update-objects POST /v1/object/{object_type}/{id}/ API to upsert (create if not exists, update if exists) object by providing a unique object ID and type. # Create / Update Tenants Source: https://docs.suprsend.com/reference/create-update-tenants POST /v1/tenant/{tenant_id} API to create a new Tenant OR update an existing Tenant # Create / Update Users Source: https://docs.suprsend.com/reference/create-update-users POST /v1/user/{distinct_id}/ API to upsert (create if not exists, update if exists) user profile using a `distinct_id`. # Create or Update Workflow Source: https://docs.suprsend.com/reference/create-update-workflow POST /v1/{workspace}/workflow/{slug}/ Create a new workflow or update the draft version of an existing one # Delete Draft List Source: https://docs.suprsend.com/reference/delete-draft-list PATCH /v1/subscriber_list/{list_id}/version/{version_id}/delete API to delete draft version of the list. Use this to discard test or interim versions before publishing the final list. # Delete a List Source: https://docs.suprsend.com/reference/delete-list PATCH /v1/subscriber_list/{list_id}/delete API to permanently delete a list. # Delete an Object Source: https://docs.suprsend.com/reference/delete-object DELETE /v1/object/{object_type}/{id}/ API to permanently delete an object by passing `object_type` and `id`. # Remove Object Subscriptions Source: https://docs.suprsend.com/reference/delete-object-subscription DELETE /v1/object/{object_type}/{id}/subscription/ API to remove object subscribers (users or child objects). # Delete Tenant Source: https://docs.suprsend.com/reference/delete-tenant DELETE /v1/tenant/{tenant_id} API to permanently delete tenant and it's associated settings. # Delete User Source: https://docs.suprsend.com/reference/delete-user DELETE /v1/user/{distinct_id}/ API to permanently delete a user from SuprSend by passing `distinct_id`. # Delete Workflow Source: https://docs.suprsend.com/reference/delete-workflow DELETE /v1/{workspace}/workflow/{slug}/ Permanently deletes a workflow by providing it's slug. # Delink Schema from Event Source: https://docs.suprsend.com/reference/delink-event-schema PATCH /v1/{workspace}/event/{url_encoded_event_name}/delink_schema/ Remove the linked schema from an event, making it schema-less. This allows the event to accept any payload structure without validation against a predefined schema. # Dynamic Workflow Trigger Source: https://docs.suprsend.com/reference/dynamic-workflow-trigger POST /{workspace_key}/trigger/ API to dynamically create and trigger a single step workflow. # Edit Object Profile Source: https://docs.suprsend.com/reference/edit-object-profile PATCH /v1/object/{object_type}/{id}/ API to edit (add/remove) properties and communication-channels for an object. # Edit User Profile Source: https://docs.suprsend.com/reference/edit-user-profile PATCH /v1/user/{distinct_id}/ API to edit (add/remove) properties and communication-channels for a user. # Enable/Disable Workflow Source: https://docs.suprsend.com/reference/enable-disable-workflow PATCH /v1/{workspace}/workflow/{slug}/enable/ Enable or disable a workflow. Disabled workflows will not be executed when triggered. By default, workflows are enabled unless you explicitly disable them. # Fetch User list subscriptions Source: https://docs.suprsend.com/reference/fetch-user-list-subscriptions GET /v1/user/{distinct_id}/subscribed_to/list/ API to retrieve all lists that a user is part of. # Fetch User object subscriptions Source: https://docs.suprsend.com/reference/fetch-user-object-subscriptions GET /v1/user/{distinct_id}/subscribed_to/object/ API to fetch all objects that a given user is subscribed to. # Finish Sync Source: https://docs.suprsend.com/reference/finish-sync PATCH /v1/subscriber_list/{list_id}/version/{version_id}/finish_sync API to finish sync and make the draft list version live. # Get all Lists Source: https://docs.suprsend.com/reference/get-all-lists GET /v1/subscriber_list/ API to fetch a paginated list of all lists created in your workspace. # Get Preference Category Source: https://docs.suprsend.com/reference/get-category GET /v1/{workspace}/preference_category/ Retrieve the current preference category for a workspace # Get Event Details Source: https://docs.suprsend.com/reference/get-event GET /v1/{workspace}/event/{url_encoded_event_name}/ Fetch event details and its linked schema. # Get Linked Workflows Source: https://docs.suprsend.com/reference/get-linked-workflows GET /v1/{workspace}/event/{url_encoded_event_name}/linked_workflows/ Fetch list of all workflows where this event is used # Get List Configuration Source: https://docs.suprsend.com/reference/get-list-details GET /v1/subscriber_list/{list_id}/ API to fetch list metadata like list type, count, source, and status. Doesn't include users in the list. # Get list users Source: https://docs.suprsend.com/reference/get-list-users GET /v1/subscriber_list/{list_id}/subscriber/ API to fetch a cursor-based paginated list of all users in the list. # Fetch Object by ID Source: https://docs.suprsend.com/reference/get-object-by-id GET /v1/object/{object_type}/{id}/ API to fetch Object details by passing object type and ID. # All Categories Source: https://docs.suprsend.com/reference/get-object-category-preference GET /v1/object/{object_type}/{id}/preference/category/ API to fetch user preferences across all categories. # All Channels Source: https://docs.suprsend.com/reference/get-object-channel-preferences GET /v1/object/{object_type}/{id}/preference/channel_preference/ API to retrieve all channel preferences for a given object. # Full Preference Source: https://docs.suprsend.com/reference/get-object-full-preference GET /v1/object/{object_type}/{id}/preference/ API to fetch object preferences across all channels and categories. # Single Category Source: https://docs.suprsend.com/reference/get-object-single-category-preference GET /v1/object/{object_type}/{id}/preference/category/{category_slug} API to fetch object preferences for a given category. # Fetch Object Subscriptions Source: https://docs.suprsend.com/reference/get-object-subscriptions GET /v1/object/{object_type}/{id}/subscribed_to/object/ API to fetch a paginated list of all parent objects that the given child object is subscribed to. # Get Schema Source: https://docs.suprsend.com/reference/get-schema GET /v1/{workspace}/schema/{schema_slug}/ Fetch the JSON schema corresponding to a slug. # Fetch Template details Source: https://docs.suprsend.com/reference/get-template-details GET /v1/template/{template_slug} API to fetch content of a template group (active & draft versions) across all associated channels and languages. # Fetch Template content for a Channel Source: https://docs.suprsend.com/reference/get-template-details-for-channel GET /v1/template/{template_slug}/channel/{channel_slug} API to fetch content of a particular channel in a template group (live & draft versions). # Fetch Template List Source: https://docs.suprsend.com/reference/get-template-list GET /v1/template API to fetch paginated list of all templates in a given workspace. # Get Tenant Default Preference Source: https://docs.suprsend.com/reference/get-tenant-category-preferences GET /v1/tenant/{tenant_id}/category/ API to get default tenant preference for all categories. # Get Tenant Data Source: https://docs.suprsend.com/reference/get-tenant-data GET /v1/tenant/{tenant_id} API to fetch tenant settings (logo, colors, links, preference) corresponding to a tenant_id. To create or update tenant data, use the [Create/Update Tenant](#create-update-tenants) endpoint. To create or update tenant data, use the [Create/Update Tenant](#create-update-tenants) endpoint. To create or update tenant data, use the [Create/Update Tenant](#operation/create-tenants) endpoint. # Get Tenant List Source: https://docs.suprsend.com/reference/get-tenant-list GET /v1/tenant/ API to fetch a paginated list of tenants available in your workspace. # Single Category Source: https://docs.suprsend.com/reference/get-user-category-preferences GET /v1/user/{distinct_id}/preference/category/{category_slug} API to fetch user preferences for a given category. # All Channels Source: https://docs.suprsend.com/reference/get-user-channel-preferences GET /v1/user/{distinct_id}/preference/channel_preference/ API to retrieve user preferences for all channels. # Full Preference Source: https://docs.suprsend.com/reference/get-user-full-preference GET /v1/user/{distinct_id}/preference/ API to fetch user preferences across all channels and categories. # Fetch User by id Source: https://docs.suprsend.com/reference/get-user-profile GET /v1/user/{distinct_id}/ API to fetch user object by passing it's unique `distinct_id`. # Get Workflow Details Source: https://docs.suprsend.com/reference/get-workflow GET /v1/{workspace}/workflow/{slug}/ Fetch workflow corresponding to the given slug in a workspace. # Idempotent Requests Source: https://docs.suprsend.com/reference/idempotent-requests Learn how to avoid duplicate API requests with idempotency key. SuprSend supports making requests idempotent to ensure that requests can be retried safely without duplicate processing. If SuprSend receives and processes a request with an `idempotency_key`, it will skip processing requests with same `idempotency_key` for next 24 hours. You can use this key to track webhooks related to workflow notifications. To make an idempotent request, pass `idempotency_key` in the workflow instance. Idempotency-key should be unique that you generate for each request. You may use any string up to 255 characters in length as an idempotency-key. Ensure that you don’t add any space in start and end of the key as it will be trimmed. Here are some common approaches for assigning idempotency-keys: * **Generate a random UUID** for each request. * **Construct the idempotency-key by combining relevant information about the request**. This can include parameters, identifiers, or specific contextual details that are meaningful within your application. e.g., you could concatenate the user ID, action, and timestamp to form an idempotency-key like `user147-new-comment-1687437670` * **Request-specific Identifier**: If your request already contains a unique identifier, such as an order ID or a job ID, you can use that identifier directly as the idempotency-key. # List Events Source: https://docs.suprsend.com/reference/list-events GET /v1/{workspace}/event/ Retrieve a list of events in a workspace. # List Object Subscriptions Source: https://docs.suprsend.com/reference/list-object-subscriptions GET /v1/object/{object_type}/{id}/subscription/ API to fetch a paginated list of users or child objects subscribed to a given object. # List Objects by Type Source: https://docs.suprsend.com/reference/list-objects-by-type GET /v1/object/{object_type}/ API to fetch paginated list of objects that belong to the specified `object_type`. # List Schemas Source: https://docs.suprsend.com/reference/list-schemas GET /v1/{workspace}/schema/ Retrieve a list of schemas in a workspace. # List Users Source: https://docs.suprsend.com/reference/list-users GET /v1/user/ API to list cursor-based paginated list of users in your workspace. # List Workflows Source: https://docs.suprsend.com/reference/list-workflows GET /v1/{workspace}/workflow/ Retrieve a list of workflows in a workspace. # MCP Server Source: https://docs.suprsend.com/reference/mcp-overview Use the SuprSend MCP server to connect SuprSend with LLMs and AI agents **The SuprSend MCP server is in Beta**. Since LLM behavior can vary and responses aren’t always deterministic, we recommend starting with a development workspace before rolling it out in production. The SuprSend MCP server exposes key SuprSend components—such as workflows, users, tenants, objects, and preferences—through the Model Context Protocol. With MCP, your AI agents can directly query, update, and manage SuprSend resources, as well as trigger notifications, all through tool calling. ## What can you do with MCP? Here are some examples of how you can use MCP server for building with LLM and SuprSend: * **Search documentation and apply results.** "Find the setup guide for integrating the React Drop-in Inbox and insert the required code snippet into my project." * **Trigger workflows on demand.** “Run the approval-required workflow for user John Doe to test my notification setup.” * **Bootstrap test data.** "Create a sample user named John Doe and a tenant called acme-corp in my workspace.” * **Manage notification preferences.** “Enable email notifications for marketing team and disable SMS.” * **Configure tenant-level branding.** “Update the logo and primary color for the enterprise tenant to match the new design guidelines.” ## Authentication The MCP server requires a Service Token for authentication. Generate one from your SuprSend dashboard: [Account Settings -> Service Tokens](https://app.suprsend.com/en/account-settings/service-tokens). **Note:** If you have configured your service token using environment variables (`SUPRSEND_SERVICE_TOKEN`) or CLI profiles, you can omit the `--service-token` flag from the MCP server configuration. ## Starting MCP Server from CLI ```bash theme={"system"} suprsend start-mcp-server # Start MCP server with specific tools using SSE transport suprsend start-mcp-server --tools "users,tenants" -t sse ``` Flags include: * `--tools` – restricts which tools are available (recommended). * `--transport` – transport to use (`stdio` or `sse`). ## Setting up MCP in IDE ### Claude 1. Open Claude Settings → Developer Section. 2. Click Edit Config under Local MCP Servers. 3. Add the following snippet: ```json theme={"system"} { "mcpServers": { "suprsend": { "command": "suprsend", "args": [ "start-mcp-server" ] } } } ``` ### Cursor 1. Go to Cursor -> Settings -> Cursor Settings 2. On the settings modal, Go to MCP & Integrations and click on "New MCP Server". 3. Inside mcp.json file, add below code: ```json theme={"system"} { "mcpServers": { "suprsend": { "command": "suprsend", "args": [ "start-mcp-server" ] } } } ``` ### Windsurf 1. Open Windsurf settings → Extensions. 2. Add the SuprSend MCP configuration: ```json theme={"system"} { "suprsend": { "command": "suprsend", "args": ["start-mcp-server"], "name": "SuprSend MCP Server" } } ``` ## Tool List Here’s a list of all tools available in the MCP server. We'll keep adding more tools to this list as we build out the MCP server: | TOOL TYPE | TOOL NAME | TOOL DESCRIPTION | | ----------------- | ------------------------------ | ---------------------------------------------------------------------- | | **documentation** | `documentation.search` | Search across SuprSend documentation for relevant topics or examples. | | **documentation** | `documentation.fetch` | Retrieve the full content of a specific documentation page by its URI. | | **objects** | `objects.get` | Fetch details for a specific object by ID or key. | | **objects** | `objects.upsert` | Create a new object or update an existing one. | | **objects** | `objects.get_subscriptions` | View all subscriptions associated with an object. | | **objects** | `objects.upsert_subscriptions` | Add or update subscriptions for an object. | | **objects** | `objects.get_preferences` | Retrieve an object’s preferences, optionally filtered by category. | | **objects** | `objects.update_preferences` | Update category-level preferences for an object. | | **tenants** | `tenants.get` | Fetch information about a specific tenant. | | **tenants** | `tenants.upsert` | Create a new tenant or update an existing one. | | **tenants** | `tenants.get_preferences` | Retrieve all category preferences for a tenant. | | **tenants** | `tenants.update_preferences` | Update category-level preferences for a tenant. | | **users** | `users.get` | Fetch information about a specific user. | | **users** | `users.upsert` | Create a new user or update an existing one. | | **users** | `users.get_preferences` | Retrieve a user’s preferences, optionally filtered by category. | | **users** | `users.update_preferences` | Update category-level preferences for a user. | ## Configuring the available tools By default, the MCP server exposes all tools. In most cases, you won’t want to grant your AI agent access to everything—especially in production. Instead, you can control which tools are available by using the `--tools` flag when starting the MCP server. This helps ensure the agent only interacts with the resources it actually needs. The `--tools` flag accepts one or more tool identifiers in the format tool\_type.tool. You can also use a wildcard (*) to enable all tools of a type. For example, users.* will allow access to all user-related tools, while users.get\_preferences will expose only that single tool. Here's an example of how to pass tool configuration: ```json theme={"system"} { "suprsend": { "command": "suprsend", "args": [ "start-mcp-server", "--tools", "users.createUser,tenants.*" ], "name": "SuprSend MCP Server" } } ``` # Merge User Profiles Source: https://docs.suprsend.com/reference/merge-users POST /v1/user/{distinct_id}/merge/ API to merge two user profiles to resolve duplicate user records. In SuprSend, you can merge duplicate user identities into a single `distinct_id` to consolidate user profiles, especially when users interact across different products or transition from anonymous to identified states. You can use it in following scenarios: * If a user can log into multiple products (such as different apps or services within your platform) and has different identifiers across products which needs to be merged later. * **Anonymous to Identified Transition**: Many platforms allow users to interact or explore the product anonymously before signing up. During this period, user actions are typically tracked under an anonymous ID. Once the user signs up or logs in, you can merge the anonymous profile into their newly created identifier. This preserves any data collected during the anonymous period and associates it with the user’s identified profile. You need to [create a user](https://docs.suprsend.com/reference/create-update-users) with the new identifier first before merging the profiles. ### How profile merge works? When you merge two users, there's a primary user passed in your API path param and merge a secondary / old user passed as `from_user_id` in your payload. The primary `distinct_id` is retained after the merge and the `from_user_id` is deleted. The deletion process is permanent and `from_user_id` can't be recovered once deleted. # Category and Channel within Category Source: https://docs.suprsend.com/reference/object-preference-category PATCH /v1/object/{object_type}/{id}/preference/category/{category_slug}/ API to update object preferences for a specific notification category. # Overview Source: https://docs.suprsend.com/reference/overview A quick overview of SuprSend API reference. SuprSend API enables you to add a powerful notification engine to your product. SuprSend offers a complete set of features that allow you to send notifications reliably and at scale, while ensuring a positive end-user experience, thus eliminating the need to build a notification service in-house. We offer backend SDKs in popular programming languages. If your backend system is programmed in any other language, you can use our REST-ful APIs to programmatically integrate with SuprSend notification engine. ### Postman collection You can try out SuprSend APIs from our [Postman Collection.](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) ### CLI Reference The SuprSend CLI provides a command-line interface for managing your SuprSend workspace, workflows, templates, and more. Install the CLI to streamline your development workflow and automate common tasks. If you are new to SuprSend, you can start by understanding the [Core Concepts](https://docs.suprsend.com/docs/templates) first. # SuprSend APIs on Postman Source: https://docs.suprsend.com/reference/postman-collection Test and Explore SuprSend APIs with Postman. Our [Postman collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) is a good way to get familiar with the SuprSend API. Here's how to get started. 1. Fork our [Postman Collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521) 2. Visit `Variables` tab on postman collection 3. From SuprSend's platform get the env variables like authorization headers, workspace key, etc and set it in the variables 4. Navigate through the collection and start using the SuprSend APIs # Remove Users from Draft List Source: https://docs.suprsend.com/reference/remove-subscribers-from-draft-list POST /v1/subscriber_list/{list_id}/version/{version_id}/subscriber/remove API to remove users from the draft list by passing its `version_id` returned in [Start Sync](/reference/start-sync) API response. # Remove Users from List Source: https://docs.suprsend.com/reference/remove-subscribers-from-list POST /v1/subscriber_list/{list_id}/subscriber/remove API to remove users from a list by passing their distinct_ids. # Overview Source: https://docs.suprsend.com/reference/replace-list-subscribers An overview of APIs to replace the entire user list. For scenarios where you need to replace the entire user list rather than just updating a subset, follow these steps using the provided APIs: * [Start Sync](/reference/start-sync): This API creates a draft version of the list where you can add or remove users. * Update Users: [Add](/reference/add-subscribers-to-draft-list) or [remove](/reference/remove-subscribers-from-draft-list) subscribers to/from this draft version as needed. * [Finish Sync](/reference/finish-sync): Call this API when you are done updating users in the draft version. The draft list will replace the current active list after calling this API. If you create a draft version by mistake and decide not to keep it, you can [delete the draft list](/reference/delete-draft-list). # Reset Source: https://docs.suprsend.com/reference/reset-user-preferences PATCH /v1/bulk/user/preference/reset/ API to reset user preference settings to default preference. # Authentication Source: https://docs.suprsend.com/reference/service-token-authentication Learn how to authenticate management API requests using service tokens. Management APIs in SuprSend use Service Token authentication instead of the usual API Key authentication used in other APIs. Service tokens are created at account level and are used to manage resources and assets (like workflows, templates, schemas, etc.) across workspaces. ## Generating Service Tokens Service tokens can be generated from your SuprSend dashboard: 1. Go to [SuprSend dashboard → Account Settings → Service Tokens](https://app.suprsend.com/en/account-settings/service-tokens) 2. `Generate a new Token` and provide a descriptive name which tells the purpose of the token. 3. Click on `Create and View`. Copy and store the token somewhere safe. For security purposes, token will only be shown at the time of generation. ## Authorizing Management APIs To authorize your API request, pass service token in the `Authorization` header as `ServiceToken XXXX`, where XXXX is the service token. **Example Header:** ```curl theme={"system"} Authorization: ServiceToken ``` # Start Sync Source: https://docs.suprsend.com/reference/start-sync POST /v1/subscriber_list/{list_id}/start_sync Creates an empty draft version of the list where you can update users to be replaced without affecting the live version. # Trigger Broadcast Source: https://docs.suprsend.com/reference/trigger-broadcast POST /{workspace_key}/broadcast API to trigger broadcast notification to list users. # Trigger an Event Source: https://docs.suprsend.com/reference/trigger-event POST /event/ API to pass an event, which in turn triggers workflows where that event is defined as the trigger. # Trigger Workflow Source: https://docs.suprsend.com/reference/trigger-workflow-api POST /trigger/ API to trigger multi-step workflow to one or more users/ objects by passing workflow slug. # Update Event Source: https://docs.suprsend.com/reference/update-event PATCH /v1/{workspace}/event/{url_encoded_event_name}/ Update event description and its linked schema. # Overall Channel level Source: https://docs.suprsend.com/reference/update-object-channel-preference PATCH /v1/object/{object_type}/{id}/preference/channel_preference/ API to update object preferences across all channels. # Update Tenant Default Preference Source: https://docs.suprsend.com/reference/update-tenant-default-preference PATCH /v1/tenant/{tenant_id}/category/{category_slug}/ API to update tenant default preferences for a specific notification category. # Category and Channel within Category Source: https://docs.suprsend.com/reference/update-user-category-preference PATCH /v1/user/{distinct_id}/preference/category/{category_slug}/ API to update user preferences for a specific notification category. # Overall Channel level Source: https://docs.suprsend.com/reference/update-user-channel-preference PATCH /v1/user/{distinct_id}/preference/channel_preference/ API to update user preferences across all channels. # Create/Update Schema Source: https://docs.suprsend.com/reference/upsert-schema POST /v1/{workspace}/schema/{schema_slug}/ Upset draft version of a JSON schema. Creates a new schema if it doesn't exist. # All Categories Source: https://docs.suprsend.com/reference/user-category-preference GET /v1/user/{distinct_id}/preference/category/ API to fetch user preferences across all categories.