<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikita Navral</title>
    <description>The latest articles on DEV Community by Nikita Navral (@nikita_navral).</description>
    <link>https://dev.to/nikita_navral</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F956914%2F6265910d-ba2b-48fc-863e-6aed19eadf88.png</url>
      <title>DEV Community: Nikita Navral</title>
      <link>https://dev.to/nikita_navral</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikita_navral"/>
    <language>en</language>
    <item>
      <title>One Platform for Every Notification Channel, and a CLI to Manage It All From Your Terminal</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Wed, 21 Oct 2026 10:42:52 +0000</pubDate>
      <link>https://dev.to/suprsend/one-platform-for-every-notification-channel-and-a-cli-to-manage-it-all-from-your-terminal-1lg6</link>
      <guid>https://dev.to/suprsend/one-platform-for-every-notification-channel-and-a-cli-to-manage-it-all-from-your-terminal-1lg6</guid>
      <description>&lt;p&gt;If you've ever built notifications into a product, you know it starts with one email and ends with a small platform: multiple channels, multiple vendors, templates, user preferences, retries, analytics, and a PM asking for copy changes every week.&lt;/p&gt;

&lt;p&gt;This post is a quick introduction to &lt;strong&gt;SuprSend&lt;/strong&gt;, the notification infrastructure we work on, and then a closer look at the &lt;strong&gt;SuprSend CLI&lt;/strong&gt;, which lets you manage all of it from your terminal and your Git repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SuprSend?
&lt;/h2&gt;

&lt;p&gt;SuprSend is a notification infrastructure platform. Instead of wiring up each channel and vendor yourself, you integrate once and manage the notification layer in one place.&lt;/p&gt;

&lt;p&gt;It covers the pieces most teams end up building on their own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple channels:&lt;/strong&gt; email, SMS, push, in-app inbox, WhatsApp, Slack and more, through one integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows:&lt;/strong&gt; the logic for when, to whom and on which channel a notification goes out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Templates:&lt;/strong&gt; content for each channel, with variables and personalization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User preferences:&lt;/strong&gt; letting end users choose what they get and where&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy:&lt;/strong&gt; per-customer branding and settings for B2B products&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; delivery and engagement data in one place across channels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple: your engineers shouldn't spend sprints reinventing notification plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap: great platform, but changes happen in a dashboard
&lt;/h2&gt;

&lt;p&gt;Most of these assets (templates, workflows, preferences) are authored in a dashboard. That's friendly for PMs and marketers, but it creates a mismatch for engineering teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source of truth isn't in your repo&lt;/li&gt;
&lt;li&gt;Staging and production can drift apart&lt;/li&gt;
&lt;li&gt;Changes don't go through pull requests&lt;/li&gt;
&lt;li&gt;Promoting to production often means someone needs production dashboard access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Engineers manage everything else from code: app logic lives in a repo, ships through PRs, deploys through CI and rolls back through Git. Notifications should be able to work that way too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the SuprSend CLI
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;SuprSend CLI&lt;/strong&gt; brings the platform into the terminal. Every core asset (&lt;strong&gt;workflows, templates, schemas, events, preference categories and translations&lt;/strong&gt;) can be managed through commands that fit into scripts and CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Install on macOS or Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; suprsend/tap/suprsend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then authenticate with a service token. For Windows and CI environments, download the binary from GitHub Releases, or build from source.&lt;/p&gt;

&lt;p&gt;Each asset can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;listed&lt;/li&gt;
&lt;li&gt;pulled to local files&lt;/li&gt;
&lt;li&gt;edited in your editor&lt;/li&gt;
&lt;li&gt;pushed back&lt;/li&gt;
&lt;li&gt;committed from draft to live&lt;/li&gt;
&lt;li&gt;synced between workspaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also activate or deactivate workflows from the shell, which is useful for shipping a new workflow, or for taking a misbehaving one offline during an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it fits the tools you already use
&lt;/h2&gt;

&lt;p&gt;The CLI is built to slot into your setup rather than replace it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your editor
&lt;/h3&gt;

&lt;p&gt;Pulling an asset writes it as readable JSON into your project. Edit it in your IDE with your usual linting, syntax highlighting and AI completions. When you push it back, it's validated against SuprSend schemas before it's saved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git
&lt;/h3&gt;

&lt;p&gt;Once assets are local files, they live in your repo like any other code. Commits show who changed what, diffs show exactly what shifted, and a revert rolls back a bad change in seconds. Your Git log becomes your notification change history.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI/CD
&lt;/h3&gt;

&lt;p&gt;Every command is scriptable. A GitHub Action, CircleCI job or any other pipeline can run pulls, pushes and syncs as deploy steps, so notification changes ship on the same cadence as your app code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things you can do with it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Promote staging to production in one command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;suprsend &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--from&lt;/span&gt; staging &lt;span class="nt"&gt;--to&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because your pipeline does the promotion, nobody needs UI access to the production workspace to ship a change. Production access stays locked down, and changes flow through reviewed pull requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Get type-safe payloads
&lt;/h3&gt;

&lt;p&gt;Generate native type definitions from your SuprSend schemas and drop them into your SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;suprsend schema generate-types &lt;span class="nt"&gt;--schema&lt;/span&gt; order-confirmation &lt;span class="nt"&gt;--output&lt;/span&gt; ./types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A wrong payload key then fails in your IDE, in CI and in your build, before any send is attempted. The schema becomes a guardrail instead of a runtime surprise.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Manage workflows and templates as code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap a new workspace&lt;/strong&gt; by provisioning templates from a script instead of building them one by one in the UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrate templates&lt;/strong&gt; from another tool or an internal codebase without recreating them by hand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review changes in PRs&lt;/strong&gt; like any other code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open source and verifiable
&lt;/h2&gt;

&lt;p&gt;The CLI is &lt;strong&gt;open source and free to use&lt;/strong&gt;. Install via Homebrew, grab the binary from GitHub Releases, or build from source. If you're in a security-sensitive environment, you can verify the binary's signature with &lt;strong&gt;Cosign&lt;/strong&gt; before installing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📖 &lt;a href="https://docs.suprsend.com/reference/cli-intro?_gl=1*1vupz8t*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTI1NzkxNjQkbzIwJGcxJHQxNzkyNTc5MTY5JGo1NSRsMCRoMTM5NDczNzY1Ng.." rel="noopener noreferrer"&gt;CLI docs&lt;/a&gt;: install and run your first command&lt;/li&gt;
&lt;li&gt;🧰 &lt;a href="https://docs.suprsend.com/reference/cli-intro?_gl=1*oexhsz*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTI1NzkxNjQkbzIwJGcxJHQxNzkyNTc5Mjg1JGo2MCRsMCRoMTM5NDczNzY1Ng..#available-commands" rel="noopener noreferrer"&gt;All CLI commands&lt;/a&gt;: the full surface&lt;/li&gt;
&lt;li&gt;🌐 &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt;: the platform itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Over to you:&lt;/strong&gt; how does your team manage notification templates today? Dashboard, Git, or a mix? Tell us in the comments.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Clicking Through a Dashboard to Deploy Notification Changes</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:02:31 +0000</pubDate>
      <link>https://dev.to/nikita_navral/stop-clicking-through-a-dashboard-to-deploy-notification-changes-ej</link>
      <guid>https://dev.to/nikita_navral/stop-clicking-through-a-dashboard-to-deploy-notification-changes-ej</guid>
      <description>&lt;p&gt;Think about how you ship a normal change at work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Branch.&lt;/li&gt;
&lt;li&gt;Pull request.&lt;/li&gt;
&lt;li&gt;Review.&lt;/li&gt;
&lt;li&gt;CI runs.&lt;/li&gt;
&lt;li&gt;Deploy.&lt;/li&gt;
&lt;li&gt;If it breaks, &lt;code&gt;git revert&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now think about how a notification change ships: someone logs into a dashboard, edits a template, clicks save, and hopes staging and production still match.&lt;/p&gt;

&lt;p&gt;Same company, same engineers, completely different standards. We wanted to close that gap, so this post is about treating notification assets like what they are: &lt;strong&gt;config that belongs in your repo.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with dashboard-only notifications
&lt;/h2&gt;

&lt;p&gt;When templates, workflows and preferences live only in a UI, a few things quietly go wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The source of truth isn't in your repo.&lt;/strong&gt; The real version lives somewhere else, and nobody can &lt;code&gt;git blame&lt;/code&gt; it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environments drift.&lt;/strong&gt; Staging and production slowly stop matching, and nobody notices until a send goes wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There's no review trail.&lt;/strong&gt; Who changed that subject line, and why? You find out by asking around.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production access gets handed out widely.&lt;/strong&gt; If the only way to ship a change is the production dashboard, many people need access to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is a notification-specific problem. It's what happens to any config that lives outside version control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "notifications as code" looks like
&lt;/h2&gt;

&lt;p&gt;The idea is simple: pull your notification assets down as local files, manage them like the rest of your code, and push them back through a pipeline.&lt;/p&gt;

&lt;p&gt;With the &lt;a href="https://docs.suprsend.com/reference/cli-intro?_gl=1*wpra63*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTIzOTY2NTUkbzE4JGcxJHQxNzkyMzk2ODQzJGo0MSRsMCRoMTY1NDY4NTE2NQ..#available-commands" rel="noopener noreferrer"&gt;SuprSend CLI&lt;/a&gt;, every core asset can be handled from the terminal: &lt;strong&gt;workflows, templates, schemas, events, preference categories and translations.&lt;/strong&gt; Each one can be listed, pulled to local files, edited, pushed back, committed from draft to live, and synced between workspaces. You can also activate or deactivate a workflow straight from the shell, which is handy for shipping a new one, or for pulling a misbehaving one offline during an incident.&lt;/p&gt;

&lt;p&gt;Install (macOS or Linux):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; suprsend/tap/suprsend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticate with a service token, and you're ready to run commands next to the rest of your engineering work. (On Windows or in CI, grab the binary from GitHub Releases, or build from source.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things this unlocks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Edit in your own editor
&lt;/h3&gt;

&lt;p&gt;Pulling an asset writes it to your project directory as readable JSON. You edit it in your IDE with your usual linting, syntax highlighting and AI completions, then push it back. Pushes are validated against SuprSend schemas before anything is saved, so a malformed file is rejected before it reaches your workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Git becomes your notification changelog
&lt;/h3&gt;

&lt;p&gt;Once assets are files, they get everything Git gives you for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commits show &lt;strong&gt;who&lt;/strong&gt; changed what&lt;/li&gt;
&lt;li&gt;Diffs show &lt;strong&gt;exactly&lt;/strong&gt; what changed&lt;/li&gt;
&lt;li&gt;A revert undoes a bad change in seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your Git log becomes your notification change history, with no separate doc to keep updated.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Deploy through CI like any other change
&lt;/h3&gt;

&lt;p&gt;Every CLI command is scriptable, so GitHub Actions, CircleCI or any other pipeline can run pulls, pushes and syncs as deploy steps.&lt;/p&gt;

&lt;p&gt;Here's an illustrative GitHub Actions job that promotes assets from staging to production when a PR merges to &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;promote-notifications&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="c1"&gt;# Install the CLI (see the docs for the binary / Homebrew options in CI)&lt;/span&gt;
      &lt;span class="c1"&gt;# Authenticate with your service token stored as a CI secret&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Promote staging to production&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suprsend sync --from staging --to production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This is a sketch to show the shape of the workflow. Check the CLI docs for the exact install and auth steps in your CI environment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The result: notification changes ship on the same cadence as your application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment promotion without production access
&lt;/h2&gt;

&lt;p&gt;This one is a security win. Moving tested assets from staging to production is a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;suprsend &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--from&lt;/span&gt; staging &lt;span class="nt"&gt;--to&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the pipeline does the promotion, nobody needs UI access to the production workspace to ship a change. Production access stays locked down, and changes only flow through reviewed pull requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type-safe payloads (fail in your IDE, not in prod)
&lt;/h2&gt;

&lt;p&gt;Here's the part developers tend to like most. You can generate native type definitions from your SuprSend schemas and drop them into your SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;suprsend schema generate-types &lt;span class="nt"&gt;--schema&lt;/span&gt; order-confirmation &lt;span class="nt"&gt;--output&lt;/span&gt; ./types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a wrong payload key fails in your IDE, then in CI, then in your build, before any send is attempted. Instead of discovering a bad key at runtime when a user doesn't get their order confirmation, the schema becomes a guardrail at compile time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other handy use cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrapping a new workspace:&lt;/strong&gt; provision templates from a script instead of rebuilding them one by one in the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrating from another tool or an internal codebase:&lt;/strong&gt; recreate templates programmatically instead of by hand in the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incident response:&lt;/strong&gt; deactivate a misbehaving workflow from your terminal in seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open source, and verifiable
&lt;/h2&gt;

&lt;p&gt;The CLI is &lt;strong&gt;open source and free to use.&lt;/strong&gt; You can install via Homebrew, download the binary from GitHub Releases, or build from source. If you're in a security-sensitive environment, you can verify the binary signature with &lt;strong&gt;Cosign&lt;/strong&gt; before installing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📖 &lt;a href="https://docs.suprsend.com/reference/cli-intro?_gl=1*17hfilp*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTIzOTY2NTUkbzE4JGcxJHQxNzkyMzk2NzEwJGo1JGwwJGgxNjU0Njg1MTY1" rel="noopener noreferrer"&gt;CLI docs&lt;/a&gt;: install and run your first command&lt;/li&gt;
&lt;li&gt;🧰 &lt;a href="https://docs.suprsend.com/reference/cli-intro?_gl=1*1gdq839*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTIzOTY2NTUkbzE4JGcxJHQxNzkyMzk2ODI0JGo2MCRsMCRoMTY1NDY4NTE2NQ..#available-commands" rel="noopener noreferrer"&gt;All CLI commands&lt;/a&gt;: the full surface&lt;/li&gt;
&lt;li&gt;⭐ &lt;a href="https://github.com/suprsend/cli" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick disclosure: we work on SuprSend, so we're obviously biased. But the underlying idea holds with any tool: if something affects what your users see, it deserves a pull request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your turn:&lt;/strong&gt; how do you manage notification templates today? Dashboard only, Git, or a mix? And what's the worst "someone edited a template in prod" story you've seen? Tell us in the comments.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>git</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Choosing The Right Message Queue Technology For Your Notification System</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Fri, 16 Oct 2026 10:57:47 +0000</pubDate>
      <link>https://dev.to/suprsend/choosing-the-right-message-queue-technology-for-your-notification-system-1ocl</link>
      <guid>https://dev.to/suprsend/choosing-the-right-message-queue-technology-for-your-notification-system-1ocl</guid>
      <description>&lt;p&gt;Your notification system works great at 100 messages a minute. Then your app goes viral, a flash sale hits, and suddenly your OTPs are stuck behind 2 million promotional pushes.&lt;/p&gt;

&lt;p&gt;Sooner or later, every notification system needs a message queue. The hard part is picking one. Kafka, RabbitMQ, SQS, Pub/Sub and Kinesis all "move messages," but they behave very differently when you need ordering, retries, replays or priorities.&lt;/p&gt;

&lt;p&gt;This guide walks through the factors that matter for notifications, compares the five most common options, and ends with a checklist you can use to decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what do notifications need from a queue?
&lt;/h2&gt;

&lt;p&gt;Forget feature lists for a second. Here's what actually matters, with a real scenario for each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Your sports app sends a push whenever a team scores. Traffic is quiet until a derby match, then every user gets a notification within seconds. You need a queue that scales out without redesign.&lt;/p&gt;

&lt;h3&gt;
  
  
  Message retention
&lt;/h3&gt;

&lt;p&gt;You send transaction alerts in real time. One night your consumer crashes for 30 minutes. If the queue discards messages that aren't consumed, those users never hear about their money moving.&lt;/p&gt;

&lt;p&gt;With retention, messages wait in the queue (for an hour, a day, longer) and are processed when the consumer comes back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low latency
&lt;/h3&gt;

&lt;p&gt;A fraud team needs to be alerted the moment a suspicious transaction appears. Delay matters here.&lt;/p&gt;

&lt;p&gt;A caveat: Redis Pub/Sub is often cited for this, but it's fire-and-forget. If no subscriber is listening, the message is gone. For anything you can't afford to lose, use Redis Streams or a durable broker.&lt;/p&gt;

&lt;h3&gt;
  
  
  High throughput
&lt;/h3&gt;

&lt;p&gt;A food delivery app sends status updates ("being prepared", "on its way") for every order. Volume is high, and many consumers need to process it in parallel. This is where Kafka and Kinesis shine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Message ordering
&lt;/h3&gt;

&lt;p&gt;A trading platform sends market alerts. If "price dropped" arrives after "price recovered," users act on stale information. You need ordering guarantees, at least per user or per entity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritization
&lt;/h3&gt;

&lt;p&gt;A hospital monitoring system alerts nurses when a patient's heart rate crosses a threshold. That alert can't wait behind a batch of routine reports.&lt;/p&gt;

&lt;p&gt;One thing to know: &lt;strong&gt;most queues don't have native priority.&lt;/strong&gt; The usual pattern is separate queues or topics per priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;TOPICS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# OTP, security, health alerts
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# transactional
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notif.low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# promos, digests
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TOPICS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;   &lt;span class="c1"&gt;# same user -&amp;gt; same partition -&amp;gt; ordered
&lt;/span&gt;    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each topic gets its own consumers and throughput limits, so a promo blast can't starve your OTPs. (RabbitMQ is the exception, with native priority queues.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Dead letter queues (DLQs)
&lt;/h3&gt;

&lt;p&gt;An e-commerce site sends order confirmations. Some email addresses are invalid and fail every time. Without a DLQ, those messages are retried forever or block the line. With a DLQ, failures move aside where you can inspect them and fix the cause.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"RedrivePolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deadLetterTargetArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:sqs:us-east-1:123456789012:notif-dlq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxReceiveCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's an SQS redrive policy: after 5 failed receives, the message goes to the DLQ.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five contenders, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;RabbitMQ&lt;/th&gt;
&lt;th&gt;Kafka&lt;/th&gt;
&lt;th&gt;AWS SQS&lt;/th&gt;
&lt;th&gt;Google Pub/Sub&lt;/th&gt;
&lt;th&gt;AWS Kinesis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broker with exchanges and queues (AMQP)&lt;/td&gt;
&lt;td&gt;Distributed log, consumer groups&lt;/td&gt;
&lt;td&gt;Queue (pull)&lt;/td&gt;
&lt;td&gt;Topics and subscriptions (push or pull)&lt;/td&gt;
&lt;td&gt;Streams split into shards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FIFO per queue&lt;/td&gt;
&lt;td&gt;Per partition&lt;/td&gt;
&lt;td&gt;FIFO queues, per message group&lt;/td&gt;
&lt;td&gt;Per ordering key (opt-in)&lt;/td&gt;
&lt;td&gt;Per shard / partition key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate to high&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High (shard-bound)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retention / replay&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Until acked (Streams plugin adds replay)&lt;/td&gt;
&lt;td&gt;Configurable, replayable&lt;/td&gt;
&lt;td&gt;Up to 14 days, no replay&lt;/td&gt;
&lt;td&gt;Configurable, can seek back&lt;/td&gt;
&lt;td&gt;24h up to 365 days, replayable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native priority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (use separate topics)&lt;/td&gt;
&lt;td&gt;No (use separate queues)&lt;/td&gt;
&lt;td&gt;No (use separate topics)&lt;/td&gt;
&lt;td&gt;No (use separate streams)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dead letter queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dead-letter exchanges&lt;/td&gt;
&lt;td&gt;DIY (a DLQ topic)&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;No native DLQ (handle in the consumer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ops burden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate (self-host or managed)&lt;/td&gt;
&lt;td&gt;Heavy if self-hosted, managed options exist&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;td&gt;Fully managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infra cost if self-hosted&lt;/td&gt;
&lt;td&gt;Infra cost if self-hosted&lt;/td&gt;
&lt;td&gt;Pay per request&lt;/td&gt;
&lt;td&gt;Pay per volume&lt;/td&gt;
&lt;td&gt;Pay per shard / volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;vhosts and permissions&lt;/td&gt;
&lt;td&gt;ACLs&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Limits like max message size change over time, so check each provider's docs before you design around them.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you pick?
&lt;/h2&gt;

&lt;p&gt;Here are three common situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Startup with low-volume transactional notifications → RabbitMQ (or SQS)
&lt;/h3&gt;

&lt;p&gt;Easy to set up, simple to reason about, and flexible routing. It has native priority queues and dead-letter exchanges, so you get a lot of notification-friendly features without much ceremony. If you're all-in on AWS and want zero ops, SQS is a strong alternative.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 Mid-sized company, high volume, low latency → Kafka
&lt;/h3&gt;

&lt;p&gt;Distributed, high throughput, and great for fan-out: one event can feed email, push and analytics consumers at once. Replication and replay are strong, and you can reprocess history when something goes wrong. The trade-off is operational complexity, so consider a managed offering.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏢 Enterprise on AWS with many microservices → Kinesis (plus SNS/SQS where it fits)
&lt;/h3&gt;

&lt;p&gt;Fully managed, integrates natively with Lambda and the rest of AWS, and handles large volumes. Kinesis is a streaming service rather than a classic queue, so many teams pair it with SQS or SNS for simple fan-out and retry patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  At-least-once delivery means you need idempotency
&lt;/h2&gt;

&lt;p&gt;Almost every queue above delivers messages &lt;strong&gt;at least once&lt;/strong&gt;. Retries and rebalances can deliver the same message twice, and a user getting the same "payment received" push twice is a bug.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;already_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;      &lt;span class="c1"&gt;# e.g. Redis SETNX or DB unique key
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mark_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whichever queue you pick, build this in from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency vs throughput: you can't max both
&lt;/h2&gt;

&lt;p&gt;Optimizing for one tends to cost you the other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low latency&lt;/strong&gt; needs fast ingest and delivery, which uses more CPU, memory and network per message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High throughput&lt;/strong&gt; relies on batching and parallelism, which can add a little delay per message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RabbitMQ leans toward low-latency, real-time delivery. Kafka leans toward high-throughput streaming, but it's tunable (settings like batch size and linger time let you trade one for the other). Even then, hardware and architecture set the limits.&lt;/p&gt;

&lt;p&gt;A practical rule: use low-latency settings for your critical queue (OTPs, alerts), and throughput-optimized settings for the low-priority bulk queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist for deciding
&lt;/h2&gt;

&lt;p&gt;Run through these before you commit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define your numbers.&lt;/strong&gt; Message volume, latency target, reliability needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick your trade-off.&lt;/strong&gt; Latency or throughput first?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for growth.&lt;/strong&gt; Can it scale horizontally as your user base grows?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check ecosystem fit.&lt;/strong&gt; Your languages, frameworks and existing cloud provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count the operational cost.&lt;/strong&gt; Setup complexity, monitoring, and what your team already knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check security.&lt;/strong&gt; Durability, access control and encryption in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget it.&lt;/strong&gt; Infrastructure, managed service fees and maintenance time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype and load-test.&lt;/strong&gt; Don't trust benchmarks, including the ones in this post. Test with your own traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running multi-cloud or hybrid?
&lt;/h2&gt;

&lt;p&gt;A few things to watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple queue technologies.&lt;/strong&gt; Different components may use different brokers. Make sure your team can operate all of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard protocols.&lt;/strong&gt; Prefer widely supported protocols like AMQP or MQTT where you can, and keep message formats consistent to avoid fragmentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity vs flexibility.&lt;/strong&gt; Cross-environment connectivity adds moving parts. Make sure the flexibility is worth it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;There's no single best message queue, only the best fit for your volume, latency needs, team and cloud. Balance performance, reliability, ease of use and ecosystem, and revisit the decision as your notification system grows.&lt;/p&gt;

&lt;p&gt;And if you'd rather not build and operate this whole layer yourself (queues, priorities, retries, fallbacks), that's what we built &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; for. Full disclosure: we work on it, so take that into account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your turn:&lt;/strong&gt; which queue are you running notifications on today, and would you pick it again? Tell us in the comments. Horror stories welcome.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>scalability</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Introducing SuprSend Agent</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Thu, 15 Oct 2026 07:54:36 +0000</pubDate>
      <link>https://dev.to/suprsend/introducing-suprsend-agent-55p6</link>
      <guid>https://dev.to/suprsend/introducing-suprsend-agent-55p6</guid>
      <description>&lt;p&gt;Quick intro: we're the team behind &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt;, a notification infrastructure platform. Today we're launching &lt;strong&gt;SuprSend Agent&lt;/strong&gt;, and we wanted to share what we built, how it's wired, and the guardrails we put around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second version
&lt;/h2&gt;

&lt;p&gt;SuprSend Agent is an AI agent that lives &lt;strong&gt;inside the SuprSend dashboard&lt;/strong&gt;. You describe what you need in plain English, and it builds, configures and debugs your notification setup for you.&lt;/p&gt;

&lt;p&gt;It's connected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the full SuprSend API surface&lt;/li&gt;
&lt;li&gt;the product documentation&lt;/li&gt;
&lt;li&gt;agent skills&lt;/li&gt;
&lt;li&gt;domain knowledge about how notifications actually work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it doesn't just fire off API calls. It understands your use case, picks the right tools, applies the correct configuration, and tests the result.&lt;/p&gt;

&lt;p&gt;Try it: &lt;strong&gt;&lt;a href="https://auth.suprsend.com/?client_id=client_01HQTQ7EHH9GZTE1RGXEPN5RX2&amp;amp;redirect_uri=https%3A%2F%2Fapp.suprsend.com%2Fen%2Fcallback&amp;amp;authorization_session_id=01M4Z7KV9RD4ZWE09P14Q8H9Q2" rel="noopener noreferrer"&gt;SuprSend Agent&lt;/a&gt;&lt;/strong&gt; | Docs: &lt;strong&gt;&lt;a href="https://docs.suprsend.com/reference/agent?_gl=1*15vu7gt*_gcl_au*MjAzODM1MDA1MS4xNzkwMzIxNjUw*_ga*MTQ5OTQ3MjI2Ny4xNzkwMzIxNjUw*_ga_PPDYBESP2L*czE3OTIwNDk1NzIkbzEzJGcxJHQxNzkyMDQ5NTc5JGo1MyRsMCRoMTI2OTEwNDgw" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built it
&lt;/h2&gt;

&lt;p&gt;Notifications are deceptively deep. One message passes through several layers of logic: who gets it, on which channel, at what time, with what content, and what happens if the user does (or doesn't) act. Every layer has to be configured, tested and iterated.&lt;/p&gt;

&lt;p&gt;That depth is what lets a platform handle real-world use cases. It's also why it takes days to build a mental model before you configure anything.&lt;/p&gt;

&lt;p&gt;The agent shifts where that effort goes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New users&lt;/strong&gt; skip the upfront learning curve. Ask the agent how things work, and let it set up your first workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing users&lt;/strong&gt; skip the click-through. No more hopping between screens to change a vendor, check an object or tweak a template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you can do with it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Ask anything (without opening another tab)
&lt;/h3&gt;

&lt;p&gt;Data modeling, how a feature works, what an error means, best practices, how to approach an integration. It answers from the docs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"How do preferences cascade from defaults to tenants to end users?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Manage your assets from a chat
&lt;/h3&gt;

&lt;p&gt;The agent can create, retrieve, update and commit across tenants, objects, preferences, vendors, translations and schemas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Update the SMS vendor for tenant cypher and show me the current preference categories."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Design and test a notification from one prompt
&lt;/h3&gt;

&lt;p&gt;This is the one we're most excited about. Normally you decide everything yourself: flow structure, timing, branching, channels, and how the content should read on each one.&lt;/p&gt;

&lt;p&gt;Now a prompt like this is enough:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Set up a trial expiry reminder for new users."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Map out the workflow nodes&lt;/li&gt;
&lt;li&gt;Set the time delays&lt;/li&gt;
&lt;li&gt;Add the branching logic&lt;/li&gt;
&lt;li&gt;Pick the right channels&lt;/li&gt;
&lt;li&gt;Design and personalize the templates with variables&lt;/li&gt;
&lt;li&gt;Test the result across every channel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You describe the outcome. It handles the build.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Ask your analytics anything
&lt;/h3&gt;

&lt;p&gt;A fixed dashboard only answers the questions it was built for. The agent reads your notification data through a &lt;strong&gt;semantic layer over the data lake&lt;/strong&gt;, so you can ask things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which notifications performed worst in the last 30 days?&lt;/li&gt;
&lt;li&gt;What's the best performing template this week?&lt;/li&gt;
&lt;li&gt;How do channels compare across segments and tenants?&lt;/li&gt;
&lt;li&gt;How many workflows are active?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Show me delivery and open rates for the weekly-digest workflow, split by tenant, for the last 30 days."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It returns the answer, can visualize it as a chart, and can post it straight to Slack.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Debug "why didn't they get it?"
&lt;/h3&gt;

&lt;p&gt;We all know this one. A notification doesn't arrive, and the cause could be a channel issue, a preference opt-out, a tenant override or a vendor failure. Finding it means digging through logs and screens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"User &lt;a href="mailto:john@cypher.com"&gt;john@cypher.com&lt;/a&gt; says he's not getting emails. What's wrong?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent traces the delivery, finds what stopped it, explains the cause and suggests a fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Cool, but is it safe on a live workspace?"
&lt;/h2&gt;

&lt;p&gt;Fair question, and honestly the one we spent the most time on. The agent acts on real organizational data, so safety is built into every step:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guardrail&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workspace-scoped access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every session is limited to one workspace. A conversation in one can't read or change data in another.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service token auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each session uses a workspace-scoped JWT that expires and auto-refreshes, so access is short-lived by default.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Permission before changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The agent asks for confirmation before any edit. Updates read the resource's current state first, so nothing is silently overwritten.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No destructive deletes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deletes aren't exposed to the agent at all. Only low-risk, reversible operations (like removing a user from a list) are allowed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validated, not guessed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It runs on validated SuprSend schemas and product knowledge, so it uses correct parameters instead of guessing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Who is it for?
&lt;/h2&gt;

&lt;p&gt;Developers, product managers, marketing, customer success and ops teams. Basically anyone who manages notifications but doesn't want to write integration code for every change. Work that used to wait on engineering can now happen in a conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bigger shift
&lt;/h2&gt;

&lt;p&gt;This launch is part of a larger direction: SuprSend is becoming &lt;strong&gt;AI-first notification infrastructure&lt;/strong&gt;, where you operate the platform by describing what you need instead of learning every knob and switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it and tell us what you think
&lt;/h2&gt;

&lt;p&gt;Start with a first prompt in the dashboard: &lt;strong&gt;&lt;a href="https://auth.suprsend.com/?client_id=client_01HQTQ7EHH9GZTE1RGXEPN5RX2&amp;amp;redirect_uri=https%3A%2F%2Fapp.suprsend.com%2Fen%2Fcallback&amp;amp;authorization_session_id=01M4Z7PMJ3RCPXM2QMZVXSYYFF" rel="noopener noreferrer"&gt;Try SuprSend Agent&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'd love to hear from you in the comments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What's the first thing you'd ask the agent to build?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What guardrails would you want on an AI agent that touches production config?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What's the most painful notification bug you've ever had to debug?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We read every reply. 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>A complete guide on Notification Service for Modern Applications in 2026</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Wed, 14 Oct 2026 10:53:02 +0000</pubDate>
      <link>https://dev.to/suprsend/a-complete-guide-on-notification-service-for-modern-applications-in-2026-128c</link>
      <guid>https://dev.to/suprsend/a-complete-guide-on-notification-service-for-modern-applications-in-2026-128c</guid>
      <description>&lt;p&gt;Most notification services start as a single function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;email_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works for a while. Then product asks for push, then SMS, then an in-app inbox, then "can users pick what they get?" Before long, this one function has become a distributed system.&lt;/p&gt;

&lt;p&gt;This post breaks down what a production notification service contains, split into the &lt;strong&gt;functional&lt;/strong&gt; parts users and PMs see, and the &lt;strong&gt;non-functional&lt;/strong&gt; parts that keep it alive at 3 a.m. At the end, there's a framework for deciding whether to build or buy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your app --&amp;gt; Notification API --&amp;gt; Preference check --&amp;gt; Template render
                                        |
                              Priority queues (critical / high / low)
                                        |
                              Channel workers (email, SMS, push, inbox...)
                                        |
                              Vendor adapters --&amp;gt; Webhooks --&amp;gt; Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each box hides real engineering work. Let's go through them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Functional components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Delivery channels and vendor integrations
&lt;/h3&gt;

&lt;p&gt;Users live on different channels: email, SMS, push, in-app inbox, WhatsApp, Slack or Teams, even automated calls. To maximize engagement you'll want several of them.&lt;/p&gt;

&lt;p&gt;Every channel means picking a vendor, learning its API, testing quality of service, monitoring it, and planning a fallback. A common way to keep this manageable is an adapter layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DeliveryResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendgridAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TwilioAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FcmAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelAdapter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your core logic talks to one interface, and vendor quirks stay at the edges.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Templating engine
&lt;/h3&gt;

&lt;p&gt;Templates quietly become the biggest ownership burden. They need constant changes for copy, personalization, branding, dynamic variables, conditional logic, multi-lingual support, and correct rendering across devices.&lt;/p&gt;

&lt;p&gt;Each channel also has different strengths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; long, detailed content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMS:&lt;/strong&gt; short and attention-grabbing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push:&lt;/strong&gt; rich media and action buttons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If every copy change needs a deploy, your engineers become a bottleneck. The fix is a visual editor so PMs and marketers can build and test without a pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. User preferences
&lt;/h3&gt;

&lt;p&gt;Marketing emails get a simple unsubscribe link. Product notifications are trickier, because users want some of them (security alerts) and not others (weekly tips).&lt;/p&gt;

&lt;p&gt;A decent preference system lets users control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; which categories they get&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channel:&lt;/strong&gt; where they get them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequency:&lt;/strong&gt; instant, hourly, daily or weekly digest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing:&lt;/strong&gt; quiet hours
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_preferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled_categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;in_quiet_hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_critical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reschedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quiet_hours_end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, users do the only thing they can: block everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Batching and digests
&lt;/h3&gt;

&lt;p&gt;Say your product has comments. If 20 people reply to a thread, you don't want 20 notifications. You want one: "20 new comments."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batching:&lt;/strong&gt; define a window, collect events inside it, then send one combined message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digests:&lt;/strong&gt; send a summary at a cadence the user chooses, such as hourly, daily or weekly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the notification type, you may need both.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Multi-tenancy
&lt;/h3&gt;

&lt;p&gt;If your product serves other businesses and sends notifications to their users, you need tenant-level isolation and customization: their branding, their sending domain, their channels, their preference rules.&lt;/p&gt;

&lt;p&gt;Example: a SaaS tool sending invoices on behalf of customers. The end user doesn't know your brand, so the email must carry your customer's logo, content and sender identity, all running on shared infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Analytics
&lt;/h3&gt;

&lt;p&gt;PMs need delivery, open and click data to improve notifications. But every channel and vendor reports differently, and some metrics don't exist (you can't track opens on SMS).&lt;/p&gt;

&lt;p&gt;That means you need a normalized event model, so cross-channel analysis is possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivered"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notification_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"u_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-14T10:32:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Map vendor webhooks into this shape and you can compare channels apples to apples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Non-functional components
&lt;/h2&gt;

&lt;p&gt;This is the stuff nobody sees until it breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Scalability and load balancing
&lt;/h3&gt;

&lt;p&gt;Notification traffic spikes unexpectedly: a flash sale, a viral feature, an incident. Design for bursts, and balance load across servers and regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fault tolerance and retries
&lt;/h3&gt;

&lt;p&gt;Downtime isn't acceptable for OTPs or compliance messages. You need redundancy, state management, fallback vendors, and rate-limit-aware throttling per channel.&lt;/p&gt;

&lt;p&gt;One subtle point: &lt;strong&gt;not every failure deserves a retry.&lt;/strong&gt; An expired push token is dead, and retrying it just wastes resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RETRYABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate_limited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_5xx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;PERMANENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invalid_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsubscribed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bounced&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RETRYABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;retry_with_backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PERMANENT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;mark_channel_inactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding why something failed matters as much as retrying it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. High deliverability
&lt;/h3&gt;

&lt;p&gt;Reaching the user involves several things at once: multiple channels, reliable vendors, cleaning inactive contacts, and managing bounces.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Low latency
&lt;/h3&gt;

&lt;p&gt;People expect notifications fast. Cut network round trips, optimize database queries, and shorten delivery paths. It's ongoing work, because performance degrades as you scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Observability
&lt;/h3&gt;

&lt;p&gt;You need detailed logging, error tracking and performance monitoring so you can diagnose failures quickly and answer compliance questions. If you can't answer "what happened to this user's notification?", debugging becomes guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Message queue prioritization
&lt;/h3&gt;

&lt;p&gt;This one saves a lot of pain. An OTP and a weekly newsletter shouldn't share a queue. If bulk sends clog the pipeline, login codes get delayed and drop-off rises.&lt;/p&gt;

&lt;p&gt;Use separate queues with their own throughput limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;QUEUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unlimited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# OTP, security
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1000/s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;      &lt;span class="c1"&gt;# transactional
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200/s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;       &lt;span class="c1"&gt;# promos, digests
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This protects critical messages without over-provisioning for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  To build or not to build?
&lt;/h2&gt;

&lt;p&gt;Here's a quick framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build in-house when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your needs are simple.&lt;/strong&gt; Few notification types, low volume, rare changes, a single channel. Direct vendor integration or a lightweight internal service will do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need heavy customization.&lt;/strong&gt; Your use case is unusual enough that third-party platforms can't support it (like a chatbot over SMS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications are your product.&lt;/strong&gt; If you're something like WhatsApp, owning it makes sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Consider alternatives when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources are limited.&lt;/strong&gt; Building and maintaining this takes real engineering capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to market matters.&lt;/strong&gt; A third-party service can get you live far faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need complex features.&lt;/strong&gt; Workflows, white-labeling, preferences, personalization, batching and cross-channel logic come ready-made on established platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to focus on your core product.&lt;/strong&gt; Getting notifications right takes experimentation, channel expertise, scaling work and observability, which pulls attention from what makes your product special.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the layer we built &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; to handle, so teams can skip the plumbing. Full disclosure: we work on SuprSend, so weigh that accordingly. The checklist above holds whichever way you go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;A good notification service balances two sides. The functional side gives product teams control over templates, preferences and analytics. The non-functional side gives everyone reliability, speed and visibility. Skipping either one hurts.&lt;/p&gt;

&lt;p&gt;Whichever path you choose, the goal is the same: a smooth, meaningful experience that keeps users connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over to you:&lt;/strong&gt; did you build your notification system in-house or use a platform? What's the one component you wish you'd planned for earlier? Tell us in the comments.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Notifications Are Not Just an API Call: 11 Reasons They Quietly Eat Your Sprint</title>
      <dc:creator>Nikita Navral</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:24:26 +0000</pubDate>
      <link>https://dev.to/suprsend/notifications-are-not-just-an-api-call-11-reasons-they-quietly-eat-your-sprint-4i5m</link>
      <guid>https://dev.to/suprsend/notifications-are-not-just-an-api-call-11-reasons-they-quietly-eat-your-sprint-4i5m</guid>
      <description>&lt;p&gt;Every product team starts the same way: "We just need to send an email when someone signs up." One SDK, one template, done by Friday.&lt;/p&gt;

&lt;p&gt;Six months later you have four channels, three vendors, a pile of cron jobs, and a PM asking whether the payment reminder copy can be changed without a deploy.&lt;/p&gt;

&lt;p&gt;Notifications look trivial and behave like infrastructure. They're how your product talks to users: alerts, reminders, recommendations, launches, even user-to-user messages. Send too little and the experience breaks. Send too much and people unsubscribe. Here are the 11 places where it gets hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Trivial tasks that keep landing on engineers
&lt;/h2&gt;

&lt;p&gt;Notification logic is rarely hard. What's hard is iteration. Getting the content, trigger and timing right takes many tries, and PMs want to move fast. But when templates and triggers live in code, every tweak joins the sprint queue. Adding a new channel to an existing notification multiplies the work again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; let non-engineers edit content and logic without a deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Nobody knows what's actually live
&lt;/h2&gt;

&lt;p&gt;Before building a new notification, you need to know what already exists. Answering that means digging through the code or trusting a shared doc that went stale weeks ago. With several PMs involved, legacy notifications keep running because nobody wants to audit them. Cleanup becomes a once-or-twice-a-year project.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Testing is a coordination problem
&lt;/h2&gt;

&lt;p&gt;Testing notifications isn't technically complex, but it eats time. Test sends often go to one shared device or account that not everyone can access. Scheduled notifications usually need a developer to trigger them manually whenever QA or a PM wants to check them. That's a lot of context switching for something that should be self-serve.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Scaling exposes the cracks
&lt;/h2&gt;

&lt;p&gt;As volume grows, delivery reliability becomes your problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No priority separation.&lt;/strong&gt; If a promotional blast clogs the queue, your OTP sits behind it. Scaling everything up to compensate gets expensive. What you want is immediate delivery for transactional messages and staggered delivery for promotional ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Pseudocode: route by priority so promos never block transactional
&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transactional&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;otp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;promotional&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No fallback or retries.&lt;/strong&gt; If the provider API is down, queued messages are lost, and so is important communication. You need retries and a fallback path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No failure alerts.&lt;/strong&gt; Many teams only learn about failed deliveries when the business team or a customer complains. In the worst case, they never find out. Treat delivery failures like server downtime and alert on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale deliveries.&lt;/strong&gt; A delayed message that arrives two weeks later, like a "payment debited" notice, is worse than no message. Put an expiry on time-sensitive notifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Drop the message if it's too old to be useful
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;mark_expired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sounds like a rare edge case, but it leaves a bad impression when it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Analytics are scattered
&lt;/h2&gt;

&lt;p&gt;Each vendor gives you its own dashboard, which makes it hard to compare delivery and click rates across channels. User-level analytics are even rarer. How many messages did one user get this week? Did several fire at once? What does the overall experience feel like from their side? Without that view, users end up either under-engaged or bombarded.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Multi-channel is a tightrope
&lt;/h2&gt;

&lt;p&gt;Reaching users on every channel raises conversion, but it also raises unsubscribes. It's also costly to send the same message on three channels when the user saw it on the first.&lt;/p&gt;

&lt;p&gt;A smarter approach staggers delivery across channels based on past behavior and message type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push -&amp;gt; wait 30 min -&amp;gt; not seen? -&amp;gt; Email -&amp;gt; wait 24h -&amp;gt; not seen? -&amp;gt; SMS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That saves money and avoids spamming people.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Sending to dead channels
&lt;/h2&gt;

&lt;p&gt;People change numbers and leave companies. Messages to inactive contacts cost money, and high email bounce rates damage your domain reputation, which can push future emails into spam. Most tools report a bounce only after you've sent. It's far better to filter out inactive channels in advance and keep your contact data clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Multi-lingual support
&lt;/h2&gt;

&lt;p&gt;Expanding into non-English markets means storing each user's language preference and maintaining a template per language, per channel. The engineering effort grows quickly, even though the logic stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Users want a say
&lt;/h2&gt;

&lt;p&gt;Letting users choose what they get notified about is good engagement strategy. Without granular preferences, many users take the only option available and unsubscribe from everything, which hurts engagement and sales. Quora is a good example: users can opt out of specific types of emails without leaving entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. One-size-fits-all frequency
&lt;/h2&gt;

&lt;p&gt;Engagement notifications usually run on a fixed schedule, because custom frequency per user isn't feasible by hand. The result is endless reminders for users who aren't interested, while engaged users could be getting more.&lt;/p&gt;

&lt;p&gt;A smarter system adapts to behavior: send more when users respond, back off when they go quiet, and try a different way to re-engage. That requires per-user data processing feeding back into the delivery pipeline, which most startups can't justify building. So they settle for light personalization and a frequency cap. It works, but it leaves engagement on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Vendor lock-in and reliability
&lt;/h2&gt;

&lt;p&gt;No vendor guarantees 100% uptime. For time-sensitive messages like OTPs, you need a fallback vendor. But every vendor has its own API and integration rules, so each one costs real engineering time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;vendor&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;VendorError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;DeliveryFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks simple, until you maintain two or three vendors' payload formats, webhooks and error codes. Most teams stay with one vendor per channel until an outage forces the issue, or pick a multi-channel vendor that may not be the cheapest option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;None of these problems is impossible alone. Together, they turn "just send a notification" into a standing drain on your team's time.&lt;/p&gt;

&lt;p&gt;That's the problem we're building &lt;a href="https://www.suprsend.com" rel="noopener noreferrer"&gt;SuprSend&lt;/a&gt; to solve, so engineering teams can stop reinventing notification infrastructure and focus on their core product.&lt;/p&gt;

&lt;p&gt;Now I'm curious: &lt;strong&gt;how does your team handle notifications today?&lt;/strong&gt; Home-grown service, a single vendor, or something else? What's been the biggest pain point? Let us know in the comments.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
