# Product Updates Source: https://docs.suprsend.com/changelog/overview Track SuprSend product updates, new feature releases, platform improvements, and bug fixes shipped across the notification infrastructure each month. ## One namespace for tenant data — `$tenant` now works everywhere Until now, tenant properties had two accessors depending on where you referenced them: `$tenant.*` in workflow expressions, `$brand.*` inside templates and JSONNET. That split meant switching syntax mid-flow and remembering which side you were on. From today, **`$tenant.*` works everywhere** `$brand.*` did — Handlebars templates, JSONNET, workflow expressions, and the branch/wait/fetch/webhook nodes. The reserved keys have been simplified too: `brand_id` → `id` and `brand_name` → `name`. One namespace, shorter property names, across the whole platform. ```handlebars theme={"system"} {{! Before }} Hello from {{$brand.brand_name}}! {{! After }} Hello from {{$tenant.name}}! ``` Existing templates using `$brand.*` continue to render — the old accessor stays available so nothing breaks. Update at your own pace; new templates should use `$tenant.*`. See [Tenant Templates](/docs/tenant-templates) for the full variable reference. ## Send email broadcasts through Mailgun Broadcasts now support **Mailgun** as an email vendor, alongside the providers already available. See [Broadcast](/docs/broadcast) to send your first one. ## Web push is now a broadcast channel Announcing a product update or running a re-engagement campaign? You can now include **web push** in a broadcast alongside email, SMS, and mobile push — one broadcast, all channels, one report. Users on the target list receive the browser notification wherever they've opted in. See [Broadcast](/docs/broadcast) to configure a web push channel on your next broadcast. ## User Schema: Help Agent generate the right SQL for segment lists When the Segment Agent generates SQL from a prompt like *"enterprise users in Germany who never opened last week's campaign"*, it references your workspace's user schema to map plain English to the right property names (`plan`, `country`, `email_open` — not `tier`, `region`, `opens`). A new **Developers → Schemas → Users** page now lists every tracked user property with its inferred type and description. Descriptions are the strongest signal the Agent has when property names look alike (`plan` vs. `plan_tier`, `country` vs. `billing_country`) — edit any description inline to sharpen future SQL generations. The schema refreshes every **3 hours**, so newly tracked properties show up on the next cycle, and your description edits persist across refreshes. Developers → Schemas → Users page listing auto-generated user properties with editable descriptions Available on all plans. See [Segment Lists → Auto-generated user schema](/docs/segment-lists#auto-generated-user-schema). ## Flutter SDK v3 — per-tenant identities and JWT auth If you ship a Flutter app, v3 brings the two things that were missing to reach parity with the rest of the platform: * **Per-tenant user profiles.** If your business runs multiple product lines as separate apps — say a Bank app and a Cards app, each set up as its own tenant — the same customer can now install both, and each install keeps its own push token, email, and custom properties. A workflow triggered under the Bank tenant lands on the Bank install; a Cards workflow lands on Cards. No more cross-firing or token stomping between apps on the same device. See [User-Tenant Mapping](/docs/user-tenant-mapping). * **Short-lived JWT auth.** Swap long-lived workspace keys for JWTs signed by your backend and scoped to a `distinct_id` — and, optionally, a `tenant_id`. A stolen token can't be replayed against another user's profile (or another tenant's), and it expires on its own — no rotation drills. Upgrade in `pubspec.yaml`: ```yaml theme={"system"} dependencies: suprsend_flutter_sdk: ^3.0.0 ``` Upgrading from v2? Follow the [Flutter SDK v2 → v3 migration guide](/docs/flutter-migration-from-v2). For fresh installs, see [Flutter SDK integration](/docs/flutter-android-integration), or check the [SDK changelog](/docs/developer/versioning/sdk-changelog) for full release notes. ## Swift SDK v2.0.0 — per-tenant identities on iOS If you ship a native iOS app, v2.0.0 brings **user-tenant mapping** support to Swift. Register the same person against different tenants and each install keeps its own push token, email, and custom properties. Update your SPM or CocoaPods manifest to `2.0.0`. Existing v1 apps continue working; migrate at your own pace. See [User-Tenant Mapping](/docs/user-tenant-mapping) for the concept, [Swift SDK integration](/docs/ios-integration) for setup, or the [SDK changelog](/docs/developer/versioning/sdk-changelog) for full release notes. ## Same user, different channels and properties per tenant Say your business runs two product lines, each with its own app — and each app set up as its own tenant. The same customer installs both. Until now, a SuprSend user held a single push token, so an alert from one product could fire into the other product's app — or replace its token entirely. Now a user can hold a different push token, email, phone number, Slack connection — or any custom property — for each tenant they belong to. Trigger a workflow under a tenant, and SuprSend sends to that tenant's identity. Under the hood, each user has one shared profile — the channels and properties common across every tenant — with tenant-level overrides layered on top. At trigger time, SuprSend merges the two to resolve the final identity the notification sends to. A user's profile viewed in one tenant — the overridden email is highlighted, and a banner confirms changes apply only to that tenant Where it lands: * **Two apps, one customer.** Your Bank app and Cards app run as separate tenants, both installed by the same person — each install keeps its own push token, and a Bank send never reaches the Cards install. * **A dedicated Slack connection per tenant.** A support engineer who's a Slack Connect guest in each of your customers' workspaces — alerts about Customer A land in Customer A's Slack, and the access token stays scoped to that tenant. * **Same person, different role per tenant.** Admin in one customer's org, viewer in another. Templates and workflow branches read the role that belongs to the tenant being messaged. * **Strictly isolated customer accounts.** A B2B SaaS where each tenant is a distinct customer and users must never cross accounts. Exclusive mode enforces one user per tenant at the API — a leaked or reused ID can never reach data in another tenant. Two setup shapes to pick from: keep users **shared** across tenants if the same person legitimately belongs to more than one, or **exclusive** — one user, one tenant, enforced at the API — if your tenants are strictly isolated customer accounts. New workspaces default to exclusive; existing workspaces default to shared. Available on Enterprise, or as an add-on on Business. [Set it up →](/docs/user-tenant-mapping) ## 🌚 SuprSend now supports dark mode For the late-night coders and midnight-oil burners — the SuprSend platform now has a dark theme. Less glare, easier on the eyes, and a lot nicer to look at during a late workflow build or a debugging session that ran a little long. SuprSend dashboard in dark mode It follows your system setting by default, so it'll pick up your browser theme automatically. Prefer to keep it dark no matter what? Flip the theme toggle in your profile menu (top-right). ## Build user segments with SQL — or just describe them Want to re-engage every player who reached level 10 last week but never made an in-app purchase? Or reward the users who played more than 100 matches this month? **Segment Lists** let you build precise, reusable audiences by filtering on user properties and past event behavior. They're defined in SQL, so there's no filter-builder ceiling — thresholds, aggregates, absence of events, property-plus-behavior combinations — if a query can express it, you can segment on it. Building a Segment List with SQL Not a SQL person? You don't have to be. Just describe the segment to the SuprSend **Agent** in chat — *"shoppers who abandoned a cart over \$100 in the last 7 days"* — and it writes the query for you. The Agent generates queries from your user and event **schemas**, so keeping them updated is what turns a plain-English ask into a correct query on the first try. Segment Lists are in **beta** and available on all plans. Head to **Lists** in your dashboard to create your first one, or see the [Segment Lists docs](/docs/segment-lists). ## Preference opt-outs now skip workflow execution at trigger Category-level preference opt-outs are now evaluated **at trigger time**. If a user has opted out of the notification category linked to a workflow, the workflow run is skipped entirely — no nodes execute, no compute is consumed. ```mermaid theme={"system"} %%{init: {'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'ui-sans-serif, system-ui, sans-serif','lineColor':'#9a9a9a'},'flowchart':{'padding':14,'nodeSpacing':45,'rankSpacing':45}}}%% flowchart LR A["Workflow
triggered"] --> B{"Opted out of
category?"} B -->|Yes| C["Run skipped —
no nodes execute"] B -->|No| D["Workflow
runs"] --> E["Delivery node checks
channel preferences"] --> F["Notification
sent"] click B href "/docs/preference-evaluation#when-are-preferences-evaluated" "When preferences are evaluated" click E href "/docs/preference-evaluation#when-are-preferences-evaluated" "Channel-level checks at the delivery node" classDef s1 fill:transparent,stroke:#d9d9d9,stroke-width:1.4px; classDef s2 fill:transparent,stroke:#b0b0b0,stroke-width:1.4px; classDef s3 fill:transparent,stroke:#8a8a8a,stroke-width:1.4px; classDef s4 fill:transparent,stroke:#5f5f5f,stroke-width:1.4px; classDef dec fill:transparent,stroke:#d9d9d9,stroke-width:1.4px; class A s1; class B dec; class C,D s2; class E s3; class F s4; ``` * **Lower execution volume** → Opted-out users no longer consume workflow runs, reducing cost and noise for high-volume workflows. * **Cleaner logs** → Trigger-level skips are logged with a clear reason, keeping your execution history focused on users who are actually eligible for delivery. * **No changes required** → Applies automatically to all existing workflows. Your trigger payloads and integrations stay the same. **A couple of exceptions still check at the delivery node:** * **Workflows with side effects** — those containing [webhooks](/docs/webhook) or data update nodes ([user profile updates](/docs/update-user-profile), [list updates](/docs/add-user-to-list), [object subscription changes](/docs/subscribe-to-object)) keep the check at the delivery node so side effects still execute. Channel-level preferences are always evaluated there too, where template and user profile data is available. * **Test triggers** — testing a workflow with the **Test** button keeps the check at the delivery node, so you can run through the full workflow logic (including preference evaluation at each send step) without short-circuiting at the trigger. See [Preference Evaluation](/docs/preference-evaluation) for the full details.
## Let users choose how often they hear from you You can now add a **digest schedule** to any preference category. Users pick how they want it delivered — *Instant*, *Daily*, or *Weekly* — right from their preference center, and notifications are batched and sent on the cadence each user chose. Instead of unsubscribing from a noisy category, users can simply dial it down to a daily or weekly summary — you keep the channel, they keep control. And one workflow now serves every cadence, so you no longer build separate flows for instant, daily, and weekly sends. See [Configuring digest schedule](/docs/managing-notification-categories#configuring-digest-schedule) to set it up. ## Preferences that decide *when* a notification is sent Preference categories now support **conditions** — custom values like a minimum severity, an alert threshold, or the roles to notify — that you or your users can set per category and use inside your workflows. This makes preferences expressive instead of all-or-nothing: a user can ask to be alerted only when an error rate crosses the threshold they chose, or only at the severity they care about — and a tenant admin can decide which roles a category reaches. The condition is resolved at send time and used to route the workflow, with no extra setup on your side. See [Configuring condition properties](/docs/managing-notification-categories#configuring-condition-properties) to set it up. ## Message Previews: see exactly what your users received You can now view the fully rendered message — the exact content delivered to each recipient — right inside your SuprSend logs. End-to-end visibility, from trigger to inbox, without leaving the dashboard. **What it unlocks** * **Faster debugging and support** — When a user reports something off, pull up the exact message that went out instead of trying to reproduce it. The rendered output, personalization, and layout, exactly as the recipient saw it. * **Check personalization and language** — See the final message with all variables filled in, so you can confirm the right values (name, amount, currency) and the right language showed up for that specific user — instead of reading the raw template and guessing. * **Test the full flow without touching real inboxes** — Pair previews with test mode and block final delivery to run a notification end to end. Inspect the rendered message in your logs and validate templates, routing, and content before going live — without ever sending to a real user. * **Audit and compliance** — Keep a clear record of exactly what was communicated, to whom, and when. Ideal for regulated workflows that need proof of customer communications. This closes one of the last blind spots in sending notifications: knowing not just that a message was delivered, but what was delivered. Grab it for a [single message](/reference/get-message-content) by ID, or pull it inline across a [message list](/reference/list-messages) with `include_content=true`. ## Dynamic expiry for In-App Inbox In-App Inbox notifications now support **dynamic expiry**, so each notification can expire at a moment that's specific to it rather than a single duration baked into the template. Until now, expiry was the same for every recipient of a template. That doesn't fit notifications whose relevance depends on the data behind them — a flash sale that ends at a different time for each region, or a time-boxed task whose deadline depends on where each user is in their own journey. In cases like these, two users can receive the same notification yet have completely different windows in which it stays valid. With dynamic expiry, the expiry is computed per notification from your event or user data, so each one clears itself exactly when it stops being useful — even when that moment is different for every recipient — keeping inboxes relevant without manual cleanup. See [Expiry](/docs/in-app-inbox-template#expiry) for details. ## Swift SDK - v1.1.0 [Swift SDK `1.1.0`](https://github.com/suprsend/suprsend-swift-sdk/releases/tag/1.1.0) ships with a preferences revamp and a couple of platform improvements. * **Preferences revamp** → [`getPreferences`](/docs/ios-preferences#get-preferences-data) now accepts `locale`, `tags`, and `tenantId` for translated, tag-filtered, and tenant-level preference categories. * **User-agent header updates** → Refined client identification on outbound requests. * **Bundle ID tracking** → App bundle identifier is now captured alongside SDK events. ## Introducing SuprSend Agent Skills SuprSend Agent - Day 6 of AI launch week An open set of instructions that teach AI agents how to build on SuprSend correctly. Install in one command in Cursor, Claude Code, Codex, GitHub Copilot, or any agent that supports the Agent Skills open standard. ```bash theme={"system"} npx skills add suprsend/skills ``` Agents already know about SuprSend from training data, but they invent `node_type` values that don't exist, hallucinate CLI flags, and write field names that were never in the schema. Skills fix this by loading SuprSend-specific knowledge on demand, using the `agentskills.io` progressive-disclosure pattern so they don't bloat context. * **Workflow generation** → `suprsend-workflow-schema` carries the complete node reference, JSON schema, and examples, so the agent generates valid workflow JSON from a plain-English description. * **CLI and tooling** → `suprsend-cli` carries the full CLI command reference with agent-targeted tips, so pushes, syncs, and versioning run with real flags. * **Template and channel content** → `suprsend-template-schema` carries the variant envelope, multi-tenant and multi-lingual variants, Handlebars and JSONNET, and the per-channel schema for all nine channels. * **Docs and product knowledge** → `suprsend-docs-support` maps the SuprSend docs and LLM-friendly endpoints so the agent fetches live docs instead of stale training data. One knowledge layer across the whole SuprSend AI line - Agent, MCP, CLI, Slack Agent, and Agent Plugin. Published through `skills.sh` and passes the Gen Agent Trust Hub audit with zero alerts. 📘 [Agent Skills overview](/reference/agent-skills) · [Quickstart](/reference/agent-skill-quickstart) ## Introducing the SuprSend Agent Plugin SuprSend Agent - Day 4 of AI launch week One install gives your coding agent everything it needs to work with SuprSend. It bundles the SuprSend MCP server and our agent skills, so the agent can author workflows, templates, schemas, events, categories, and translations, and follow SuprSend primitives without manual configuration. The plugin format is shared across Claude Code, VS Code Copilot, and GitHub Copilot CLI, so you install it wherever you already build. The skills are the part that matters. They carry SuprSend's domain knowledge, the workflow node schema, the template variant schema across all nine channels, and the full CLI reference. The agent builds from your real schema instead of guessing at it. * **Integrate SuprSend into your product** → Add the SDK, drop in the in-app inbox or preference center, and set up event triggers on product actions, from a prompt in your codebase. * **Set up your account and test data** → Describe when a notification should fire and the agent builds the workflow, generates templates across channels, and configures a preference hierarchy from category defaults to tenant overrides to user choices. * **Query and debug** → Ask why user 583 never got a notification, how a workflow performed this week, or how many users opted out of a category. You get an answer, not a pile of raw logs. 📘 [Setup guide](/reference/agent-plugin) · [Read more about the plugin](https://github.com/suprsend/claude-code-plugin) ## Amazon S3 v2.0 connector — compression and path layout The [Amazon S3 v2.0 connector](/docs/amazon_s3_v2) now lets you control how Parquet files are written to your bucket. New connectors default to `snappy` + `per_type`, which works out of the box with Amazon Athena and every common warehouse. * **Compression** → Pick the Parquet codec. `snappy` (new default) is fast and broadly supported; `gzip`, `zstd`, `brotli`, `lz4`, and `none` are also available. See [Compression and path layout](/docs/amazon_s3_v2#compression-and-path-layout). * **Path layout** → Choose `per_type` (new default; one folder per data point — `messages/`, `workflow_executions/`, `requests/`) or `shared` (all data points share the same date-partitioned path). * **Query with Amazon Athena** → A new [Athena setup guide](/docs/athena_s3_query) walks through the database, external tables with partition projection on `year/month/day/hour`, and a sample query that traces one notification end to end. Connectors created before today still run on the previous defaults (`lz4` + `shared`). Reach out to [support@suprsend.com](mailto:support@suprsend.com) if you'd like to switch—Athena specifically needs a non-`lz4` codec and the `per_type` layout. ## Introducing the SuprSend Slack Agent SuprSend Agent - Day 4 of AI launch week SuprSend Slack Agent brings notification management and analysis right where you work. Tag `@SuprSend` or DM it, ask in plain language, and get an answer in the thread. Every request runs with the requester's own SuprSend role and tenant scope, so they get the same data access they have when using SuprSend directly. * **Manage your setup** → Ask how to model a use case, or check what's already configured. The Agent answers setup questions from SuprSend's docs and reads your live workspace to tell you exactly which tenants, workflows, categories, and event links exist right now. * **Analyze performance** → Analytics isn't limited to a fixed dashboard anymore. Ask the kind of question you'd normally hand to a data analyst - which workflow performed best last month, where errors are concentrated, how channels compare across tenants - and get a chart with a written read, right in the thread. * **Debug a delivery** → When a user reports a missing notification, ask the Agent why. It traces the user's channels, preferences, executions, and logs, then explains where it slowed down and the most likely reason. A root cause and a next step, not a pile of raw logs. * **Privacy Filter on by default** → A Slack channel is not a private space - messages are searchable and channel membership grows over time. When an answer would include PII, the Agent posts a clean summary in the channel and sends the sensitive details to the requester's DM. 📘 [Slack Agent overview](/reference/slack-agent) ## Introducing SuprSend Agent SuprSend Agent - Day 3 of AI launch week The SuprSend Agent is an AI agent inside the dashboard that builds, configures, and debugs your notification setup from simple prompts - connected to the full SuprSend API surface, product documentation, agent skills, and domain knowledge of how notifications actually work. * **Ask anything** → How to model your data, how a feature works, what an error means, what the platform best practices are - answered with context from SuprSend's documentation without opening another tab. * **Manage all assets** → Create, retrieve, update, and commit across tenants, objects, preferences, vendors, translations, and schemas, all from the same conversation. * **Design and test notifications** → A prompt like *"set up a trial expiry reminder for new users"* is enough. The Agent maps out workflow nodes, sets time delays, adds branching logic, picks channels, designs and personalizes templates with variables, and tests the result across every channel. * **Check analytics** → Reads your notification data through a semantic layer over the data lake, so you can ask anything, visualize as charts, and post results straight to Slack. * **Debug and inspect delivery** → Traces the delivery, finds the issue that caused the failure, and explains the cause and potential solution. Runs on your live workspace with safety built in - workspace-scoped sessions, permission asked before any edit, read-before-write on updates, and no destructive deletes exposed to the Agent. 📘 [Agent overview](/reference/agent) · [Read the launch post](https://www.suprsend.com/post/introducing-suprsend-agent) ## Introducing SuprSend CLI SuprSend CLI - Day 2 of AI launch week The SuprSend CLI brings the full SuprSend surface into your terminal - every core asset (workflows, templates, schemas, events, preference categories, translations) is callable through commands that pipe cleanly into shells, scripts, and CI/CD pipelines. ```bash theme={"system"} brew install --cask suprsend/tap/suprsend ``` * **Notifications as code** → Pull assets to readable JSON, edit in your IDE with linting and AI completions, push back with schema validation. Templates and workflows become first-class citizens in your repo, versioned through Git like the rest of your code. * **Environment promotion** → `suprsend sync --from staging --to production` moves committed assets between workspaces. No one needs UI access to production - changes ship only through reviewed PRs and CI. * **Type-safe payloads** → `suprsend schema generate-types` turns your schemas into native type definitions. Wrong payload keys fail in your IDE and your CI, not at send time. * **Incident-ready workflows** → `suprsend workflow disable` takes a misbehaving workflow offline from the same shell you're already debugging in; `enable` brings it back once the fix ships. * **MCP from the CLI** → `suprsend start-mcp-server` exposes the same operations to Claude, Cursor, Windsurf, and any other MCP client. Open source. Autocompletion for bash/zsh/fish/PowerShell, dry runs, profile management, and Cosign-verifiable binaries for security-sensitive setups. 📘 [CLI Overview](/reference/cli-intro) · [Installation](/reference/cli-installation) · [Quickstart](/reference/cli-quickstart) · [Changelog](/reference/cli-changelog) ## Introducing SuprSend MCP SuprSend MCP - Day 1 of AI launch week The SuprSend MCP server exposes full notification stack - workflows, templates, tenants, preferences, objects, delivery logs - as typed tools to Cursor, Claude, Windsurf, or any MCP client. One command to start, then prompt your way through the integration. ```bash theme={"system"} npx suprsend start-mcp-server ``` * **Integrate from your IDE** → Wire the SDK into your codebase, drop in the in-app inbox or preference center, add user identification, and migrate templates from external tools - all by prompt. * **Build workflows and seed test data** → Create multi-channel workflows, generate templates across email/SMS/push/in-app, configure preference hierarchies, seed users and tenants. * **Query and debug live state** → "User 583 says they never got the notification - show me their channels and preferences." Inspect workflow health, opt-outs, and errors without leaving your editor. * **Correct on the first run** → Skills load SuprSend domain context, schemas validate every call, and update tools read before they write so existing config is never overwritten. * **Production-safe** → Scoped service tokens, `--tools` flag for read-only environments, no bulk deletes through AI. 📘 [MCP Overview](/reference/mcp-overview) · [Cursor](/reference/mcp-quickstart-cursor) · [Claude](/reference/mcp-quickstart-claude) · [Windsurf](/reference/mcp-quickstart-windsurf) · [Tool list](/reference/mcp-tool-list) ## Python SDK - Messages API support [Python SDK `v0.18.2`](https://github.com/suprsend/suprsend-py-sdk/releases/tag/v0.18.2) adds native support for the [Messages API](/reference/list-messages) - fetch message logs and update statuses. * **[`supr_client.messages.list(...)`](/docs/python-messages#list-messages)** → Paginate messages in your workspace and filter by recipient, workflow, channel, status, tenant, category, or `created_at` range. * **[`supr_client.messages.bulk_update(...)`](/docs/python-messages#bulk-update-message-status)** → Update up to 1000 messages per call across `seen`, `clicked`, `dismissed`, `read`, `unread`, `archived`, and `unarchived` in a single request. Use it to: * **Keep your inbox in sync** → Mark messages `read` / `archived` when the user acts elsewhere (e.g. resolves a task from the dashboard), so the unread count stays accurate. * **Clean up stale notifications** → Archive alerts once the underlying event resolves (e.g. a scheduled downtime that's now over). * **Smooth rollout** → Backfill `read` / `archived` state for messages sent before your inbox went live, so users don't land on a wall of stale unreads. Upgrade with `pip install suprsend-py-sdk --upgrade`. See the full [Python Messages doc](/docs/python-messages) here. ## Messages API - query and update delivery state programmatically You can fetch all messages and update their status directly through API. Use this to change the status of Inbox messages or other channels. * **[List messages](/reference/list-messages)** → Paginate all messages in a workspace. Filter by recipient, workflow, channel, status, tenant, category, or time range. Build a "your notifications" page in your product, expose delivery history to support agents, debug "did this user get the email," export to your warehouse for analytics, or reconcile SuprSend state against your own DB. * **[Bulk update messages](/reference/bulk-update-messages)** → Update up to 1000 messages per call across `seen`, `clicked`, `dismissed`, `read`, `unread`, `archived`, and `unarchived`. Sync engagement from your own inbox UI - mark everything visible as `seen` on inbox open, wire "Mark all as read" or swipe-to-archive without per-message round-trips, or backfill engagement state when migrating from another notification system. ## CLI & MCP Server - Zero-install via `npx suprsend` The SuprSend CLI is now published to npm. You can run any command - including the MCP server - directly with `npx`, without a separate install step. Every invocation uses the latest published release. ```bash theme={"system"} npx suprsend --help npx suprsend workflow list npx suprsend start-mcp-server ``` **Updated MCP configuration** for Cursor, Claude Desktop, and Windsurf: ```json theme={"system"} { "mcpServers": { "suprsend": { "command": "", "args": ["-y", "suprsend", "start-mcp-server"], "env": { "SUPRSEND_SERVICE_TOKEN": "your_token_here" } } } } ``` Homebrew, binary, and source-build installs continue to work as before - pick whichever suits your environment. 📘 [CLI Quickstart](/reference/cli-quickstart) · [MCP Quickstart](/reference/mcp-quickstart) ## Templates 2.0 - variants, redesigned editor, one template for every channel Templates 2.0 * **[Variants](/docs/template-variants)** → Send different content to different users from the same template. Add conditions on [tenant](/docs/tenants), language, plan, or any trigger data - SuprSend picks the right version at send time. White-label for 50 tenants without 50 templates. [Localise](/docs/multi-lingual-template) into 10 languages without 10 copies. A/B test copy without touching your codebase. * **Variant ordering** → Variants are evaluated top to bottom. First match wins, default is always the fallback. Drag to reorder priorities. [Test the full template group](/docs/templates#test) to verify the right variant fires for the right user. * **Redesigned [Variables panel](/docs/templates#the-variables-panel)** → Payload, [recipient](/docs/users), [tenant](/docs/tenants), and actor data in one sidebar. Auto-suggestions as you type, live preview rendering, missing values flagged in red - catch issues before they hit production. * **Draft-safe editing** → Edits never touch live traffic until you [commit](/docs/templates#commit). Full version history with one-click [rollback](/docs/templates#version-history). Import content from other templates, [clone across workspaces](/docs/templates#clone-and-promote), disable channels without deleting. * **[Channel editors](/docs/templates#write-your-content) rebuilt** → Consistent authoring flow across all 9 channels. [Email](/docs/email-template) gets designer + HTML + plain text, tenant branding blocks, display conditions, merge tags for arrays, and email markup for Gmail rich cards. * **[WhatsApp](/docs/whatsapp-template) and [SMS (DLT)](/docs/sms-template#sms-in-india-dlt) approval** → Commit puts the version into Approval Pending. SuprSend handles vendor submission automatically. Dedicated [DLT flow](/docs/dlt-guidelines) for India with separate fields and approval steps. * **[Template API v2](/reference/list-templates-v2) + [CLI](/reference/cli-template-overview)** → Full Management API and CLI commands (`pull`, `push`, `commit`) for programmatic management and CI/CD pipelines. ## SDK Package Signing — Know exactly what you're installing Python and Java SDK releases now ship with a SHA-256 checksum and a Cosign cryptographic signature. Before installing, one command tells you the package is exactly what SuprSend built — unmodified, verified, safe to ship to production. Useful if your team has supply chain security requirements, SOC 2 or ISO 27001 audit needs, or automated dependency checks in CI. Available from **Python SDK `v0.18.1`** and **Java SDK `v0.13.1`** onwards. No changes to how you install or use the SDK. * **Python SDK** → [Verify package signature](/docs/python-verify-signature) * **Java SDK** → [Verify package signature](/docs/java-verify-signature) ## Track SuprSend metrics in your own monitoring tool: New Relic, Datadog, and OpenTelemetry We're releasing the OpenTelemetry (OTEL) Connector along with native integrations for Datadog and New Relic - so you can monitor your SuprSend notification activity right alongside the rest of your application logs. SuprSend streams `suprsend.*` metrics into Datadog, New Relic, or any OpenTelemetry-compatible platform - under 1 minute of latency. Three metric groups are exported - **API requests**, **Workflow executions**, and **Messages**. All metrics are tagged by workspace, tenant, workflow, channel, vendor, and template. **What you can do with this:** * **Track API health**, workflow execution, delivery, and engagement right next to your existing application metrics. No separate tool, no tab-switching. * **Set up alerts** on delivery drops, API error spikes, workflow failures, or vendor degradation using the escalation policies your team already has. * **Correlate notification failures** with deploys, config changes, or ongoing incidents in the same timeline you're already watching. Both connectors ship with a pre-built dashboard - import the JSON and you're set. Observability Connectors Available under [**Connectors**](https://app.suprsend.com/connectors). Requires the **Enterprise plan**. 📘 [Datadog](/docs/datadog) · [New Relic](/docs/new-relic) · [OpenTelemetry](/docs/opentelemetry) ## Agent Skills — Install SuprSend domain knowledge into your AI coding agent Your AI coding agent is confident. It's also frequently wrong about SuprSend field names, CLI flags, and workflow schema structure — because it's guessing from training data, not reading the actual spec. Agent Skills Agent Skills fix that. They are Markdown knowledge packages that live inside your agent — Cursor, Claude Code, GitHub Copilot, Amp, Cline, and others. At startup, skill names and descriptions load so the agent knows what's available. When you describe a SuprSend task, the full `SKILL.md` loads into context. Deep reference files — schema guides, node tables — load only when needed, keeping context usage efficient. **Three skills are available today:** * **`suprsend-workflow-schema`** → Generate valid workflow JSON from plain English. Full schema of all workflow nodes included — the agent builds it right on the first pass. * **`suprsend-cli`** → Every CLI command and flag. Push workflows, sync workspaces, generate types — no more guessing at flags or running commands twice. * **`suprsend-docs-support`** → LLM-optimized doc endpoints your agent can fetch directly at runtime, so answers come from the source. **Supported agents:** Cursor, Claude Code, GitHub Copilot, Amp, Cline, Warp, Continue, Augment, and any agent following the [agentskills.io](https://agentskills.io/specification) progressive disclosure spec. **Security:** All skills pass the Gen Agent Trust Hub audit with zero Socket alerts. Full report at [skills.sh/suprsend/skills](https://skills.sh/suprsend/skills). Skills run with full agent permissions — review contents before deploying in production. 📘 [Install Agent Skills](/reference/agent-skills) · [Quickstart](/reference/agent-skill-quickstart) ## CLI & SDK Updates * **CLI [`0.2.21`](https://github.com/suprsend/cli/releases)** → Cleaner command descriptions and flags, fetch and save a single asset without a full SuprSend directory setup, and a type generation fix. * **CLI [`0.2.18`](https://github.com/suprsend/cli/releases/tag/0.2.18)** → Automatic `SKILLS.md` generation so your project scaffolding stays complete out of the box. * **Node SDK** → Fixed a corrupt `package-lock.json` that was quietly breaking fresh installs. * **Skills & SuprSend Agent Plugin** → Refreshed with the latest improvements. ## CLI - Reusable Skills, Workflow MCP Tool The [SuprSend CLI](https://github.com/suprsend/cli) just got meaningfully more capable: * **CLI Skills** → Define reusable skills once, invoke them anywhere. Less repetition, more consistency across your workflows. * **Workflow List MCP Tool** → Query and browse your workflows directly from your AI assistant. No tab-switching, no copy-pasting IDs. ## Agent - Read-Only Tools Agent can now read your SuprSend data - users, preferences, subscriptions, workflows, translations, objects and more. Ask questions in plain language and get answers instantly, without digging through dashboards or hitting the API yourself. Try asking: * *"What channels and preferences does this user have set?"* * *"See if this user has this property set"* * *"Is this user subscribed to marketing emails?"* * *"Is this user part of marketing list?"* * *"What is the default preference set for xyz tenant?"* * "Do I have french translation file available?" * *"Does this workflow have a fallback set up?"* ## Fixes & Improvements * **Python SDK 0.17.0** → `suprsend-py-sdk==0.17.0` is now available. * **Filter state bug** → Fixed a cross-filter state issue in `suprsend-web-sdk`, `suprsend-react-core`, and `suprsend-react`. * **WhatsApp Vendor Forms** → Added WABA ID field to streamline vendor setup. * **Inbox SDK** → New option to disable automatic seen tracking - you decide when notifications get marked as read. * **Keyboard navigation** → `Cmd + Click` now opens items in a new tab. * **AI Orb placement** → Fixed a positioning issue with the AI assistant orb in the dashboard. * **Dependency security** → Resolved critical Dependabot vulnerabilities in `suprsend-app-new`, `suprsend-node-sdk`, and `suprsend-rn-sdk`. ## CLI - All Binaries Are Now Signed and Notarized From [`v0.2.19`](https://github.com/suprsend/suprsend-cli/releases/tag/v0.2.19) onwards, every CLI release is signed with [Cosign](https://docs.sigstore.dev/cosign/overview/). Cryptographically verify the binary before installing - one command, zero guesswork. 📘 [Verify your binary →](https://docs.suprsend.com/reference/cli-verify-signature) ## Agent - Instant Answers, Inside SuprSend Agent is now live inside the SuprSend dashboard. Ask anything about the product - integrations, workflow setup, errors, modeling - and get a precise answer without opening docs or waiting on support. Agent * *"Walk me through adding Inbox to a React app."* * *"How do I set up a payment reminder with escalation?"* * *"What does `SUBSCRIBER_NOT_FOUND` mean?"* * *"How should I structure notifications for a multi-tenant product?"* Trained on the full SuprSend documentation today. Next: an Agent that acts - creating workflows, configuring templates, managing subscribers, all from a conversation. ## Analytics - Export to CSV **Export notification analytics as CSV** to build internal dashboards, share reports with stakeholders, or run custom analysis. **Use cases:**
* **Internal reporting:** Share delivery and engagement metrics with product, ops, or leadership in their preferred tools * **Detailed analysis:** Drill into user-level data in Excel, Google Sheets, or your data warehouse to drilldown into trends and identify patterns. **How it works:**
**One-click export:** Click on the download button on any data point, choose type of export (aggregate or raw), apply filters and click export. You'll get an email or In-app notification as soon as the export is ready. Export Analytics Data as CSV 📘 Learn more in the [Analytics documentation](/docs/analytics#exporting-data).
## Workflow - Branching on Message Status You can now branch workflows based on message status, **enabling routing decisions using actual delivery and engagement signals**. This is especially useful for reminder and escalation workflows-for example, sending a follow-up notification only if the initial message was not seen. Workflow - Branching on Message Status 📘 Learn more in the [Branch documentation](/docs/branch#condition-on-message-status). ## Hosted Preference Page - Modern Design with Multi-Language Support The hosted preference page has been updated with a refreshed UI and locale-aware localization support. Static UI content (CTAs, labels, system text) is translated automatically using built-in i18n support for up to 23 languages. Dynamic content, including preference category names and descriptions, is rendered using the translation files configured by you, based on the user’s locale. Hosted Preference Page 📘 Checkout [hosted preference page documentation](/docs/user-preferences#hosted-preference-page) and see how [translations work](/docs/user-preferences#translating-preference-categories-in-user’s-locale) ## Category Translations for Preference Centers Reach users worldwide with **category translations** - show your preference centers in your users' native language. Whether your users speak Spanish, French, German, or any other language, they'll see category names and descriptions in their preferred locale, making it easier for them to understand and manage their notification preferences. **What you get:** * **Multi-language support**: Upload translations via Dashboard, API, or CLI - choose the method that works best for your workflow * **Smart fallbacks**: If a translation isn't available for a specific locale (e.g., `es-AR`), we automatically try the base locale (`es`), then fall back to English - your users always see something meaningful * **Zero maintenance**: English translations are automatically generated from your category names and descriptions, so you don't need to manage them separately Category Translations for Preference Centers 📘 Learn more in the [category translations documentation](/docs/notification-category#category-translations). ## 📚 S3 Connector v2.0 - Comprehensive Notification Data Export S3 Connector v2.0 exports end-to-end notification data to your S3 bucket, giving you full visibility into requests, workflows, and message delivery for analytics, debugging, and compliance. It replaces the limited v1.0 connector with complete, structured logging. S3 Connector v1.0 will be deprecated over time. Migrate to v2.0 to access full logs and notification analytics. ### What's New We've added 3 data points for **end-to-end traceability** of notifications from **API request → workflow execution → final delivery**: * **Messages:** Delivery status, engagement metrics, vendor responses, and failures * **Workflow Executions:** Step-by-step workflow logs for debugging conditions, preferences, and errors * **Requests:** API payloads and responses for trigger-level debugging and audit trails ### Use Cases * Internal analytics or customer-facing analytics * Debug delivery and workflow issues using detailed logs or show logs on your customer portal * Maintain audit trails for compliance and internal reporting * Query and analyze notification data you fully own
📘 Check out the [S3 Connector v2.0 documentation](/docs/amazon_s3_v2) for more details.
## Channel-Level Control for Preference Categories Choose which channels users are opted into by default when setting up preference categories. You can use this to have preference category defaults as user gets in-app notification by default and other channels will be sent only if user explicitly opts in to them. 📘 Learn more in the [preference categories documentation](/docs/notification-category). Channel-Level Control for Preference Categories ## Type-Safe Workflow Triggers Catch payload errors at compile time and get IDE autocomplete for workflow payloads and event properties using generated type definitions. Define your payload structure once using SuprSend [JSON schemas](/docs/validate-workflow-payload), and automatically generate type definitions using SuprSend [CLI](/reference/cli-schema-generate-types). ### What’s Included and Why This Matters * Prevents production bugs caused by invalid payloads * Keeps backend code and notification schemas in sync * Get IDE autocomplete, inline validation, and type hints for payload fields * Supported languages: TypeScript, Python, Go, Java, Kotlin, Swift, Dart 📘 Learn more in the [type safety & type generation documentation](/docs/type-generation). ## 🌍 Translations - One template, all languages, zero hassle Go global with **translations** - the easiest way to localize your notifications. One template, multiple languages, automatic fallbacks. No more maintaining separate templates for each language. ### What You Can Do **Localize notifications instantly:** * **Smart translation keys** → Use `{{t "key"}}` syntax in templates and let SuprSend handle the rest * **Automatic fallbacks** → Users always get a translation, even if their exact locale isn't available * **Dynamic content** → Pass variables like `{{t "key" name=user.first_name}}` for personalized content * **Pluralization** → Automatic handling of singular/plural forms based on count **Manage translations like code:** * **Upload, download, edit** → Work with translation files locally or in the dashboard * **Version control** → Complete history tracking with one-click rollbacks * **CLI & API support** → Manage translations programmatically or via command line **Built for developers:** * **Namespaced keys** → `{{t "feature:key"}}` to avoid conflicts across features * **JSONNET support** → Complex conditional logic for advanced use cases * **Handlebars integration** → Combine with other helpers for dynamic content * **Version control for translations** → Track changes, maintain history, and roll back when needed *** 📘 Check out the [translation documentation](/docs/translations) to get started. ## 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 trigger 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 communications 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 preference 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 preference 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#merge-tags-dynamic-lists). * Added support for handlebars helper in [display condition](/docs/email-template#display-conditions). ## 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](/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 Step by step guide to integrate your ACL Sinch SMS provider account with SuprSend, configure credentials, and start sending SMS notifications to users. 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" preference 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*) *for example 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 Use the add user to list workflow node to dynamically add a recipient or actor to a SuprSend subscriber list based on real time events and triggers. 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. for example, 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 Connect your AWS Amazon SES account with SuprSend to send transactional emails, including IAM setup, region selection, and sender domain configuration steps. ## 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 (for example `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 (for example `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 (for example `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 (for example 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. For example - *AWS SES \[Production]* | | From Email | Default 'From Email ID' that email will go from. You can override this in the individual template.\*for example [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.*for example SuprSend* | | Reply Address | Default 'Reply To Email id' on which replies are received. You can override this in the individual template.\*for example [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#creating-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#creating-iam-user) to create a new IAM user and generate access key. | | Configuration Set | Configuration-set is used to track email events (for example send, delivery, open, click, bounce, complaint etc.). [Refer Step](/docs/amazon-ses#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#manage-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. Successful subscription: ## 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 Export SuprSend notification data and message templates to your Amazon S3 bucket as parquet and JSON files for warehousing, analytics, and long term storage. ## 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. *** # Overview Source: https://docs.suprsend.com/docs/amazon_s3_v2 Use the Amazon S3 v2 connector to export notification logs, delivery events, and analytics from SuprSend to S3 for debugging, reporting, and compliance audits. **On the old S3 connector (v1.0)?** v1.0 will be deprecated over time. v2.0 exports all log types and notification analytics and closes gaps in error logging that exist in v1.0—see the [v1.0 doc](/docs/amazon_s3) for the migration path. Export your SuprSend notification data directly to your S3 bucket. Build custom analytics dashboards, debug delivery issues, surface errors to your customers, or maintain compliance audit trails—all with data you fully own and control. *** ## How it works SuprSend syncs your notification data to S3 every **5 minutes** as Parquet files, partitioned by hour. By default, each data point goes into its own top-level folder (the `per_type` path layout)—see [Compression and path layout](#compression-and-path-layout) for the exact folder structure and how to change it. A few details worth knowing: * **Updates rewrite files in place.** Each sync re-writes the hourly Parquet files whose data has changed. Query engines like Athena, Spark, and Presto see the new state automatically. For warehouses that don't overwrite rows (e.g., BigQuery), use the `updated_at` column to pick the latest version. * **Parquet is read natively** by every common query engine and warehouse. For Athena specifically, see [Query with Athena](/docs/athena_s3_query). * **Data is encrypted in transit** (TLS 1.2+) and **at rest** (SSE-S3 or SSE-KMS). * **If you pause the sync, the pause period backfills automatically** when you resume. *** ## What you can export You can pick which data points to sync based on what you're trying to do. Most teams start with **Messages** (analytics and delivery troubleshooting) and add **Workflow Executions** and **Requests** when they need to debug end-to-end. Each data point maps to its own table in your warehouse or query engine. ### Data points | Data point | What's in it | Use it for | | ----------------------- | -------------------------------------------------- | --------------------------------------------------------------------- | | **Messages** | Delivery status, engagement, vendor info, failures | Analytics, delivery troubleshooting | | **Workflow Executions** | Step-by-step workflow logs | Debugging workflow level errors or computations like user preferences | | **Requests** | API payloads and their responses | API debugging, Audit trails, Workflow Trigger level errors | You'll choose which data points to export when you create the connector—open the SuprSend dashboard at [app.suprsend.com](https://app.suprsend.com) and go to **Connectors** → **Add connector** → **Amazon S3 v2.0** to see the selection panel: Data Points Parameter on SuprSend Platform **Errors logged in each table:** | Table | Errors | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Requests | API level errors, workflow trigger level errors (condition mismatch, user not found, etc.) | | Workflow Executions | Workflow level errors (dynamic variables in workflow could not be resolved, template rendering failed, webhook returned a 404 response, etc.) | | Messages | Delivery failures | ### Table schema Columns and types below match the Parquet files SuprSend writes to S3. Several semantically-typed fields (JSON, boolean, integer, timestamp) are stored as `string` in Parquet—they're called out in the description so you can cast them in your queries. | Column name | Description | Datatype | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | | workspace\_key | SuprSend workspace identifier | string | | created\_at | Time when the request was received by SuprSend (UTC) | timestamp | | updated\_at | Time when this entry was last updated | timestamp | | api\_type | Entity type for the API call | string | | api\_name | Workflow, event, or broadcast name passed in the API call | string | | wf\_trigger\_type | Workflow trigger type | string | | distinct\_id\_list | List of user `distinct_id` values, or `object_type/id` for object recipients | `array` | | actor | Actor passed in the event or workflow API request | string | | tenant\_id | Tenant ID for which the API request was sent | string | | payload | Input payload passed in the trigger, including API call details (JSON serialized as string) | string | | response | HTTP API response (JSON serialized as string) | string | | metadata | SDK, machine, and location information for the request (JSON serialized as string) | string | | errors | Request-level errors. Each element has `error_code`, `error_description`, `error_type`, `severity`, `workflow_slug` | `array` | | executions | Workflow or broadcast executions triggered by this API call. Each element has `distinct_id`, `exec_id`, `workflow_slug`. `exec_id` joins to `workflow_executions.execution_id` | `array` | | idempotency\_key | Idempotency key passed in the API request. A UUID is generated if not provided | string | | status | Status of the API request | string | **status** can have these values: * `completed`: request is successfully processed. * `failure`: request failed to process due to some error. * `partial_failure`: request has been partially processed with some failure or has an acceptable warning (like workflow conditions evaluated to false). | Column name | Description | Datatype | | ----------------------------- | ---------------------------------------------------------------------------------- | --------- | | workspace\_key | SuprSend workspace identifier | string | | created\_at | Time when the workflow execution started | timestamp | | updated\_at | Time when the workflow step log was last updated | timestamp | | execution\_id | Unique identifier for a workflow execution | string | | recipient\_distinct\_id | User `distinct_id`; for objects, `object_type/id` | string | | tenant\_id | Unique identifier of the tenant | string | | idempotency\_key | Idempotency key passed in the API request. A UUID is generated if not provided | string | | parent\_object\_execution\_id | Execution ID of the parent object when triggered on an object | string | | parent\_object | Parent `object_type/id` when the workflow runs for subscribers | string | | workflow\_slug | Unique slug of the workflow | string | | workflow\_version | Version of the workflow | string | | node\_id | Unique identifier of the node | string | | node\_name | Name of the node | string | | node\_type | Type of the node | string | | execution\_stage | Current execution stage of the node | string | | status | Log status of the step (`error`, `warning`, `info`) | string | | message | Short description of the event or error at this stage | string | | properties | Additional input or output data for the node execution (JSON serialized as string) | string | | Column name | Description | Datatype | | ----------------------------- | ---------------------------------------------------------------------------------------- | --------- | | workspace\_key | SuprSend workspace identifier | string | | created\_at | Time when this entry was created | timestamp | | updated\_at | Time when the message status was last updated | timestamp | | wf\_execution\_id | Workflow execution this message belongs to. Joins to `workflow_executions.execution_id` | string | | broadcast\_execution\_id | Broadcast execution this message belongs to | string | | message\_id | Message identifier; present only when there is no execution error | string | | recipient\_distinct\_id | User `distinct_id`; for objects, `object_type/id` | string | | tenant\_id | Unique identifier of the tenant | string | | idempotency\_key | Idempotency key passed in the API request. A UUID is generated if not provided | string | | parent\_object | Parent `object_type/id` when the workflow runs for subscribers | string | | parent\_object\_execution\_id | Execution ID of the parent object when triggered on an object | string | | workflow\_slug | Unique slug of the workflow | string | | template\_name | Name of the template group | string | | template\_slug | Unique slug of the template | string | | message\_status | Delivery status of the message | string | | message\_triggered\_at | Timestamp when SuprSend sent the message request to the vendor | timestamp | | message\_delivered\_at | Timestamp when the vendor reported delivery | timestamp | | message\_seen\_at | Timestamp when the message was seen by the user | timestamp | | message\_clicked\_at | Timestamp when the message was clicked by the user | timestamp | | node\_id | Unique identifier of the node | string | | node\_name | Name of the node | string | | node\_type | Type of the node | string | | execution\_failure\_reason | Workflow-execution-level failure details with severity (JSON serialized as string) | string | | delivery\_failure\_reason | Failure reason returned by the vendor | string | | note | Additional note attached to the message | string | | message\_id\_by\_vendor | Vendor-generated identifier for this message | string | | vendor\_fallback\_applicable | Whether vendor fallback was enabled (`true` / `false` as string) | string | | vendor\_fallback\_level | Order in which this vendor was used during fallback, starting at `0` (integer as string) | string | | vendor\_nickname | Vendor nickname configured in SuprSend | string | | vendor\_slug | Vendor identifier combining vendor type and channel | string | | is\_smart | Whether the node used smart channel routing (`true` / `false` as string) | string | | success\_metric | Success metric defined for smart channel routing | string | | success\_achieved\_at | Timestamp when the success metric was achieved (ISO timestamp as string) | string | | wait\_time\_in\_seconds | Wait time between channels for smart routing, in seconds (integer as string) | string | | channel\_slug | Communication channel | string | | channel\_value | Channel-specific value (for example, email address) | string | | webhook\_data | Request and response payload for webhook nodes (JSON serialized as string) | string | ### Linking different data points These data points are linked, so you can trace a notification from the initial API request all the way to final delivery. The `idempotency_key` is shared across all three tables and is the easiest way to follow a single request end to end—it's also the value you can pass in your API call and store on your side to correlate SuprSend logs with your internal ones. ```mermaid theme={"system"} graph LR A[Requests] -->|executions[].exec_id → execution_id| B[Workflow Executions] B -->|execution_id → wf_execution_id| C[Messages] A -->|idempotency_key| C style A fill:#60a5fa,stroke:#93c5fd,color:#000000 style B fill:#fbbf24,stroke:#fcd34d,color:#000000 style C fill:#34d399,stroke:#6ee7b7,color:#000000 ``` | From → To | Join on | | ------------------------------ | ------------------------------------------------------------------------ | | Requests → Workflow Executions | `UNNEST(requests.executions).exec_id = workflow_executions.execution_id` | | Workflow Executions → Messages | `workflow_executions.execution_id = messages.wf_execution_id` | | Requests → Messages (shortcut) | `requests.idempotency_key = messages.idempotency_key` | ## Compression and path layout You can configure how Parquet files are written when you add or edit the connector. The defaults work for almost every use case—change them only if you have a specific need. **Connectors created before 21 May 2026** still use the previous defaults—**`lz4` compression** and the **`shared` path layout**. New connectors default to `snappy` + `per_type`. Changing either setting on an existing connector only affects files written **after** the change; everything already in your bucket keeps its original codec and folder. If you're on the old defaults and want to switch, write to [support@suprsend.com](mailto:support@suprsend.com)—we'll help you migrate and rewrite the historical files if you need them in the new layout. ### Compression Parquet files are written with **`snappy`** by default, which works well for almost every query engine and warehouse. You can switch the codec to `lz4`, `gzip`, `zstd`, `brotli`, or `none` if you have a specific need. Don't use `lz4` if you plan to query the data with Amazon Athena—Athena can't read `lz4`-compressed Parquet reliably (a known Athena limitation, not specific to SuprSend). ### Path layout Two layouts are available for how files are organized inside your bucket. Both use Hive-style date partitions (`year=YYYY/month=MM/day=DD/hour=HH`). Each data point gets its own top-level folder, then date-partitioned subfolders: ``` your-bucket/ ├── messages/year=2025/month=01/day=15/hour=14/messages.parquet ├── workflow_executions/year=2025/month=01/day=15/hour=14/workflow_executions.parquet └── requests/year=2025/month=01/day=15/hour=14/requests.parquet ``` **Recommended for every query engine and warehouse.** Each data point lives in its own prefix, so tables, schemas, and IAM scopes stay cleanly separated—a one-to-one mapping between table and folder. **Required if you're using Amazon Athena**—see [Query with Athena](/docs/athena_s3_query). All data points share the same date-partitioned path, distinguished only by file name: ``` your-bucket/ ├── year=2025/month=01/day=15/hour=14/messages.parquet ├── year=2025/month=01/day=15/hour=14/workflow_executions.parquet └── year=2025/month=01/day=15/hour=14/requests.parquet ``` Works with BigQuery and DuckDB if you already consume data from a single date-partitioned prefix. **Not compatible with Amazon Athena.** ## Setup ### Step 1: Create your S3 bucket Open [AWS S3 Console](https://s3.console.aws.amazon.com/) and create a bucket with these settings: * **Bucket name**: Something like `suprsend-logs-production` (save this—you'll need it) * **Region**: Pick one close to you * **Block all public access**: Yes * **Encryption**: SSE-S3 (or SSE-KMS for compliance) ### Step 2: Create an IAM policy This gives SuprSend permission to write to your bucket. In [IAM Console](https://console.aws.amazon.com/iam/), create a policy with this JSON: ```json theme={"system"} { "Version": "2012-10-17", "Statement": [{ "Sid": "SuprSendS3ExportAccess", "Effect": "Allow", "Action": ["s3:PutObject", "s3:ListBucket", "s3:GetObject"], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME/*", "arn:aws:s3:::YOUR_BUCKET_NAME" ] }] } ``` Replace `YOUR_BUCKET_NAME` with your actual bucket name. Save it as something like `suprsend_s3_policy`. ### Step 3: Set up authentication Two authentication methods are available: | Method | When to use | We recommend | | ------------ | -------------------------------------------- | ---------------------------------------------------------------------------- | | **IAM Role** | Production, enterprise, multi-account setups | ✅ Yes—credentials rotate automatically, no secrets to manage | | **IAM User** | Development, testing, quick POCs | Only if IAM Role isn't feasible. Requires manual key rotation every 90 days. | **Use IAM Role when:** * Running in production environments * Security compliance requires no long-lived credentials * You have multi-account AWS setups * You want zero credential management overhead **Steps to create IAM Role:** 1. In IAM Console → **Roles** → **Create Role** 2. Select **Another AWS Account** and enter SuprSend's account ID: `924219879248` 3. Attach the policy you just created 4. Name it (for example, `suprsend_s3_role`) Now configure the trust relationship. Generate an External ID at [uuidgenerator.net](https://www.uuidgenerator.net/), then update the trust policy: ```json theme={"system"} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::924219879248:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "YOUR_EXTERNAL_ID" }} }] } ``` **Save these for the next step:** Role ARN + External ID (case-sensitive, no extra spaces) **Use IAM User when:** * Setting up for development or testing * Quick proof-of-concept needed * IAM Role setup isn't feasible in your environment IAM User credentials require manual rotation every 90 days for security compliance. **Steps to create IAM User:** 1. In IAM Console → **Users** → **Add users** 2. Name it (for example, `suprsend-s3-connector`) 3. Attach your policy 4. Go to **Security credentials** → **Create access key** → **Third party service** **Save immediately:** Access Key ID + Secret Access Key to add in the next step (AWS won't show the secret again) ### Step 4: Connect in SuprSend In the SuprSend dashboard ([app.suprsend.com](https://app.suprsend.com)), go to **Connectors** → **Add connector** → **Amazon S3 v2.0**. S3 Connector IAM Role Configuration S3 Connector IAM User Configuration Enter your AWS credentials, select which data points to export, then **Save** and toggle **Enable sync**. ### Step 5: Verify it's working Give it about 10 minutes, then check your S3 bucket. With the default `per_type` layout, you should see one folder per data point you enabled (`messages/`, `workflow_executions/`, `requests/`), each containing hourly partitions like `year=2025/month=01/day=15/hour=14/...`. Nothing showing up? Jump to [FAQs](#faqs) for troubleshooting steps. ### Step 6: Query the data with Athena (optional) If you want to run SQL on the exported data, [Amazon Athena](https://docs.aws.amazon.com/athena/latest/ug/what-is.html) reads the Parquet files in place—no warehouse to provision, no ingestion pipeline. The flow is: 1. **Set the Athena query result location** in the same region as your S3 bucket. 2. **Create a database**, e.g., `suprsend_db`. 3. **Register one external table per data point** (`ss_requests`, `ss_workflow_executions`, `ss_messages`) using partition projection on `year/month/day/hour`, so new hourly partitions show up automatically. 4. **Query**, always filtering on the partition columns to keep cost down. For the full DDL, partition-projection settings, and example queries (including a join that traces a single request across all three tables), follow the [Query with Athena](/docs/athena_s3_query) guide. *** ## Best practices * **Use an IAM Role in production**—credentials rotate automatically, with no long-lived secrets to manage. * **Use an External ID on the role's trust policy** to prevent "confused deputy" attacks. * If you must use an IAM User, **rotate the access keys every 90 days** and never commit them to git. * **Assign policies to IAM groups rather than individual users** to keep permissions easy to manage. * **Keep the bucket private**: block all public access and enable encryption (SSE-S3 or SSE-KMS). * **Only sync the data points you need.** Messages alone covers analytics and delivery troubleshooting; add Workflow Executions and Requests when you need to debug end-to-end. * **Filter on the partition columns first.** Engines like Athena scan only the matching `year`/`month`/`day`/`hour` folders, which keeps queries fast and cheap. * **Use `updated_at` for incremental jobs.** Pulling only rows where `updated_at` is newer than your last checkpoint avoids re-scanning history. * Files accumulate over time. Set up an S3 Lifecycle rule to transition or expire old partitions if you don't need them indefinitely. *** ## FAQs Work through this checklist: 1. **Wait 10 minutes.** The first sync takes time. 2. **Bucket name** matches exactly in AWS and SuprSend (case-sensitive). 3. **Region** matches in both AWS and SuprSend. 4. **Credentials** are right—Role ARN + External ID, or Access Key + Secret. 5. **Policy** grants `s3:PutObject`, `s3:ListBucket`, and `s3:GetObject` on the correct bucket ARNs. In the SuprSend dashboard, open **Connectors** → **Amazon S3 v2.0**. You should see: * Sync toggle ON * Status: **Active** * At least one data point selected * Data point might not be selected—check your export settings * When setting up the connector first time, data points only export going forward (no historical backfill) * Were notifications actually sent during that time? * If sync was paused, data backfills when you resume If you still find some gap in data, please contact support. Yes—you can add or remove data points at any time. Here's how each action behaves: | What you did | What happens | | ------------------------------------------ | ------------------------------------------------------ | | Paused then resumed the connector | Backfills everything from the pause window | | Added a brand-new data point | Starts exporting going forward; no historical backfill | | Removed a data point | Sync stops for it; data already in S3 stays there | | Re-enabled a previously enabled data point | Backfills from when it was disabled | # Analytics Source: https://docs.suprsend.com/docs/analytics Centralize notification performance tracking, monitor delivery across channels, and gain insights to optimize your notification strategy. Analytics provides visibility into your notification system. Track notification lifecycle signals, understand user engagement patterns, and optimize your workflows based on near real-time data. Navigate to the [Analytics tab](https://app.suprsend.com/en/production/analytics/) to get started. **Analytics vs Logs:** Analytics shows trends and summaries (like "email has 45% open rate"). [Logs](/docs/logging) shows individual events (like "this email was delivered at 2:34 PM"). Use Analytics to spot patterns, then use Logs to investigate specific issues. *** ## Available data points Analytics provides visibility into notification performance across multiple dimensions. You can analyze data by: * **[Channel performance](#channel-performance)** - Identify underperforming channels by comparing delivery and engagement metrics or look for any dips in delivery, open or click rates over time. * **[Notification volume](#notification-volume)** - Detect over-messaging patterns by tracking notifications per user across channels and categories. * **[Unsubscription trends](#unsubscription-trends)** - Reduce churn by understanding which channels and categories drive opt-outs. * **[Workflow Level Breakdown](#workflow-level-breakdown)** - Optimize notifications by comparing engagement across workflows, templates, channels and vendors. * **[Errors](#errors)** - Proactively fix issues by surfacing API, workflow, and delivery errors before users notice. *** ### Performance Overview Performance Overview provides a high-level view of your notifications performance. #### Channel performance Compare delivery and engagement across channels to see where users are most responsive. You see overall delivery, open and click rates by channel and their timeseries trend. You can drilldown graph by template-in addition to the [global filters](#filters) (workflow, tenant, date range). The chart above shows these metrics by channel. | Metric | What it means | | -------------------- | -------------------------------------------------------------------------------------------- | | **Delivered** | Provider or channel reports a delivered (or final) status | | **Seen** | A view/open signal is received (varies by channel) | | **Clicked** | User clicked on the notification | | **Delivery Failed** | Provider or channel reports a delivery failed status | | **Delivery Blocked** | Delivery blocked by SuprSend when test mode is enabled and the channel is not a test channel | You might see a warning icon next to a channel when the tracking is not properly configured for the channel. Click on the icon to see the details and fix the tracking issue. **Metrics tracked across channels:** Not all channels track all metrics. Here's what's tracked at the channel level: | Channel | Delivered | Seen | Clicked | | ---------------------------------------------- | --------- | ---- | ------- | | Email, Android Push, iOS Push, Web Push, Inbox | ✅ | ✅ | ✅ | | WhatsApp | ✅ | ✅ | ❌ | | SMS, Slack, MS Teams | ✅ | ❌ | ❌ | Configure `https://hub.suprsend.com/webhook/*` in your vendor dashboard to track delivery, seen, and click events for email, SMS and WhatsApp. See [vendor integration docs](/docs/vendors) for step-by-step instructions. **Interpreting your metrics:** Investigate vendor issues or delivery failures in [Errors](#errors). Review content and send timing. Use [Workflow Level Breakdown](#workflow-level-breakdown) to find which workflows are dragging the metric. If volume is high, use [batching](/docs/batch) or [throttling](/docs/throttle). For export options, see [Exporting data](#exporting-data). *** #### Notification volume Monitor average notifications sent per user per day. **Over-messaging is one of the reasons users unsubscribe.** Use this view to spot over-messaging before opt-outs spike. The following breakdown explains what you're looking at. **What you'll see:** * **Average notifications per user** - Average number of notifications each user receives per day. You can notification bucket vs %of users in each bucket. Anything in the red zone is considered over-messaging and you should try to keep the notification volume in the blue zone. * **Volume trends over time** - How notification volume changes over time * **Volume by category** - Which preference categories contribute most to total volume * **Volume by channel** - Which channels are sending the most notifications These ranges are general guidelines. Transaction-heavy products may legitimately exceed them. Monitor your opt-out rates to determine what works for your users. **Reducing volume:** * Use [throttling](/docs/throttle) to limit how often workflows trigger per user * Use [batch](/docs/batch) to consolidate multiple updates into single notifications * Use [smart channel routing](/docs/smart-delivery) to stop delivery on further channels when a user engages with the notification on any one channel. * If you're sending broadcast or promotional notifications, reduce the frequency of sending them. * Use [preference categories](/docs/managing-notification-categories) to let users manage preferences per category-prevents complete channel opt-outs and highlights where to reduce volume. *** #### Unsubscription trends Monitor opt-outs by channel, category, and time period to understand what is driving your users to opt out. **User preferences required:** This tracking will be available only when you use SuprSend's [preferences capabilities](/docs/user-preferences). **What you'll see:** * **Total opt-outs** - Number and percentage of users who unsubscribed * **Day-wise trends** - Opt-out patterns over time (use available time breakdowns to spot spikes) * **Category breakdown** - Which categories drive the most opt-outs * **Channel breakdown** - Which channels contribute most to opt-outs Watch for: Opt-out rate above 0.5%, spikes in opt-outs for specific categories, high opt-outs from a single channel, or opt-out rates increasing over time **Investigate the opt-outs:** 1. **Check notification volume first** - Over-messaging is the most common cause. Check if volume trends correlate with opt-out spikes. 2. **Filter by category or channel** - Identify which categories or channels have the highest opt-out rates and then optimize the volume or content of the notifications. 3. **Analyze day-wise trends** - Check if opt-outs spiked after a specific campaign or change. Over-messaging. Reduce frequency. Content or relevance issue. Review what you're sending. That campaign is the problem. Review its content and frequency. Users prefer other channels. Adjust channel strategy. Use filters (category/channel/workflow) to isolate drivers. Export raw user data for deeper analysis. *** ### Workflow Level Breakdown The Workflow Level Breakdown helps you compare notification performance **at the most actionable level** - individual workflows, templates, channels, and vendors. Each row shows notification performance indicators for a specific workflow, template, channel, vendor and category. **Metrics shown:** | Column | Meaning | | ------------------------- | ---------------------------------------------------------------------------------- | | **Triggered** | # of notification requests triggered or sent to vendor via SuprSend | | **% Delivered** | Percentage of triggered notifications that was delivered to the end user | | **% Seen / Delivered** | Percentage of delivered notifications that were opened or viewed (where supported) | | **% Clicked / Delivered** | Percentage of delivered notifications that were clicked (where supported) |
**Filters:**
Other than the global filters (workflow, tenant, date range), you can also filter by all table columns locally - channel, vendor, category and template.
**Sorting:**
Click any column header to sort. Sort by lowest seen/click rate to find underperformers, highest to find top performers, or trigger count for high-volume workflows.
**What to look for:** 1. **Identify top performers** * Sort by **% Seen / Delivered** or **% Clicked / Delivered** (descending) * Look for patterns in channel, timing, or template design * Reuse successful patterns across similar workflows 2. **Identify underperformers** * Sort by **% Seen / Delivered** or **% Clicked / Delivered** (ascending) * Compare against similar workflows in the same category * Check whether the issue is content, channel choice, timing or frequency of sending 3. **Identify issues with delivery** * Low **% Delivered** indicates delivery or vendor issues * Click through to the Errors tab to investigate notification delivery failures and fix them. 4. **Compare channels for the same workflow**
If you are sending on multiple channels for the same workflow, * Compare engagement rates side by side * Decide whether certain channels should be deprioritized or removed * Consider using [smart delivery](/docs/smart-delivery) to automatically choose the best channel *** ### Errors The Errors tab helps you identify and resolve issues that prevent notifications from being triggered, executed, or delivered. Unlike Logs (which show every event), Errors aggregates failures so you can quickly spot issues and prioritize fixes. You'll see errors grouped into three sections: Errors where API or SDK requests were not successfully processed. Workflow execution will not start for failed requests. Errors while running workflows or broadcasts. Message logs could still show for these cases for successful step execution. Vendor-reported delivery issues and couldn't deliver the notification to the end user. #### API Request Failures Errors where API or SDK requests were not successfully processed, and **no workflow execution was started**. If this error table is empty and you're actively sending requests, your integration is working as expected. **What you'll see:** * **Total occurrences** - How often the error occurred in the selected time range * **Last seen** - When the error last happened (to see if it's ongoing) * **Context** - API type or request context involved * **Error description** - The exact error message reported **Common causes:** * Invalid or expired API key * Authentication failure * Missing required fields (for example `user_id`) * Invalid request payload format **Filters:** API type, error description. **Sortable.** *** #### Workflow / Broadcast Execution Failures Errors that occur **after a request is accepted**, but while executing a workflow or broadcast. These usually indicate issues with workflow configuration or template data. Each row represents a workflow (or broadcast) and the node where execution failed. **What you'll see:** * **Total occurrences** - How often the error occurred in the selected time range * **Last seen** - When the error last happened (to see if it's ongoing) * **Workflow** - The workflow where the failure occurred * **Node Name** - The specific node in the workflow where execution failed * **Error description** - The exact error message reported **Most common errors:** * Template rendering failures due to missing variables * Dynamic fields in workflow couldn't be evaluated due to missing data or invalid data types If the same error appears multiple times with a high occurrence count, it indicates a systemic issue affecting many notifications. **Filters:** Workflow and Tenant (Global filter), Node name, error description. **Sortable.** #### Notification Delivery Failures Errors reported by vendors **after SuprSend successfully sent the request**, but the vendor could not deliver the notification. **What you'll see:** * **Total occurrences** - How often the error occurred in the selected time range * **Last seen** - When the error last happened (to see if it's ongoing) * **Template** - The template slug for the failed notification * **Channel** - The channel where delivery failed (email, SMS, push, etc.) * **Vendor** - The vendor that reported the delivery failure * **Error description** - The exact error message reported **Common delivery errors:** * User channel identities are invalid, blocked or missing * Vendor-side rate limiting * Credit limit exceeded * Temporary vendor outages Some delivery failures (like invalid addresses) require fixing user data. Others (like temporary outages) may resolve automatically. **Filters:** Workflow and Tenant (Global filter), Template slug, channel, vendor, error description. **Sortable.** If errors persist or you're unsure how to resolve a specific failure, see the [Error Guide](/docs/error-guides) or contact support. *** ## Exporting data Export CSVs for analysis outside SuprSend (Sheets/Excel/Data warehouse). **Summary:** You can export upto 50 million rows per file. If your export exceeds 50M rows, SuprSend automatically splits it into multiple files with `partition_id` column telling you the file number. **How to export:** 1. Navigate to the data point you want to export 2. Click the 📥 icon in the top right of that section 3. Select your date range (relative or absolute) 4. Choose your export type (aggregate or raw user data) 5. Apply any filters you want to include 6. Download the CSV file(s)
**Export types:** **Aggregate user data** - Summary statistics grouped by date and time (matches what you see in charts). Includes: Date/time buckets, total counts, percentage rates, grouped by workflow/channel/category/vendor/template. Use cases: Custom dashboards, trend analysis, high-level reporting. **Raw user data** - Individual user-level records with timestamps for available events. Includes: User records, timestamps for each event, workflow/template/channel/vendor/category, user properties and metadata. Use cases: Check each log, detailed user behavior analysis, and drilldown into trends to identify patterns. *** ## Navigating Analytics ### Refresh Analytics data updates automatically every 5 minutes. You can manually refresh the data by clicking the refresh button to get the latest metrics. ### Filters You have global filters to drill down analytics data by workflow, tenant, and date range. Other than this, local filters are available for against respective data point to filter by channel, vendor, category, node, error description and template. ### Date Range and Retention Analytics default to "Last 7 days" when first opened. Choose from relative ranges (Last 1 day, 7 days, 30 days, and additional presets) or select absolute date ranges with timezone-aware selection. Data retention period is based on your [pricing plan](https://www.suprsend.com/pricing). For extended retention options, contact our [sales team](mailto:sales@suprsend.com). *** ## Frequently Asked Questions Analytics data updates in near real-time, usually within a few minutes of events occurring. We cache the data for 5 minutes after last refresh. For immediate verification of a specific event, use [Logs](/docs/logging) instead. Analytics automatically uses your browser's timezone by default. You can change the timezone in the top navbar if you need to view data in a different timezone. It could be because the data retention period for your pricing plan is shorter than 90 days. Check the [pricing page](https://www.suprsend.com/pricing) for specific retention periods for your pricing plan. If you see "seen" or "clicked" metrics showing as 0% or missing for channels that support them, here are the most common causes: * **Tracking not configured:** Configure the webhook URL `https://hub.suprsend.com/webhook/*` in your vendor account. See [vendor integration docs](/docs/vendors) for step-by-step instructions * **Vendor has not sent engagement events:** There might be some delay in vendor reporting the engagement events. You can wait for some time and check again. If the issue persists, contact your vendor or SuprSend support. * **Channel limitation:** Some channels (SMS, Slack, MS Teams) don't support seen/clicked metrics-this is expected High opt-out rates usually indicate over-messaging or the user doesn't find value in the notification. See what categories or channels are contributing to the high opt-out rate and optimize the workflows in those categories or in general check if you are over-messaging the users. See the [Unsubscription trends](#unsubscription-trends) section for detailed investigation steps and the [Notification volume](#notification-volume) section for reducing frequency. Analytics numbers change because vendors send status updates over time. This is normal behavior as users can open or click on the notification at any time. Need help? [Contact our support team](mailto:support@suprsend.com). # Manage Users Source: https://docs.suprsend.com/docs/android-create-user Use the SuprSend Android SDK to create and identify users, set push tokens, attach email or SMS channels, and manage user profile properties from your app. ## 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 Integrate Firebase Cloud Messaging FCM push into your Android app with SuprSend, including Firebase project setup, service account keys, and SDK installation. ## 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 Source: https://docs.suprsend.com/docs/android-push-template Design Android push templates in SuprSend with title, image, buttons, action URLs, sound, silent and sticky notification options, plus live device preview. The Android Push editor is a form with title, message, image, action URL, and buttons - with a live device preview on the right. Content is personalised with [Handlebars](/docs/handlebars-helpers) variables (`{{variable_name}}`). ## Android Push fields **Title** - single-line heading. Keep under 40 characters - Android truncates to one line. Supports Handlebars variables. **Small Icon** - icon in the status bar and notification header. Defaults to the SuprSend bell icon. See [custom icon setup](#how-to-change-the-small-icon-for-a-notification) to use your app icon. **Large Icon** - appears left of the text on Android 4.0–6.0, right on Android 7.0+. Defaults to your organisation logo (set in Organisation Settings). **Message** - multi-line body text. Front-load key info in the first 2 lines (expanded view shows \~6 lines). Supports Handlebars variables. **Subtext** - *optional.* Appears next to your brand name at the top of the notification. **Banner Image** - *optional.* Supported formats: PNG, JPG, JPEG. Recommended: 2:1 aspect ratio, under 700 KB. Static uploads are auto-scaled and optimised by SuprSend. **Action URL** - URL opened on notification tap. Supports [deep links](#deep-linking) and Handlebars variables. **Action Buttons** - *optional.* Up to 3 buttons with label + URL. Use 1-2 buttons with concise labels (2-3 words): "Track Order", "View Details". Supports Handlebars in both fields. Button colour is set in Organisation Settings. **Expo SDK support** When delivering Android Push via the [Expo SDK](/docs/expo-push-notifications), only the following fields are supported: * Title * Message (Body) * Banner Image * App Icon * Sound * Custom Key-Value Pairs Action URL is **not** a supported field on Expo — pass the destination URL through a **Custom Key-Value Pair** and handle the tap in your app code. Other fields listed on this page (Small Icon, Large Icon, Subtext, Action Buttons, Silent, Timeout, Sticky, Notification Group) are not delivered through the Expo SDK. ## 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: Add sample data in the **Variables panel** (Input Payload section) on the left side of the editor. If you have declared the variables and added sample data, they will come as auto-suggestions when you type a curly bracket `{`. This removes the chances of error like variable mismatch at the time of template rendering. To see how to declare variables, refer to [this section in the Templates documentation](/docs/templates#the-variables-panel). 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}}` Note that you will be able to enter a variable name even when you have not declared it in the Variables panel. To manually enter the variable name, follow the [handlebars guide here](https://handlebarsjs.com/guide/#what-is-handlebars). Below are some examples: ```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: `{{event.location.city}}` * To refer to an array element: `{{array.[0].product_name}}` * If you have a space in the variable name: `{{event.[first name]}}` You will be able to see the sample values in the Preview section. If a variable isn't rendering, check: 1. The variable is defined in the Variables panel. 2. The variable name matches the Handlebars syntax exactly. 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. For example 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. ## Advanced configurations | Field | Type | Description | | ---------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Silent | Boolean | Users won't see this message. Triggers background activities using the notification payload. Useful for data sync, content pre-fetching, or breaking news alerts. | | Timeout | Numeric | Auto-dismiss after N seconds if the user hasn't interacted. Combine with Sticky for urgent time-limited alerts (for example, 2FA prompts). | | Sticky Notifications | Boolean | Prevents swipe-dismiss. Removed only when the user taps the notification. | | Notification Group | Text | Group name to stack related notifications (for example, chat messages from the same conversation) to avoid flooding the tray. | | App Icon (Small Icon) | Text | Icon name without extension. See custom icon setup in the [FAQ below](#frequently-asked-questions). | | Sound | Text | Sound file name. See custom sound setup in the [FAQ below](#frequently-asked-questions). | | Custom Key-Value Pairs | Key-value | Send custom data to the app. Both key and value are strings. Combine with silent notifications for background data updates. | **SDK version requirements:** * App icon: Android native and React Native SDK version 0.1.8+ * Custom sound: Android native and React Native SDK version 2.2.0+ **Expo SDK support for advanced configurations** From the table above, only **App Icon**, **Sound**, and **Custom Key-Value Pairs** are delivered through the [Expo SDK](/docs/expo-push-notifications). **Silent**, **Timeout**, **Sticky Notifications**, and **Notification Group** are not supported on Expo. ## Preview and test The right panel shows a live Android device preview, updated in real time as you edit. Variables render using data from the **Variables panel**. Click **Test** in the top-right corner to send a real push notification to a real device. This uses the **live version** - commit your changes before testing. See [Testing a Template](/docs/templates#test) for the full guide. ## Commit Click **Commit** in the top bar to publish the current draft as a new live version. Add an optional description for versioning. Once committed, all notifications triggered after this point use the new content. ## Common scenarios | Field | Value | | ------------ | ------------------------------------------------------------------ | | Title | `Order #{{order_id}} shipped` | | Message | `Your package is on the way! Expected delivery: {{delivery_date}}` | | Banner Image | `{{product_image_url}}` | | Action URL | `https://yourapp.com/track/{{order_id}}` | | Field | Value | | ------------ | --------------------------------------------------- | | Title | `Flash sale ends tonight` | | Message | `Up to 50% off on your favourites. Don't miss out!` | | Banner Image | (static promotional banner) | | Button 1 | `Shop Now` → `https://yourapp.com/sale` | | Button 2 | `View Wishlist` → `yourapp://wishlist` | | Field | Value | | ---------------------- | ----------------------------------------------------------------- | | Silent | `ON` | | Custom Key-Value Pairs | `sync_type` = `catalog_update`, `version` = `{{catalog_version}}` | Use silent notifications to trigger background data fetches — for example, pre-loading content so it's ready when the user opens the app. ## Frequently asked questions The **Action URL** and **Action Button URLs** support three URL types: | URL type | Format | Behaviour | | ------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Web URL | `https://yourapp.com/orders/123` | Opens in the browser (or your app if [App Links](https://developer.android.com/training/app-links) are configured) | | Custom scheme | `yourapp://orders/123` | Opens the matching activity in your app directly | | App Link | `https://yourapp.com/orders/123` (verified) | Opens your app directly without a browser redirect | If your app supports App Links, prefer `https://` URLs - they work as both web fallback and deep links. Add a small icon named `ic_suprsend_app_icon` in the drawable folders of your app. Android only uses the alpha channel - the icon displays as monochrome in the status bar. You can use a vector drawable (`androidApp/src/main/res/drawable/`) or PNG icons at each density: | Density | Size | | ------- | ----- | | MDPI | 24x24 | | HDPI | 36x36 | | XHDPI | 48x48 | | XXHDPI | 72x72 | | XXXHDPI | 96x96 | Use [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html) for quick icon generation. If you see the default bell icon, ensure all density sizes are present. If you see a solid square, the image lacks alpha transparency. **SDK requirement:** Android native and React Native SDK version 0.1.8+. **Expo SDK:** App Icon is supported when delivering via the [Expo SDK](/docs/expo-push-notifications). Add the sound file to `projectroot/app/res/raw` (lowercase filename, underscores instead of spaces, for example `notification_music.mp3`). **Important:** On Android 8.0+ (\~95% of users), sound is set at the [notification channel](https://developer.android.com/develop/ui/views/notifications/channels) level when the [category](/docs/notification-category) is first created. Changing the sound in the template only takes effect for: * a new preference category * a user installing the app for the first time * a user who uninstalled and reinstalled the app **SDK requirement:** Android native and React Native SDK version 2.2.0+. For static images uploaded in the Banner Image field, SuprSend applies two optimisations: * **Screen width** - large images are resized to fit the user's mobile screen width. * **Network-aware** - image quality is adjusted based on the user's connection (WiFi, 4G, 3G, 2G) to improve delivery speed on slow networks. Silent notifications deliver a payload to the app without displaying anything to the user. Use them for background data sync, content pre-fetching, or triggering app-level logic. Combine with **Custom Key-Value Pairs** to pass structured data. SuprSend discards the Android Push notification for that user. Other channels in the same template group (email, SMS, etc.) are still sent if they render successfully. # Send and Track Events Source: https://docs.suprsend.com/docs/android-send-event-data Use the SuprSend Android SDK to send and track user events from your app, pass event properties, and trigger notification workflows on real time user actions. ## 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 Reference list of Android push delivery errors returned by FCM legacy and HTTP v1 APIs in SuprSend, with root causes and step by step resolution guidance. ## 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://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ) 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 (for example 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 (for example, 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?tenant_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?tenant_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://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ) 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?tenant_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://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ) 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). *** # Query with Athena Source: https://docs.suprsend.com/docs/athena_s3_query Run SQL on your SuprSend logs in S3 — no warehouse, no ETL. Set up the database, register external tables, and run your first query. [Amazon Athena](https://docs.aws.amazon.com/athena/latest/ug/what-is.html) reads Parquet files directly from S3 and bills per terabyte scanned, so it's a fast way to explore the data the [Amazon S3 v2.0 connector](/docs/amazon_s3_v2) writes—no warehouse to provision, no ingestion pipeline to maintain. By the end of this guide you'll have a database named `suprsend_db` with three tables (`ss_requests`, `ss_workflow_executions`, `ss_messages`), and you'll have run your first query. *** ## Prerequisites You'll need: * The [S3 v2.0 connector](/docs/amazon_s3_v2#setup) running and writing Parquet files to your bucket. * Access to the AWS account that owns the bucket, with permission to use Athena and read the bucket. * An S3 location for Athena to store query results (a separate prefix or a different bucket). Your connector also needs two specific settings—both are defaults for new connectors: * **Path layout: `per_type`.** The DDL in this guide creates a separate external table for each data point, with each one pointing at its own S3 prefix (`/workflow_executions/`, `.../requests/`, `.../messages/`). That folder structure exists only with `per_type`. * **Compression: any codec except `lz4`.** Athena can't reliably read `lz4`-encoded Parquet—this is a known Athena limitation, not something specific to SuprSend. `snappy` is the default compression which works well with Athena. You can review or change either setting in [Compression and path layout](/docs/amazon_s3_v2#compression-and-path-layout). **Connectors enabled before 21 May 2026** were on `lz4` compression and `shared` path layout which had issues with Athena connector setup. So, we recommend changing your compression to `snappy` and path layout to `per_type` which works very well with Athena. When you change the setting, the new setting will only apply to files written **after** the switch. In order to sync the older data, you can reach out to [suppport@suprsend.com](mailto:suppport@suprsend.com) and we can run one time sync of older data in your S3 bucket. *** ## 1. Set the Athena query result location Open the [Athena console](https://console.aws.amazon.com/athena/) in the same region as your S3 bucket. The first time you use Athena in a region you have to set a result location: 1. Go to **Settings** → **Manage**. 2. Set **Location of query result** to a path you control, for example `s3://YOUR_BUCKET_NAME/athena-results/`. 3. Save. *** ## 2. Create the database In the query editor, run: ```sql theme={"system"} CREATE DATABASE IF NOT EXISTS suprsend_db; ``` The rest of the guide uses `suprsend_db`. If you pick a different name, update the `CREATE EXTERNAL TABLE` statements below to match. *** ## 3. Create the external tables Run each statement to register one external table per data point. They use [Athena partition projection](https://docs.aws.amazon.com/athena/latest/ug/partition-projection.html) on `year/month/day/hour`, so new hourly partitions are picked up automatically—you don't need `MSCK REPAIR TABLE` or a Glue Crawler. Replace `YOUR_BUCKET_NAME` in `LOCATION` and `storage.location.template` with your bucket. If your connector writes under a path prefix (for example `staging/`), include it before the data-point folder—`s3://YOUR_BUCKET_NAME/staging/workflow_executions/`. ```sql theme={"system"} CREATE EXTERNAL TABLE suprsend_db.ss_workflow_executions ( workspace_key string, created_at timestamp, updated_at timestamp, execution_id string, recipient_distinct_id string, tenant_id string, idempotency_key string, parent_object_execution_id string, parent_object string, workflow_slug string, workflow_version string, node_id string, node_name string, node_type string, execution_stage string, status string, message string, properties string ) PARTITIONED BY ( year string, month string, day string, hour string ) STORED AS PARQUET LOCATION 's3://YOUR_BUCKET_NAME/workflow_executions/' TBLPROPERTIES ( 'projection.enabled' = 'true', 'projection.year.type' = 'integer', 'projection.year.range' = '2022,2030', 'projection.month.type' = 'integer', 'projection.month.range' = '1,12', 'projection.day.type' = 'integer', 'projection.day.range' = '1,31', 'projection.hour.type' = 'integer', 'projection.hour.range' = '0,23', 'storage.location.template' = 's3://YOUR_BUCKET_NAME/workflow_executions/year=${year}/month=${month}/day=${day}/hour=${hour}/' ); ``` ```sql theme={"system"} CREATE EXTERNAL TABLE suprsend_db.ss_requests ( workspace_key string, created_at timestamp, updated_at timestamp, api_type string, api_name string, wf_trigger_type string, distinct_id_list array, actor string, tenant_id string, payload string, response string, metadata string, errors array>, executions array>, idempotency_key string, status string ) PARTITIONED BY ( year string, month string, day string, hour string ) STORED AS PARQUET LOCATION 's3://YOUR_BUCKET_NAME/requests/' TBLPROPERTIES ( 'projection.enabled' = 'true', 'projection.year.type' = 'integer', 'projection.year.range' = '2022,2030', 'projection.month.type' = 'integer', 'projection.month.range' = '1,12', 'projection.day.type' = 'integer', 'projection.day.range' = '1,31', 'projection.hour.type' = 'integer', 'projection.hour.range' = '0,23', 'storage.location.template' = 's3://YOUR_BUCKET_NAME/requests/year=${year}/month=${month}/day=${day}/hour=${hour}/' ); ``` ```sql theme={"system"} CREATE EXTERNAL TABLE suprsend_db.ss_messages ( workspace_key string, created_at timestamp, updated_at timestamp, wf_execution_id string, broadcast_execution_id string, message_id string, recipient_distinct_id string, tenant_id string, idempotency_key string, parent_object string, parent_object_execution_id string, workflow_slug string, template_name string, template_slug string, message_status string, message_triggered_at timestamp, message_delivered_at timestamp, message_seen_at timestamp, message_clicked_at timestamp, node_id string, node_name string, node_type string, execution_failure_reason string, delivery_failure_reason string, note string, message_id_by_vendor string, vendor_fallback_applicable string, vendor_fallback_level string, vendor_nickname string, vendor_slug string, is_smart string, success_metric string, success_achieved_at string, wait_time_in_seconds string, channel_slug string, channel_value string, webhook_data string ) PARTITIONED BY ( year string, month string, day string, hour string ) STORED AS PARQUET LOCATION 's3://YOUR_BUCKET_NAME/messages/' TBLPROPERTIES ( 'projection.enabled' = 'true', 'projection.year.type' = 'integer', 'projection.year.range' = '2022,2030', 'projection.month.type' = 'integer', 'projection.month.range' = '1,12', 'projection.day.type' = 'integer', 'projection.day.range' = '1,31', 'projection.hour.type' = 'integer', 'projection.hour.range' = '0,23', 'storage.location.template' = 's3://YOUR_BUCKET_NAME/messages/year=${year}/month=${month}/day=${day}/hour=${hour}/' ); ``` `projection.year.range` is set to `2022,2030`. Widen the upper bound if you'll be querying data beyond 2030. For column meanings, see the [table schemas](/docs/amazon_s3_v2#table-schema) in the S3 v2.0 doc. *** ## 4. Run your first query You're ready to query. The cardinal rule with Athena is to always filter on the partition columns (`year`, `month`, `day`, `hour`) where you can—Athena bills per TB scanned, so partition pruning is the main way to keep costs predictable. ```sql theme={"system"} SELECT * FROM suprsend_db.ss_workflow_executions WHERE year = '2026' LIMIT 100; ``` Trace a single request across all three tables using `idempotency_key`, which is shared across Requests, Workflow Executions, and Messages: ```sql theme={"system"} SELECT r.idempotency_key, w.execution_id, m.message_id, m.message_status FROM suprsend_db.ss_requests AS r LEFT JOIN suprsend_db.ss_workflow_executions AS w ON w.idempotency_key = r.idempotency_key LEFT JOIN suprsend_db.ss_messages AS m ON m.wf_execution_id = w.execution_id WHERE r.year = '2026' AND r.idempotency_key = 'YOUR_IDEMPOTENCY_KEY'; ``` For the canonical relational join (Requests → Workflow Executions via `UNNEST(requests.executions).exec_id = workflow_executions.execution_id`), see the [linking columns](/docs/amazon_s3_v2#linking-different-data-points) reference. *** ## Points to note Some columns are stored as `string` in Parquet even though they hold JSON, booleans, integers, or timestamps. Cast them explicitly: * **JSON** (`payload`, `response`, `metadata`, `properties`, `webhook_data`, `execution_failure_reason`) → `json_extract(column, '$.field')` * **Integer** (`vendor_fallback_level`, `wait_time_in_seconds`) → `CAST(column AS BIGINT)` * **Boolean** (`vendor_fallback_applicable`, `is_smart`) → compare against `'true'` / `'false'` * **Timestamp** (`success_achieved_at`) → `from_iso8601_timestamp(column)` or `date_parse(column, '')` Full column list in the [table schemas](/docs/amazon_s3_v2#table-schema). The connector rewrites hourly Parquet files when upstream data changes. The next Athena query picks up the new state automatically — no `MSCK REPAIR TABLE` or Glue Crawler refresh needed. If you point the connector at a new bucket or prefix, existing tables stop returning new data. Drop and recreate each table with the updated `LOCATION` and `storage.location.template`. # Audit Logs Source: https://docs.suprsend.com/docs/audit-logs Use SuprSend audit logs to track every action taken by team members in your account, including who changed what, when, for security and team accountability. Audit logs provide a complete record of all actions performed by team members on your SuprSend account. Each entry includes who performed the action, when it happened, and details about what changed. This is particularly useful for maintaining team accountability, tracking security-sensitive operations, and quickly identifying who made specific changes-especially helpful when someone accidentally deletes a service token or makes an unintended configuration change. Audit logs are available on the Enterprise plan. ## Accessing audit logs To view all account activity: 1. Go to **Account Settings** by clicking on the user icon in the top right corner of the dashboard. 2. Select [Audit Logs](https://app.suprsend.com/en/account-settings/audit-logs) tab. The audit logs page displays a list of actions performed in your account, showing the actor, action, workspace, location, and date for each entry. Filter audit logs to find specific entries by: | Filter option | Description | | ------------- | -------------------------------------------------------------------------------------------------- | | Date range | Choose relative date ranges (for example, last 7 days, last 30 days) or set an absolute date range | | Actor | Filter by team member email or name | | Action | Select one or more action types (for example, `account.member_invited`, `api_key.generated`) | ## Tracked actions Monitors all security-related changes including authentication methods, API keys, tokens, and signing keys. These are the highest priority events to track. **Authentication** (Account Settings → Authentication) | Action | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account.mfa_enabled` | Multi-factor authentication (MFA) was enabled for your account. This adds an extra layer of security by requiring a second verification step when logging in. | | `account.mfa_disabled` | Multi-factor authentication (MFA) was disabled for your account. This removes the additional security layer. | **Service Tokens** (Account Settings → Service Tokens) | Action | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `service_token.generated` | A new service token was created. Service tokens are used for authentication of management API requests or for CLI operations. | | `service_token.deleted` | Service token was deleted. This token can no longer be used for authentication. | **API Keys** (Developers → API Keys) | Action | Description | | ------------------- | -------------------------------------------------------------------------------------- | | `api_key.generated` | A new API key was created. API keys are used to authenticate API requests to SuprSend. | | `api_key.disabled` | API key was deleted. This key can no longer be used to make API requests. | **Public Keys** (Developers → API Keys) | Action | Description | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `public_api_key.generated` | A new public API key was generated. Public API keys are used for authentication of client SDK requests. | | `public_api_key.rotated` | Public API key was rotated (replaced with a new key). This is done for security purposes to invalidate the old key. | | `public_api_key.deleted` | Public API key was deleted. This key can no longer be used for authentication. | | `public_api_key.secure_mode_enabled` | Enhanced security mode was enabled for public API key. This requires a signed user token to be sent along with client requests. | | `public_api_key.secure_mode_disabled` | Enhanced security mode was disabled for public API key. This no longer requires a signed user token to be sent along with client requests. | **Signing Keys** (Developers → API Keys) | Action | Description | | --------------------------- | ------------------------------------------------------------------------------------------------------------ | | `signing_api_key.generated` | A new signing API key was generated. Signing keys are used to sign JWT tokens for client SDK requests. | | `signing_api_key.deleted` | Signing API key was deleted. This key can no longer be used to sign JWT tokens. | | `signing_api_key.rolled` | Signing API key was rolled (replaced with a new key). The old key is invalidated and a new one is generated. | Tracks changes to your team - member invitations, deactivations, and role changes. **Team Management** (Account Settings → Team) | Action | Description | | --------------------------------- | --------------------------------------------------------------------------------------------- | | `account.member_invited` | New team member was invited to the team. They will receive an email to join your team. | | `account.member_deactivated` | Team member was deactivated. This user can no longer access the account. | | `account.member_invite_cancelled` | Pending member invitation was cancelled. They will not be able to join using that invitation. | | `account.member_invite_resent` | Member invitation was resent. This is useful if the original invitation expired or was lost. | | `account.member_role_changed` | Team member role was changed. This affects their permissions and access level. | We're continuously expanding audit log coverage to include other account level actions. If you have any suggestions or need to track other actions on priority basis, please contact the SuprSend [support team](mailto:support@suprsend.com). ## FAQs This feature to download audit logs or export it in your data warehouse is not available yet. You can raise feature request on [slack community](https://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ) or email us at [support@suprsend.com](mailto:support@suprsend.com). We start tracking audit logs in your account as soon as you upgrade to the Enterprise plan. The log retention timeframe depends on your billing plan. When events occur that don't originate from a user action (like automated system updates or side effects from merges), these events may be attributed to system processes. This helps maintain a complete audit trail of all changes to your account. # Authentication Methods Source: https://docs.suprsend.com/docs/authentication-methods Overview of authentication methods to securely access your SuprSend dashboard account, including Magic Auth, Google SSO, SAML SSO, and invite only sign ups. **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 Integrate Amazon AWS SNS with SuprSend to deliver SMS notifications worldwide, including IAM setup, access keys, sender ID, and SNS vendor configuration steps. ## 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. for example - *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 Use the batch node in SuprSend workflows to aggregate multiple triggers and send one consolidated notification instead of spamming users with repeated alerts. 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. For example, 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, for example 10 minutes before task due time). Fixed batch window is defined in your workflow form as `xxd xxh xxm xxs` 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`.preferences.alert_frequency`. ```json json theme={"system"} properties = { "preferences": { "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 (for example 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 `xxd xxh xxm xxs`, 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. For example, 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 (for example 30m for 30 minutes). This can be: Fixed (for example always 1 hour minutes before). Dynamic, where the value is retrieved from the payload (for example 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 (for example 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. 📘 **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#the-variables-panel) 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. For example, 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 Best practices for securely managing SuprSend workspace keys, secrets, API keys, and service tokens in your backend, mobile, and frontend application 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 workspaces. **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 (for example, 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 (for example, 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 (for example, GitHub). 2. **Use environment variables** or a **Key Management Service** (for example, 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, repositories, 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 for designing a scalable notification system backend that integrates cleanly with SuprSend APIs, SDKs, workflows, and user preference management. ### 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 Set up the SuprSend BigQuery connector to auto sync user cohorts and subscriber lists from your data warehouse and power notifications on warehouse data. 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. If you have not created OAuth consent screen before, you will need to create one. Since SuprSend will be used for your internal employees only, Use `Internal` option from the list Enter required details like App name, your email, developer contact information, authorized domain etc. ### 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**. 1.2. Fill in the details as shown 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 2.2. Fill in the Service Account details as shown below, and click on CREATE AND CONTINUE: 2.3. Fill in the Role details as shown below, and click on CONTINUE: 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: 3.2. Click on **ADD KEY**, followed by Create new key, as shown: 3.3. Select **JSON** and click on **CREATE** 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 the branch node in SuprSend workflows to route notifications through different paths with if, else if, and else conditions evaluated on input data. Branch is an `if / else if / else` step that routes notifications through different workflow paths based on conditions. You can add up to 10 branches (9 condition branches + 1 default branch). The first branch that satisfies its condition will be executed. Conditions are evaluated **at runtime**, using the latest available data at the moment the Branch node executes. **Common use cases:** A/B testing, personalized journeys, conditional follow-ups, multi-step reminders, routing by trigger payload, batch/digest data, or tenant settings. ## Execution Model The Branch node supports two branch types: 1. **Condition Branch**: Executes when its condition evaluates to true (up to 9 condition branches). 2. **Default Branch**: Executes when no condition branch evaluates to true. **Execution rules:** * Conditions are evaluated in order from first to last. * The first branch whose condition evaluates to true is executed. * If no condition evaluates to true, the default branch executes. * If the branch has no nodes below it, it automatically connects to the exit node. ## Condition Syntax Conditions evaluate data from trigger payloads, user properties, tenant properties, or message status. A condition consists of four components: **Data type**, **Key**, **Operator**, and **Value**. ### Data Types | Data Type | Description | What to pass in key | What to pass in dynamic value | | | ------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | | **Input Payload** | Data from trigger payload or nodes before the branch | Directly specify as `key` with no prefix | Use key directly (for example, `payment_due_date`) | | | **Actor** | Properties of the user who performed the action | Add as `` | Add as `$actor.` | | | **Recipient** | Properties of the user receiving the notification | Add as `` | Add as `$recipient.` | | | **Tenant** | Properties of the tenant/brand | Add as `` for reserved properties, `properties.` for custom properties | Add as `$tenant.` or `$tenant.properties.` | | | **Message Status** | Delivery status of previously sent notifications | Pass the node slug whose message status you want to check, for example, `message_status from node "welcome-email" == "seen"` | Pass the status you want to check, for example, `"seen"` | Value can be one of `delivered`, `seen`, `clicked`, `delivery_failed` | ### Operators | Operator | Usage | Supported Data Types | | ---------------------------------------------------------- | ----------------------------------------------------------------- | -------------------- | | `==` / `!=` | Equal to / not equal to (case sensitive) | All | | `>`, `>=` | Greater than / greater than or equal | Numbers, timestamps | | `<`, `<=` | Less than / less than or equal to | Numbers, timestamps | | `contains` / `not contains` | Substring or array item match / no substring or array item match | Strings, arrays | | `is empty` / `is not empty` | key is missing, empty or null / key is present, not empty or null | All | | `datetime is` / `datetime is before` / `datetime is after` | Datetime equal to / less than / greater than | Timestamps | | `intersects` / `not intersects` | Any array value matches / No array values match | Arrays | **Type constraints**: If a key's data type doesn't match the operator (for example, using `>` on a string), the condition will always evaluate to false. ### Values You can either add a fixed value or a dynamic value to the condition. #### Fixed values | Type | Syntax | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | String | Enclose within double inverted commas as `"string"` | | Number | Add without double inverted commas as `100`, `99.99` | | Boolean | Add without double inverted commas as `true`, `false` | | Datetime | Enclose within double inverted commas as `"2024-01-01T00:00:00Z"` or `"now+1d"` | | Message Status | Select from dropdown on frontend or pass as `$message.seen` in workflow API. Value can be one of `delivered`, `seen`, `clicked`, `delivery_failed` | #### 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. | Type | Syntax | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Input payload | Add key directly (for example, `payment_due_date`) | | Actor | Add as `$actor.` (for example, `$actor.role`) | | Recipient | Add as `$recipient.` (for example, `$recipient.plan`) | | Tenant | Add as `$tenant.` for reserved properties, add as `properties.` for custom properties (for example, `$tenant.timezone`, `properties.timezone`) | ## Combining Conditions Combine conditions using `AND` and `OR` logical operators: * **AND**: All conditions must evaluate to true * **OR**: At least one condition must evaluate to true **Example:** `(user_role == "admin" OR user_role == "manager") AND priority == "high"` Nested grouping is not supported. Rewrite complex logic using equivalent expanded conditions. For example, `(a OR b) AND c` becomes `(a AND c) OR (b AND c)`. ## Condition on Message Status Message Status is a special data type that lets you evaluate the delivery or engagement state of a previously sent notification. Common use cases include reminder and escalation workflows-for example, sending a follow-up notification if the user has not seen the earlier message. When using message status checks, always add a delay before evaluating the status to allow sufficient time for vendors to report delivery or engagement events.
**Condition Syntax:**
Condition on message status looks like this
` IS/ IS_NOT `. Eg. `message_status from node "welcome-email" is "seen"`. You can find node slug below node name in the workflow editor. ```json theme={"system"} { "op": "IS", "ref": "multichannel_1", // node slug "value": "$message.seen", // message status "variable": "message_status", // fixed value "variable_ns": "$ref" // fixed value } ``` On UI, it looks like this:
**Message Status and their meaning:**
Here's a list of all message statuses, supported operators and their meaning. | Status | `IS` | `IS_NOT` | Description | | ----------------- | ---- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `delivered` | ✅ | ✅ | Notification reached user (includes delivered, seen, clicked) | | `seen` | ✅ | ✅ | User viewed/opened or clicked notification. For Android Push channel, user clearing the notification from tray is also considered as seen. | | `clicked` | ✅ | ✅ | User clicked notification | | `delivery_failed` | ✅ | ❌ | Vendor reported some error which prevented delivery | For multi-channel and smart channel routing, `delivered`, `seen` and `clicked` status requires at least one successful channel, while `delivery_failed` requires all channels to fail.
**Per-Channel Message Status:**
Following message statuses are currently tracked for each channel: | Channels | Delivered | Seen | Clicked | | ---------------------------------------------- | --------- | ---- | ------- | | Email, Android Push, iOS Push, Web Push, Inbox | ✅ | ✅ | ✅ | | WhatsApp | ✅ | ✅ | ❌ | | SMS, Slack, MS Teams | ✅ | ❌ | ❌ | To track message status for external channels like WhatsApp, SMS, and Email, you'll need to add suprsend tracking URL as webhook in your vendor dashboard. ## Frequently Asked Questions Up to 10 branches total: 9 condition branches + 1 default branch. The first branch in order is executed (as mentioned in the intro). Place branch with more specificity before the branch with lesser condition if they have common condition set. Only after the referenced node has executed and vendor feedback has been received. The condition checks actual delivery state, not input data. Message Status requires vendor tracking to be setup. Ensure you have: * SuprSend tracking URL added as webhook in your vendor dashboard. * Proper SDK installation for Android Push and iOS Push channels. * For Inbox, Slack, MS Teams, engagement status are automatically tracked. In case all the above conditions met and still the condition is not evaluated, it could be due to delay in vendor reporting the status. * **`IS delivery_failed`**: Evaluates to true only when the vendor explicitly reports a delivery failure. For multi-channel delivery nodes, this condition is met only if all channels fail. * **`IS_NOT "delivered"`**: Indicates that a successful delivery status has not been received for the channel. This can also occur if the vendor has not yet reported the delivery status by the time the branch condition is evaluated. Each node has a unique slug. You can see node slug below node name in the workflow editor. You can also edit node slug by clicking on navigation icon next to node name and selecting "Edit metadata". *** # Broadcast notifications overview Source: https://docs.suprsend.com/docs/broadcast Learn how SuprSend broadcasts work to send the same notification to all users on a subscriber list across email, SMS, push, and other channels in one request. 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 are 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 selecting `Run Broadcast` option in the kebab menu on `Subscriber -> Lists` tab This will open the broadcast form. Fill in the relevant information and select the workflow template. | 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. | | Preference category | *Mandatory* | [Preference 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. **Note:** This data is generic and will be the same for all users in the list. For example: `{"promo_code": "SUMMER2024", "discount": "20%"}` will send the same promo code and discount to every user. For per-user personalization, add `$recipient.` as variable in your template. Anything inside `$recipient.` will be replaced with the user's property value. | | When | *Optional* | -**Immediately** will trigger the broadcast instantly on the click of the button -Select **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 the **Run Broadcast** button, it will ask for a confirmation. This is just to ensure that bulk campaigns are not triggered by mistake. Click **Confirm** to trigger the broadcast. You can check the notification status on Logs page. *** # Broadcast execution logs Source: https://docs.suprsend.com/docs/broadcast-execution-logs Aggregate execution summary for broadcast campaigns — processing steps, user statistics, and delivery status across lists. Broadcast execution logs track broadcast campaigns sent to user lists. Unlike workflow execution logs which are per-user, broadcast logs provide a summary of execution across a list of users, showing aggregate statistics and processing steps. Each broadcast execution log entry includes these sections: * **Overview**: Generic information about the broadcast execution: * Broadcast slug, tenant ID and List ID (clickable) * Broadcast Execution ID (share this with SuprSend support if you need help in debugging the broadcast or want to report an issue) * Idempotency key (unique identifier of the request passed to SuprSend) * Status (Triggered, Skipped, Failed) * Start time (broadcast start timestamp) * Preference category * **Execution history**: Chronological logs of broadcast start, template loading, user profile fetching (with progress percentage), channel computation, template rendering, and notification triggering. * **Aggregate statistics**: Summary of the broadcast performance: * Total users processed (percentage and count) at each stage of the broadcast execution * At delivery stage: Preference evaluation, messages triggered and delivery status (delivered, seen, clicked) **Status indicators:** | Status | Meaning | | ---------------- | ----------------------------------------------------------------------- | | 🟢 **Triggered** | Users who received notifications on at least one channel | | 🟠 **Skipped** | Excluded due to missing channels or opt-out | | 🔴 **Failed** | Trigger failed due to errors (for example, template rendering failures) | **Common errors captured:** | Error Type | Examples | | ------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Template rendering failures** | Missing variables, syntax errors | | **Channel computation issues** | No active channels, User Preference opt-outs, Tenant Preference opt-outs | | **Vendor Config issue** | Vendor not configured for the channel and preference category or broadcast not supported for that vendor | **Why execution is skipped:** | Reason | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **Unsubscribed** | User has unsubscribed from that preference category or the required channel in the category | | **Missing channel** | User does not have that channel or that is marked inactive due to hard bounce in the past (for example, no email address, no phone number) | **Navigate from here:** Successful broadcasts will have delivery logs available in [Message logs](/docs/message-logs). Click "View all message logs" next to delivery step to navigate directly to individual message delivery status for all users in the broadcast. Broadcast logs show processing up to trigger. For delivery details, see [Message logs](/docs/message-logs). # Send broadcast notifications with the Go SDK Source: https://docs.suprsend.com/docs/broadcast-go Use the SuprSend Go SDK to send broadcast notifications to a list of subscribers, with code examples for setting up the broadcast request and payload. ## 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 [Preference 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). *** # Send broadcast notifications with the Java SDK Source: https://docs.suprsend.com/docs/broadcast-java Use the SuprSend Java SDK to send broadcast notifications to a list of subscribers, with code examples for setting up the broadcast request and payload. 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 selecting 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 (for example "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 Authenticate SuprSend client SDKs from web and mobile apps using public API keys and signed user JWT tokens for secure end user identification and access. 📘 Some mobile SDKs still use workspace key and workspace secret authentication. SuprSend client SDKs 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 unix epoch in seconds "iat": 1725814228 // token issued unix epoch in seconds. "scope": { "tenant_id": string[] } } ``` If your workspace uses multiple tenants, use scope.tenant\_id to restrict user access to specific tenants. Pass an array of tenant\_id's to allow only those tenants, or `["*"]` to allow all of them. If you omit scope or set it to null, inbox and preferences fall back to the default tenant, while other features like events and user methods use the null scope as-is. > Note: If you pass tenant\_id in scope, you must also pass tenant\_id in the identify method of every SuprSend SDK, else all SDK API calls throw a scoping error. 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 Overview of SuprSend connectors to sync users, events, and lists from third party platforms or export notification logs and analytics to your data warehouse. With SuprSend Connectors, **you can sync events, users, lists from third-party platforms** into SuprSend or **export SuprSend tracked logs and notification data to your data warehouse** for reporting and analysis. ## Usecases of connectors If you already track customer data in these platforms, you can directly sync user data from these platforms within mins. You can sync user cohorts from third-party platforms like Mixpanel in a list in SuprSend and trigger notifications via [broadcast API](/docs/broadcast). Connect your [database](/docs/database) to SuprSend and write SQL queries to sync users and lists in SuprSend. You can use [Amazon S3 connector](/docs/amazon_s3_v2) to export notification logs and metrics to your data warehouse for reporting and analysis. Connect SuprSend to [Datadog](/docs/datadog), [New Relic](/docs/new-relic), or any [OpenTelemetry-compatible](/docs/opentelemetry) platform to monitor API requests, workflow executions, and message delivery in real-time. ## Connector Types There are 3 types of connectors in SuprSend: ### Third party data source Used to sync event and user data from a third-party platform to power notifications or sync user cohorts (lists) in SuprSend. Export user cohorts from Mixpanel into SuprSend and create subscriber [lists](/docs/lists) to trigger notifications via [broadcast API](/docs/broadcast). Sync users and events from Segment to SuprSend to power automated workflows, a low-code way to trigger notifications. ### 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. Export notification logs and metrics in your S3 bucket for internal or in-product reporting and analysis. ### Observability & Monitoring Stream real-time notification metrics from SuprSend to your observability platform via OpenTelemetry (OTLP) - with latency under 10 seconds. Build dashboards, set up alerts, and monitor your notification pipeline alongside your application metrics. Stream `suprsend.*` metrics to Datadog. Includes a pre-built dashboard starter kit. Stream `suprsend.*` metrics to New Relic. Includes a pre-built dashboard starter kit. Connect to any OTLP-compatible platform - Grafana Cloud, Honeycomb, Dynatrace, or your own collector. ### 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. Sync user profiles and create lists in SuprSend on top of Postgres database. Sync user profiles and create lists in SuprSend on top of MySQL database. Sync user profiles and create lists in SuprSend on top of BigQuery database. Need support for another connector? [Please Let us know](https://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ) *** ## 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 staging connector project to your staging workspace, and your production connector project to your production workspace. # Data Transform Source: https://docs.suprsend.com/docs/data-transform Use the data transform node in SuprSend workflows to generate or modify variables on the fly based on input data, conditions, and prior workflow step outputs. 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. for example, 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. for example, 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 Use the SuprSend database connector to write SQL queries on your warehouse, auto sync subscriber lists, and power notifications directly from your source data. 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. for example, 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). *** # Datadog Source: https://docs.suprsend.com/docs/datadog Stream real time SuprSend notification metrics, API requests, and delivery events to your Datadog account via OpenTelemetry OTLP with sub minute latency. The SuprSend Datadog connector streams notification metrics to your Datadog account via [OpenTelemetry (OTLP)](https://opentelemetry.io/) in near real-time - with latency under **1 minute**. It covers three key areas of your notification pipeline: Track every API call hitting SuprSend - success/failure counts, error breakdowns, and request volume over time. Monitor workflow performance, catch execution errors early, and identify top failing workflows. Follow the full message lifecycle - triggered, delivered, seen, clicked - with delivery errors by vendor and channel. **Enterprise plan feature.** The SuprSend Datadog connector is only available on our [Enterprise plan](https://suprsend.com/pricing). ## How it works SuprSend exports `suprsend.*` metrics to Datadog via OTLP over HTTP. You provide your **Datadog OTLP endpoint** and **API key**, choose which events to sync, and SuprSend streams them in near real-time - with latency **under 1 minute**. Once connected you can **build custom dashboards**, **set up monitors and alerts** for delivery failures or API errors, **slice by any dimension** (workspace, tenant, workflow, channel, vendor, template), and **correlate notification health with your infrastructure and APM data** in Datadog. All metrics are tagged by workspace, tenant, workflow, category, channel, vendor, template, node, and error details. See [Reported metrics](#reported-metrics) for the full list. Please refer to your Datadog pricing agreement for information on how custom metrics sent to Datadog are priced for your account. *** ## Installing the connector In your SuprSend dashboard, navigate to [**Connectors**](https://app.suprsend.com/connectors) and click **New Connector**. Fill in the following fields: | Field | Description | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Connector Name** | A label to identify this connector (for example `Datadog Production`) | | **OTLP Endpoint** | Select the site that matches your Datadog account's region from the dropdown | | **API Key** | A Datadog API key from the [API Keys](https://app.datadoghq.com/organization-settings/api-keys) page. We recommend creating a dedicated key for SuprSend | | **Events to sync** | One or more metric events to export (see [Reported metrics](#reported-metrics)) | | **Protocol** | HTTP | Click **Save**. Your metrics will start streaming to Datadog within a few minutes. *** ## Dashboard starter kit Get started quickly with our pre-built Datadog dashboard to visualize your SuprSend metrics in a few clicks. ```json Copy Dashboard Starter Kit JSON theme={"system"} {"title":"SuprSend Notification Dashboard","description":"","layout_type":"ordered","reflow_type":"fixed","tags":[],"template_variables":[{"name":"api_type","prefix":"api_type","available_values":[],"default":"*"},{"name":"workflow_slug","prefix":"workflow_slug","available_values":[],"default":"*"},{"name":"category","prefix":"category","available_values":[],"default":"*"},{"name":"channel","prefix":"channel","available_values":[],"default":"*"},{"name":"vendor","prefix":"vendor","available_values":[],"default":"*"}],"widgets":[{"definition":{"type":"note","content":"## Overview","background_color":"white","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"definition":{"type":"query_value","title":"API Requests \u2014 Total","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Total Requests"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.api_request.total{*}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":0,"y":1,"width":3,"height":2}},{"definition":{"type":"query_value","title":"Workflows \u2014 Total","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Total Executions"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.workflow_execution.total{*}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":3,"y":1,"width":3,"height":2}},{"definition":{"type":"query_value","title":"Notifications \u2014 Triggered","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Triggered"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.triggered{*}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":6,"y":1,"width":3,"height":2}},{"definition":{"type":"note","content":"### Glossary\n\n**Requests** \u2014 API/SDK calls made to SuprSend. Failures at this stage typically mean a bad request or missing assets (for example recipient not found, workflow doesn't exist). A failed request does not trigger a workflow execution.\n\n**Workflow Executions** \u2014 Workflows that were actually executed for a recipient. This excludes skipped runs (for example trigger conditions not met, throttle limits hit). Each execution corresponds to one recipient. Errors here stem from issues like variable mismatches, incorrect vendor configuration on a delivery node, or a failed webhook request.\n\n**Notifications** \u2014 Messages sent by SuprSend to the end vendor (for example SendGrid, Twilio, FCM). For out-of-the-box channels, final delivery is handled by the vendor, and delivery errors are reported back by them. The exception is Inbox, where SuprSend handles delivery end-to-end. Common causes of delivery failures include vendor misconfiguration, invalid or expired channel credentials (for example a stale push token), or client-side blocks (for example mailbox full, DND settings).","background_color":"white","font_size":"12","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":9,"y":1,"width":3,"height":4}},{"definition":{"type":"query_value","title":"API Requests \u2014 Failed","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Failed"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.api_request.errors{*}.as_count()","aggregator":"sum"}],"response_format":"scalar","conditional_formats":[{"comparator":">","value":0,"palette":"red_on_white"},{"comparator":"<=","value":0,"palette":"green_on_white"}]}],"autoscale":true,"precision":0},"layout":{"x":0,"y":3,"width":3,"height":2}},{"definition":{"type":"query_value","title":"Workflows \u2014 Failed","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1 + query2","alias":"Failed"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.workflow_execution.errors{*}.as_count()","aggregator":"sum"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.broadcast_execution.errors{*}.as_count()","aggregator":"sum"}],"response_format":"scalar","conditional_formats":[{"comparator":">","value":0,"palette":"red_on_white"},{"comparator":"<=","value":0,"palette":"green_on_white"}]}],"autoscale":true,"precision":0},"layout":{"x":3,"y":3,"width":3,"height":2}},{"definition":{"type":"query_value","title":"Delivery Errors","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Delivery Failures"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.delivery_errors{*}.as_count()","aggregator":"sum"}],"response_format":"scalar","conditional_formats":[{"comparator":">","value":0,"palette":"red_on_white"},{"comparator":"<=","value":0,"palette":"green_on_white"}]}],"autoscale":true,"precision":0},"layout":{"x":6,"y":3,"width":3,"height":2}},{"definition":{"type":"note","content":"## API Requests & Failures\n**Requests** \u2014 API calls made to SuprSend (workflow triggers, event tracking, user updates etc.). Use the template variables at the top to filter by `api_type`.","background_color":"white","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":5,"width":12,"height":1}},{"definition":{"type":"timeseries","title":"API Requests & Failures Over Time (15 min)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"requests":[{"formulas":[{"formula":"query1","alias":"Total Requests","style":{"palette":"blue","palette_index":4}},{"formula":"query2","alias":"Failed Requests","style":{"palette":"red","palette_index":4}}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.api_request.total{$api_type}.as_count().rollup(sum, 900)"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.api_request.errors{$api_type}.as_count().rollup(sum, 900)"}],"response_format":"timeseries","display_type":"line","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"}}],"yaxis":{"include_zero":true}},"layout":{"x":0,"y":6,"width":5,"height":3}},{"definition":{"type":"toplist","title":"Errors by API Type","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Error Count","limit":{"count":10,"order":"desc"}}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.api_request.errors{$api_type} by {api_type}.as_count()","aggregator":"sum"}],"response_format":"scalar"}]},"layout":{"x":5,"y":6,"width":4,"height":3}},{"definition":{"type":"sunburst","title":"Requests by API Type","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.api_request.total{$api_type} by {api_type}.as_count()","aggregator":"sum"}],"response_format":"scalar"}]},"layout":{"x":9,"y":6,"width":3,"height":3}},{"definition":{"type":"note","content":"## Workflow Executions & Failures\n**Executions** \u2014 Workflow and broadcast runs that process notification logic (routing, templates, channel selection). Use the template variables to filter by `workflow_slug` or `category`.","background_color":"white","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":9,"width":12,"height":1}},{"definition":{"type":"timeseries","title":"Workflow Executions & Errors Over Time (15 min)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"requests":[{"formulas":[{"formula":"query1","alias":"Total Executions"},{"formula":"query2","alias":"Workflow Errors","style":{"palette":"red","palette_index":4}},{"formula":"query3","alias":"Broadcast Errors","style":{"palette":"red","palette_index":2}}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.workflow_execution.total{$workflow_slug,$category}.as_count().rollup(sum, 900)"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.workflow_execution.errors{$workflow_slug,$category}.as_count().rollup(sum, 900)"},{"name":"query3","data_source":"metrics","query":"sum:suprsend.broadcast_execution.errors{$workflow_slug,$category}.as_count().rollup(sum, 900)"}],"response_format":"timeseries","display_type":"line","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"}}],"yaxis":{"include_zero":true}},"layout":{"x":0,"y":10,"width":5,"height":3}},{"definition":{"type":"toplist","title":"Top Failing Workflows","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Errors","limit":{"count":100,"order":"desc"}}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.workflow_execution.errors{$workflow_slug,$category} by {workflow_slug}.as_count()","aggregator":"sum"}],"response_format":"scalar"}]},"layout":{"x":5,"y":10,"width":4,"height":3}},{"definition":{"type":"sunburst","title":"Executions by Workflow","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.workflow_execution.total{$workflow_slug,$category} by {workflow_slug}.as_count()","aggregator":"sum"}],"response_format":"scalar"}]},"layout":{"x":9,"y":10,"width":3,"height":3}},{"definition":{"type":"note","content":"## Notification Delivery & Engagement\n**Delivery** \u2014 Actual notifications sent to end-users via channels (email, SMS, push, WhatsApp, etc.). Use the template variables to filter by `channel` or `vendor`.","background_color":"white","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":13,"width":12,"height":1}},{"definition":{"type":"query_value","title":"Triggered","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Triggered"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.triggered{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":0,"y":14,"width":2,"height":2}},{"definition":{"type":"query_value","title":"Delivered","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Delivered"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.delivered{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":2,"y":14,"width":2,"height":2}},{"definition":{"type":"query_value","title":"Delivery Failures","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Delivery Failures"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.delivery_errors{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar","conditional_formats":[{"comparator":">","value":0,"palette":"red_on_white"},{"comparator":"<=","value":0,"palette":"green_on_white"}]}],"autoscale":true,"precision":0},"layout":{"x":4,"y":14,"width":2,"height":2}},{"definition":{"type":"query_value","title":"Seen","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Seen"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.seen{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":6,"y":14,"width":2,"height":2}},{"definition":{"type":"query_value","title":"Clicked","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Clicked"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.clicked{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":true,"precision":0},"layout":{"x":8,"y":14,"width":2,"height":2}},{"definition":{"type":"query_value","title":"% Delivered","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"(query2 / query1) * 100","alias":"% Delivered"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.triggered{$channel,$vendor}.as_count()","aggregator":"sum"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.messages.delivered{$channel,$vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar"}],"autoscale":false,"precision":1,"custom_unit":"%"},"layout":{"x":10,"y":14,"width":2,"height":2}},{"definition":{"type":"timeseries","title":"Delivered & Delivery Errors Over Time (15 min)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"requests":[{"formulas":[{"formula":"query1","alias":"Triggered"},{"formula":"query2","alias":"Delivered"},{"formula":"query3","alias":"Delivery Errors","style":{"palette":"red","palette_index":4}}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.triggered{$channel,$vendor}.as_count().rollup(sum, 900)"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.messages.delivered{$channel,$vendor}.as_count().rollup(sum, 900)"},{"name":"query3","data_source":"metrics","query":"sum:suprsend.messages.delivery_errors{$channel,$vendor}.as_count().rollup(sum, 900)"}],"response_format":"timeseries","display_type":"line","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"}}],"yaxis":{"include_zero":true}},"layout":{"x":0,"y":16,"width":6,"height":3}},{"definition":{"type":"timeseries","title":"Seen & Clicked Over Time (15 min)","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"requests":[{"formulas":[{"formula":"query1","alias":"Seen"},{"formula":"query2","alias":"Clicked"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.seen{$channel,$vendor}.as_count().rollup(sum, 900)"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.messages.clicked{$channel,$vendor}.as_count().rollup(sum, 900)"}],"response_format":"timeseries","display_type":"line","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"}}],"yaxis":{"include_zero":true}},"layout":{"x":6,"y":16,"width":6,"height":3}},{"definition":{"type":"query_table","title":"Delivery & Engagement \u2014 Workflow Level Breakdown","title_size":"16","title_align":"left","requests":[{"formulas":[{"formula":"query1","alias":"Triggered","cell_display_mode":"number"},{"formula":"(query2 / query1) * 100","alias":"% Delivered","cell_display_mode":"number"},{"formula":"(query3 / query2) * 100","alias":"% Seen / Delivered","cell_display_mode":"number"},{"formula":"(query4 / query2) * 100","alias":"% Clicked / Delivered","cell_display_mode":"number"}],"queries":[{"name":"query1","data_source":"metrics","query":"sum:suprsend.messages.triggered{$workflow_slug,$category,$channel,$vendor} by {workflow_slug,category,channel,vendor}.as_count()","aggregator":"sum"},{"name":"query2","data_source":"metrics","query":"sum:suprsend.messages.delivered{$workflow_slug,$category,$channel,$vendor} by {workflow_slug,category,channel,vendor}.as_count()","aggregator":"sum"},{"name":"query3","data_source":"metrics","query":"sum:suprsend.messages.seen{$workflow_slug,$category,$channel,$vendor} by {workflow_slug,category,channel,vendor}.as_count()","aggregator":"sum"},{"name":"query4","data_source":"metrics","query":"sum:suprsend.messages.clicked{$workflow_slug,$category,$channel,$vendor} by {workflow_slug,category,channel,vendor}.as_count()","aggregator":"sum"}],"response_format":"scalar","sort":{"count":100,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}]},"layout":{"x":0,"y":19,"width":12,"height":5}}]} ``` To import, visit [dashboards](https://app.datadoghq.com/dashboard/lists), click **New Dashboard**, then click the **gear icon** in the top-right corner and choose **Import dashboard JSON**. Paste the JSON above. Use the copy button to copy the JSON. *** ## Setting up alerts Once your metrics are flowing into Datadog, you can set up monitors to get paged when something goes wrong in your notification pipeline. Here are two examples to get you started. ### Example 1 - Alert on delivery error rate spike Alert when message delivery errors exceed a threshold - useful for catching vendor-side failures or misconfigurations before users notice. 1. In Datadog, go to [**Monitors → New Monitor**](https://app.datadoghq.com/monitors/create) and select [**Metric**](https://docs.datadoghq.com/monitors/types/metric/). 2. Under **Choose the detection method**, select **Threshold Alert**. 3. Under **Define the metric**, enter: ``` sum:suprsend.messages.delivery_errors{*}.as_count() ``` Set **Evaluate the bounds for the last** to **5 minutes**. 4. Under **Set alert conditions**: * **Trigger when the evaluated value is**: `above` the threshold * **Alert threshold**: `50` - adjust based on your typical delivery volume. A good starting point is 1–2% of your average message volume per 5 minutes. * **Warning threshold**: `20` - optional, but useful for early visibility before a full alert fires * **If data is missing**: set to **Evaluate as zero** 5. Under [**Configure notifications & automations**](https://docs.datadoghq.com/monitors/notify/), give the monitor a name (for example `SuprSend — Delivery Error Spike`) and add a message with context: ``` Delivery errors are elevated - {{value}} errors in the last 5 minutes. Investigate: https://app.suprsend.com/logs ``` Add your notification recipients (email, Slack, PagerDuty, etc.) using **@ mentions** in the message body. 6. Click **Save**. ### Example 2 - Alert on anomalous message delivery drop Alert when delivered message volume drops unexpectedly - useful for catching silent failures where messages stop flowing without a corresponding error spike. 1. In Datadog, go to [**Monitors → New Monitor**](https://app.datadoghq.com/monitors/create) and select **Metric**. 2. Under **Choose the detection method**, select [**Anomaly Detection**](https://docs.datadoghq.com/monitors/types/anomaly/). 3. Under **Define the metric**, enter: ``` sum:suprsend.messages.delivered{*}.as_count() ``` Set **Evaluate the bounds for the last** to **15 minutes**. 4. Under **Set alert conditions**: * **Trigger when the evaluated values have been**: `below` the bounds - so you're only alerted when delivery drops, not when it spikes * **Alert threshold**: `75` - alert when 75% or more of the values in the window fall below the expected range * Under **Advanced options → Anomaly detection algorithm options**: set deviations to `2` and keep the algorithm as **basic** 5. Under [**Configure notifications & automations**](https://docs.datadoghq.com/monitors/notify/), give the monitor a name (for example `SuprSend — Delivery Drop Anomaly`) and add a message: ``` Message delivery has dropped below expected levels - {{value}} messages delivered in the last 15 minutes. Investigate: https://app.suprsend.com/logs ``` Add your notification recipients using **@ mentions** in the message body. 6. Click **Save**. Scope either monitor to a specific workflow or channel by adding `workflow_slug:your-workflow` or `channel:email` to the metric query in step 3. This lets you create targeted monitors for your most critical notification flows. *** ## Reported metrics SuprSend streams the following `suprsend.*` counter metrics to Datadog. All counters are monotonically increasing and represent cumulative counts from the time the connector is enabled. ### API Requests Track the volume and health of every API call made to SuprSend. | Metric | Description | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `suprsend.api_request.total` | Total number of API requests received by SuprSend. Use the `status` tag to break down by outcome - `sent`, `in_progress`, `success`, `failed`, or `partial_failure`. | | `suprsend.api_request.errors` | API requests that failed to process due to an error. Group by `api_type` to identify which endpoint is failing, and by `error_severity` to prioritize investigation. | **Tags on API Request metrics** | Tag | Description | Example values | | ---------------- | --------------------------------------------------- | ------------------------------------------------------------- | | `api_type` | The SuprSend API endpoint that was called | `workflow_trigger`, `broadcast_trigger`, `user_edit`, `event` | | `method` | HTTP method of the request | `POST`, `GET`, `PUT`, `PATCH` | | `status` | Outcome of the request | `sent`, `in_progress`, `success`, `failed`, `partial_failure` | | `ws_uid` | Identifier of the SuprSend workspace (environment) | `your_ws_uid` | | `ws_slug` | Slug of the SuprSend workspace. Eg. `staging` | | | `tenant_id` | Tenant ID if the request is scoped to a tenant | `tenant_abc` | | `error_severity` | Severity level of the error (on error metrics only) | `critical`, `warning`, `info` | ### Workflow Executions Monitor how your workflows and broadcasts are performing and quickly spot execution failures. | Metric | Description | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `suprsend.workflow_execution.total` | Total number of workflow executions started. Workflow executions are counted per-recipient. Use `workflow_slug` and `category` tags to drill down into specific workflows. | | `suprsend.workflow_execution.errors` | Workflow executions that failed to process due to an error. | | `suprsend.broadcast_execution.errors` | Broadcast executions that failed to process due to an error. | **Tags on Workflow Execution metrics** | Tag | Description | Example values | | ---------------- | --------------------------------------------------- | ------------------------------------ | | `workflow_slug` | Slug of the workflow that was executed | `welcome-flow`, `order-confirmation` | | `trigger_type` | How the workflow was triggered | `api`, `event` | | `category` | Preference category of the workflow | `transactional`, `promotional` | | `root_category` | Top-level category when categories are nested | `marketing` | | `ws_slug` | Slug of the SuprSend workspace | `my-workspace` | | `ws_uid` | Identifier of the SuprSend workspace (environment) | `your_ws_uid` | | `tenant_id` | Tenant ID if the workflow is scoped to a tenant | `tenant_abc` | | `error_severity` | Severity level of the error (on error metrics only) | `critical`, `warning`, `info` | ### Messages Follow the full lifecycle of every message - from trigger through delivery, open, and click - and monitor delivery failures by vendor and channel. | Metric | Description | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `suprsend.messages.triggered` | Total number of messages triggered. Slice by `channel` and `vendor` to see volume distribution. | | `suprsend.messages.delivered` | Total number of messages delivered to the recipient. | | `suprsend.messages.delivery_errors` | Messages that failed to deliver due to an error. Use `vendor` / `channel` tags to isolate the error source. | | `suprsend.messages.seen` | Total number of messages seen by the recipient. Requires vendor webhook callbacks to be configured (see note below). | | `suprsend.messages.clicked` | Total number of messages clicked by the recipient. Requires vendor webhook callbacks to be configured (see note below). | **Tags on Message metrics** | Tag | Description | Example values | | --------------- | -------------------------------------------------- | ---------------------------------------------------------------- | | `ws_slug` | Slug of the SuprSend workspace | `my-workspace` | | `ws_uid` | Identifier of the SuprSend workspace (environment) | `your_ws_uid` | | `tenant_id` | Tenant ID if the message is scoped to a tenant | `tenant_abc` | | `category` | Preference category of the parent workflow | `transactional`, `promotional` | | `channel` | Delivery channel used for the message | `email`, `sms`, `push`, `whatsapp`, `inbox`, `slack`, `ms_teams` | | `vendor` | Vendor used to deliver the message | `mailgun`, `twilio`, `firebase`, `sendgrid`, etc. | | `workflow_slug` | Slug of the workflow that generated the message | `welcome-flow`, `order-confirmation` | * All metrics are scoped to the SuprSend environment (for example `production`, `staging`) via the `ws_uid` tag. * For delivery, seen, and click metrics to be populated for Email, SMS, and WhatsApp, configure `https://hub.suprsend.com/webhook/*` as a callback URL in your vendor dashboard. See the [vendor integration docs](/docs/vendors) for per-vendor instructions. * For error details, the [SuprSend dashboard](https://app.suprsend.com) provides more granular information via [Logs](/docs/logging) and [Analytics](/docs/analytics). *** ## Disabling the connector To pause metrics streaming, open the connector from the [**Connectors**](https://app.suprsend.com/connectors) page and toggle **Enable sync** off. Re-enable it at any time to resume. # Delay Source: https://docs.suprsend.com/docs/delay Use the delay node in SuprSend workflows to pause execution for a fixed, dynamic, or specific time before moving to the next step for reminders and follow ups. 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, for example 10 minutes before task due time). Fixed delay is defined in your workflow form as `xxd xxh xxm xxs` 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. for example, 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 (for example 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. for example, 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 (for example 30m for 30 minutes). This can be: * **Fixed** (for example always 30 minutes before). * **Dynamic**, where the value is retrieved from the payload (for example 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 (for example 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` *** # Delivery- Multi-Channel Source: https://docs.suprsend.com/docs/delivery-multi-channel Use the multi-channel delivery node to notify users across email, SMS, push, WhatsApp and chat channels in a single SuprSend 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 within the preference category (defined in workflow settings). You can check user preference status using [get user preference API](/reference/get-user-category-preferences). **Category-level opt-out is checked at trigger time.** If a user has opted out of the entire preference category, the workflow is skipped before execution begins — the workflow run does not start. The delivery node only evaluates **channel-level** preferences (whether the user has opted into the specific channel within the category). See [Preference Evaluation](/docs/preference-evaluation) for details. ### 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. for example, 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 Configure single channel delivery nodes for email, SMS, WhatsApp, mobile push, web push, Slack, MS Teams and inbox messages in SuprSend 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 within the preference category (defined in workflow settings). You can check user preference status using [get user preference API](/reference/get-user-category-preferences). **Category-level opt-out is checked at trigger time.** If a user has opted out of the entire preference category, the workflow is skipped before execution begins — the workflow run does not start. The delivery node only evaluates **channel-level** preferences (whether the user has opted into the specific channel within the category). See [Preference Evaluation](/docs/preference-evaluation) for details. ### 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. for example, 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, test, and publish a notification workflow on the SuprSend dashboard, including how to add nodes, branches, and delivery channels. ## Prerequisites [Understanding the basics of workflows](/docs/workflows) ## Creating a new workflow Select the button on the workflow page to create a 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. * [Preference Category](/docs/notification-category) is used to apply user preferences to the workflow. While creating the workflow, you can select any preference 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. ## 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 add 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 exits a [list](/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 latter 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 sends the notification to active user channels whose template content is live. [Channel-level preferences](/docs/user-preferences) are evaluated at this stage — if the user has opted out of a specific channel within the category, that channel is skipped. Category-level opt-outs are checked earlier, at [trigger time](/docs/preference-evaluation), so the workflow doesn't run at all for users who have opted out of the entire category. Corresponding preference details 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://join.slack.com/t/suprsendcommunity/shared_invite/zt-3932rw936-XNWY1RC8bsffh4if4ZyoXQ), and we'll prioritize it. ### 4. Branch nodes Branches split the workflow execution into parallel flows. We currently support the 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 subscription. ### 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. For example - `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. For example - `Send 1 hour before the appointment`. | | **Preference Category** | *\*mandatory* | [Preference category](/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 preference 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. For example, 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](/docs/throttle). | ### Change node name and description To modify the name and description of a node, select 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. ## Disabling a workflow You can disable a workflow from the workflow details page to stop it from accepting new triggers without deleting its design or history. Disabling is reversible, you can re-enable the workflow at any time and it will resume accepting triggers immediately. **Disabling only blocks new triggers, in-flight runs continue to completion.** Any workflow runs that were already in progress when you disabled the workflow (e.g., waiting on a [delay](/docs/delay), [batch](/docs/batch), [digest](/docs/digest), or [wait until](/docs/wait-until) node) will continue executing through their remaining steps and deliver as designed. Only new triggers received after the workflow is disabled are blocked. *** # API Keys and Secrets Source: https://docs.suprsend.com/docs/developer/api-keys Generate API keys, manage workspace secrets and pick the right authentication method to securely integrate SuprSend into your application backend. 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 create, version and deploy templates, workflows and notification assets across workspaces. ## Overview The **SuprSend Management API** lets you programmatically manage assets such as workflows, templates, schemas, translations, and **workspace-level resources** (create workspaces, manage workspace API keys, public keys, and signing keys). This API is designed for **asset management** and **cross-workspace operations** (for example promoting workflows from Staging → Production). The Management API only covers the **dashboard / control-plane** surface (assets and workspace credentials). For user delivery, triggering workflows, tenants, or preferences, use the [REST API (Hub)](/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 collections * **[SuprSend workspace collection](https://www.postman.com/suprsend/workspace/suprsend/collection/27786422-d77a13c1-8f59-406d-9669-078a10d52521)** - broad set of APIs for trying SuprSend quickly. * **[Management APIs - workspaces & keys](https://go.postman.co/collection/32180119-c6953766-3d06-4d59-b73d-b11b09bde33f?source=collection_link)** - matches the routes for listing/creating workspaces, listing templates, and managing workspace API keys, public keys, and signing keys (use your **management** base URL on self-hosted: `https://` and `Authorization: ServiceToken `). For base URL and authentication details, see the [Management API overview](/docs/management-api-overview). # 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 and efficiently. 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 workspaces 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. Stream real-time notification metrics to any OTLP-compatible platform - Grafana Cloud, Honeycomb, Dynatrace, or your own collector. Stream `suprsend.*` metrics to Datadog with a pre-built dashboard starter kit. Stream `suprsend.*` metrics to New Relic with a pre-built dashboard starter kit. ## 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 Try SuprSend APIs from the official Postman collection, with pre-configured endpoints, sample payloads and environment variables for quick testing. 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 Use SuprSend REST APIs to sync user profiles, trigger workflows, send broadcasts and deliver multi-channel notifications at production 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** (for example, 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 Browse SuprSend server and client SDKs for Node, Python, Go, Java, PHP, iOS, Android, React and more to integrate notifications into your app. 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 # Versioning and Support Policy Source: https://docs.suprsend.com/docs/developer/sdk-versioning-policy Understand SuprSend SDK versioning under SemVer, deprecation timelines and the support windows for major, minor and patch server SDK releases. ## Versioning Policy SuprSend's SDK versioning policy follows 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.** Major. Introduces breaking changes that are not backward compatible. These may include renamed or removed methods, structural or conceptual redesigns, or change in underlying framework or library. Eg. Changing authentication mechanism in client SDKs.
Updating to a new major version typically requires code changes or migration. We don't deprecate older versions support so you don't have to worry about breaking changes in older versions. * **Minor.** New features that are backward compatible. Eg. Adding new methods or introducing more capabilities in existing methods. * **Patch.** Backward-compatible bug fixes. Eg. Enhanced documentation with comprehensive in-app feed integration guides. 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 do not receive ongoing updates or enhancements. * While most releases are backward compatible, major releases may introduce breaking changes. In such occurrences, we clearly mention this in changelog, documentation and provide migration guides to help you upgrade smoothly with minimal code changes. * We recommend keeping your SDKs up to date to benefit from the latest security improvements, enhanced authorization mechanisms (especially for frontend SDKs), better performance, and access to new API capabilities. * SuprSend’s release cycle is aligned with product development milestones, ensuring that SDK and API versions evolve together. This synchronization guarantees consistent behavior across the platform and minimizes the risk of unexpected breaking changes. ### Migration Guides We provide migration guides to help you upgrade from older major SDK versions. You can find them in our documentation: * [React migration guide](/docs/react-migration-guide) * [Web SDK migration guide](/docs/migration-guide-from-v1) ## SDK Language and Framework Support SuprSend SDKs are actively maintained across all supported languages and frameworks. We currently support all listed SDKs without any planned deprecations. In the unlikely event that a language or framework version is deprecated, we will provide **advance notice** and a clear **migration path** to ensure sufficient time for transition. ### Supported Versions | Language / Framework | Minimum Supported Version | | ------------------------ | ------------------------- | | **JavaScript / Node.js** | 14+ | | **Python** | 3.9+ | | **Java** | 8+ | | **Go** | 1.18+ | | **Swift** | 5.0+ | | **Kotlin** | 1.5+ | | **React Native** | 0.60+ | | **Flutter** | 2.0+ | ## 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. **Backend SDKs:** New versions are primarily released for new API additions or enhancements. SDK changes are generally backward compatible, except in case of major releases where breaking changes may occur. Major releases are communicated well in advance, and existing APIs remain supported for a reasonable transition period. **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 and all updates are logged in [changelog](/docs/developer/versioning/sdk-changelog). ## Best Practices
• Test in staging workspace 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. # Service Token Source: https://docs.suprsend.com/docs/developer/service-tokens Create and rotate Service Tokens to authenticate SuprSend Management API requests and securely automate deployments from CI and scripts. 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** (for example, `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 Enable test mode in SuprSend to preview and debug workflow runs, templates and channel delivery safely without sending notifications 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** (for example, email, SMS).\ All notifications sent to non-test users on that channel will be redirected here (for example, `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 Browse the full SuprSend SDK changelog with release notes, new features, bug fixes and breaking changes across server and client SDK versions. 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.19.8

v0.19.8

Jul 29, 2026

Changes:

v0.19.7

Jul 23, 2026

Changes:

  • Added tenant user mapping support

v0.19.6

Jul 14, 2026

Changes:

v0.19.5

Jul 13, 2026

Changes:

v0.19.4

Jul 9, 2026

Changes:

v0.19.3

May 19, 2026

Changes:

v0.19.2

May 18, 2026

Changes:

v0.19.1

May 15, 2026

Changes:

v0.19.0

May 15, 2026

Changes:

v0.18.1

Mar 27, 2026

🔐 Package signing

  • Every release now ships with a SHA-256 checksum and a Cosign cryptographic signature — verify the package was built by SuprSend and hasn't been modified in transit before installing.
  • PyPI listing now carries a Verified details badge, independently confirming the GitHub repository is owned by the same account publishing the package — protecting against typosquatting.
  • No breaking changes. Existing integrations require no code changes.

→ Verify package signature

v0.18.2

May 13, 2026

Features:

  • Added Messages API support - supr\_client.messages.list(...) and supr\_client.messages.bulk\_update(...)

v0.18.1

Mar 13, 2026

Changes:

v0.18.0

Feb 21, 2026

Changes:

  • Made libmagic and python-magic optional dependencies

v0.17.0

Feb 20, 2026

Bug Fixes:

  • Fixed logging library possibly overwriting application log levels causing silent log loss

v0.16.0

Nov 7, 2025

Features:

  • Set locale method

Notes: Added set locale method for better localization support.

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.15.1

v1.15.1

May 19, 2026

Bugfix:

  • Fixed issue related to babel upgrade

v1.15.0

May 19, 2026

Features:

  • Added user-agent support in SDK
  • Added Messages APIs and fixed dependabot issues

v1.14.0

May 18, 2026

Changes:

  • Added GitHub Actions workflow to build and publish to the NPM registry
  • Fixed npm publish commands in release workflow
  • Added user-agent code

v1.13.3

Mar 14, 2026

Bugfix:

  • Updated package-lock.json to fix corrupt files issue

v1.13.2

Feb 14, 2026

Improvements:

  • Bumped axios version to fix dependabot issues

v1.13.1

Oct 1, 2025

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.14.0

v0.14.0

May 15, 2026

Changes:

  • Standardized user-agent and bumped version

v0.13.2

Mar 27, 2026

🔐 Package signing

  • Bumped patch version for signed release — every release now ships with a SHA-256 checksum and a Cosign cryptographic signature. Verify the artifact you're pulling into your build was produced by SuprSend's release pipeline and is byte-for-byte unmodified.
  • No breaking changes. Existing integrations require no code changes.

→ Verify package signature

v0.13.0

Nov 7, 2025

Features:

  • Add locale support to ObjectEdit, Subscriber, and UserEdit classes

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.12.0

v0.12.0

Aug 2, 2026

Features:

  • FCM package\_name / bundle\_id support
  • Added tenant user mapping

v0.11.0

Jul 9, 2026

Features:

  • Category preference digest schedule and properties support

v0.10.1

May 25, 2026

Changes:

  • Message API README documentation
  • HTTP context support

v0.10.0

May 15, 2026

Changes:

  • Replaced deprecated tenant APIs
  • Added Message APIs — GET list, bulk patch status, GET message details
  • Added user-agent support

v0.9.0

Nov 7, 2025

Features:

  • Add locale support to user and object edit APIs

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: v5.0.0

v5.0.0

Aug 7, 2026

Features:

  • Added tenant scoping support

v4.4.0

Jun 29, 2026

Features:

  • Preference digest schedule and properties support

v4.3.1

Jun 12, 2026

Bugfix:

  • Fixed update webpush subscription bug in non-browser environments

v4.3.0

May 19, 2026

Changes:

  • Fixed dependabot issues

v4.2.0

May 15, 2026

Changes:

  • User-agent changes and createUser flag in identify

v4.1.2

Apr 28, 2026

Changes:

  • Tenant filter in channel preference

v4.1.1

Feb 27, 2026

Bugfix:

  • Fixed data mixup issue in stores by adding abort controller on active store changes

v4.1.0

Dec 4, 2025

Features:

  • Translations in preferences

v4.0.4

Oct 16, 2025

Bugfix:

  • Fixed socketio issue during authentication

v4.0.3

Oct 12, 2025

Bugfix:

  • Socket connection authentication failed issue after refresh

v4.0.2

Sep 29, 2025

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

v3.0.3

Apr 22, 2025

Changes:

  • Updated documentation

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: v1.0.0

v1.0.0

Aug 7, 2026

Features:

  • Added tenant scoping support

v0.5.0

May 19, 2026

Changes:

  • Fixed dependabot issues

v0.4.0

May 18, 2026

Changes:

  • Added user-agent support in SDK

v0.3.8

Apr 28, 2026

Changes:

  • Updated React core SDK

v0.3.7

Feb 27, 2026

Bugfix:

  • Updated react-core SDK version to fix filter mixup issue

v0.3.6

Feb 6, 2026

Features:

  • Support to disable automatic seen tracking

v0.3.5

Dec 4, 2025

Changes:

  • Updated react core SDK to integrate preference translations

v0.3.4

Oct 16, 2025

Bugfix:

  • Fixed socketio issue with authentication

v0.3.3

Oct 14, 2025

Changes:

  • Added class names to elements

v0.3.2

Oct 12, 2025

Bugfix:

  • Socket connection authentication failed issue after refresh

v0.3.1

Sep 29, 2025

Changes:

  • Version updated and Socket.io connection config changes

v0.3.0

Aug 19, 2025

Features:

  • Shadow DOM support
  • Custom infinite scroll component

v0.2.1

Jul 28, 2025

Features:

  • Webpush add token optimisation

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

Changes:

  • 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-swift-sdk

Latest Release: 2.0.0

2.0.0

Jul 28, 2026

Changes:

  • Added tenant scoping at the global level — user-tenant mapping support for per-tenant push tokens, emails, and custom properties
  • Miscellaneous improvements

1.2.0

May 29, 2026

Changes:

  • Inbox feature
  • Example project cleanup

1.1.0

May 25, 2026

Changes:

  • Preferences revamp — added support for locale, tags, and tenant-level preferences in getPreferences
  • User-agent header updates
  • Bundle ID tracking

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.2

v2.5.2

Feb 17, 2026

Changes:

  • Dependency bump — form-data from 3.0.1 to 3.0.4

v2.5.1

Oct 27, 2025

Bugfix:

  • Removed jcenter and changed compileSdkVersion to 30

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: v3.0.0

Migration guide: Flutter SDK v2 → v3

v3.0.0

Jul 30, 2026

Features:

  • Native Android cleanup
  • Added deeplink custom schema support
  • Added tenant support
  • License update

v2.5.1

Dec 22, 2025

Changes:

  • Upgraded native SuprSend Android SDK version

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 Review the SuprSend SDK versioning and support policy, including SemVer rules, deprecation windows and which SDK versions receive active updates. 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 (for example, renaming SDK exception classes). **Minor.** New features that are backward compatible (for example, adding new methods or optional parameters). **Patch.** Backward-compatible bug fixes (for example, 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 workspace 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. # Digest Source: https://docs.suprsend.com/docs/digest Use the Digest node to batch repeated alerts and send a scheduled summary of notifications to users, reducing noise and notification fatigue. 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). ## 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. For example, 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). For example, 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 (for example Mondays and Wednesdays of the week) * every month on selected days of the month (for example 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. For example, 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 (for example 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). for example, `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 preference 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#the-variables-panel) 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. for example, 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 Follow DLT registration guidelines to approve sender IDs and template IDs for sending compliant transactional and promotional 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 Configure an email vendor, verify your sending domain and send your first transactional email notification through SuprSend in a few minutes. ### 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 are 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](/docs/handlebars-helpers) as our templating language. You can add dynamic content as `{{var}}`; this syntax HTML-escapes values by default to help prevent injection. For URLs and values which can have special characters, you can use triple curly braces `{{{var}}}` to avoid HTML-escaping. 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). 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 workspace, 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/vendors) 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 Source: https://docs.suprsend.com/docs/email-template Design email templates using the drag-and-drop editor, raw HTML, or plain text - with variables, tenant branding, display conditions, and email markup. The email editor has three modes - **Design Editor** (drag-and-drop), **HTML Editor** (raw code), and **Plain Text** (text fallback). Plain Text is always sent alongside HTML - you don't choose between them, both go out. *** ## HTML Editor For full control, switch to the **HTML** tab. Write or paste HTML directly - the live preview renders on the right. Supports Handlebars variables inside the HTML (`{{order_id}}`, `{{{tracking_url}}}`). Email clients strip many HTML/CSS features: * **Gmail** - strips `