<?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: zacbuilds</title>
    <description>The latest articles on DEV Community by zacbuilds (@zacbuilds).</description>
    <link>https://dev.to/zacbuilds</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%2F4085397%2Fb4833034-a1f6-4220-a010-3c6094dd7a87.png</url>
      <title>DEV Community: zacbuilds</title>
      <link>https://dev.to/zacbuilds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zacbuilds"/>
    <language>en</language>
    <item>
      <title>Notavia is live: one API for email, in-app inbox, SMS, and chat — built .NET-first</title>
      <dc:creator>zacbuilds</dc:creator>
      <pubDate>Mon, 31 Aug 2026 10:14:20 +0000</pubDate>
      <link>https://dev.to/zacbuilds/notavia-is-live-one-api-for-email-in-app-inbox-sms-and-chat-built-net-first-2lmc</link>
      <guid>https://dev.to/zacbuilds/notavia-is-live-one-api-for-email-in-app-inbox-sms-and-chat-built-net-first-2lmc</guid>
      <description>&lt;p&gt;Disclosure first: I build Notavia. This is a launch post, but I've written the thing I'd have wanted to read — what it does, what it deliberately doesn't, and where the edges are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem I kept re-solving
&lt;/h2&gt;

&lt;p&gt;Every SaaS product eventually has to tell a user something. A new invoice. A failed sync. A teammate's comment. A security alert at 2am. You need that message to reach whichever channel the user is actually paying attention to, and you need to know it arrived.&lt;/p&gt;

&lt;p&gt;I have written that layer more than once. It always starts as "just send an email" and ends as a scheduled job, a half-finished retry queue, provider credentials in three places, and no reliable answer to "did it actually get delivered?"&lt;/p&gt;

&lt;p&gt;Notavia is that layer, built once, properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  One call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SendNotificationRequest&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;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Recipient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ada@example.com"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TemplateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TemplateData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ada"&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;Notavia looks up the recipient's channel preferences, renders the template for each active channel, dispatches, and gives you a replayable delivery log — without you managing per-channel provider credentials or writing the same routing logic for the fourth time.&lt;/p&gt;

&lt;p&gt;The four channels that are actually live&lt;/p&gt;

&lt;p&gt;Email — your own SMTP relay or any compatible provider. Paid plans include managed EU sending with no provider setup at all. Custom sender domains, GDPR-clean unsubscribes, suppression lists.&lt;/p&gt;

&lt;p&gt;In-app inbox — a real-time notification bell for your product: unread counts, mark-as-read state, full history. In .NET this is a drop-in Blazor component, not a JavaScript widget with a C# footnote bolted on.&lt;/p&gt;

&lt;p&gt;SMS — your own Twilio or Vonage credentials. There is no managed SMS option, and I'd rather say that than let you find out later.&lt;/p&gt;

&lt;p&gt;Chat — Slack, Microsoft Teams, and Discord. Mostly useful for internal tooling and DevOps alerting, where your users are developers who live in those apps.&lt;/p&gt;

&lt;p&gt;Push is on the roadmap. The architecture is there; I'm still working through certificate management and TTL edge cases, and I'm not calling it production-ready until that's done.&lt;/p&gt;

&lt;p&gt;One template, every channel&lt;/p&gt;

&lt;p&gt;This is where most of the time went. Templates are written in Scriban — logic-capable, and it renders cleanly to HTML, plain text, and short-form message bodies. You write one template per event type and Notavia renders the right variant per channel.&lt;/p&gt;

&lt;p&gt;Templates are runtime-editable, so tenant administrators can change the copy through the console without a deployment. You ship the default; your customers adjust the wording for their brand.&lt;/p&gt;

&lt;p&gt;The .NET-first part&lt;/p&gt;

&lt;p&gt;The API is plain REST and works fine with no SDK at all. But the reason this exists is that the .NET ecosystem usually gets the afterthought client, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SDKs published for .NET, TypeScript, Python, Go, and PHP — on NuGet, npm, PyPI, the Go proxy, and Packagist&lt;/li&gt;
&lt;li&gt;A Blazor inbox component with real-time updates&lt;/li&gt;
&lt;li&gt;An MCP server, so an AI editor can wire notifications into your app by talking to the API directly&lt;/li&gt;
&lt;li&gt;White-label throughout: per-tenant sender domains, branded inbox, hosted unsubscribe pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it costs&lt;/p&gt;

&lt;p&gt;Free forever at 1,000 sends a month, no card required. On the free plan you bring your own provider keys; managed sending is on the paid plans.&lt;/p&gt;

&lt;p&gt;If you want to try it: the first 20 teams get assisted onboarding, an architecture review, and the Growth plan at the Starter price for their first 60 days. Reply here or email &lt;a href="mailto:hello@saas-infrastructure.com"&gt;hello@saas-infrastructure.com&lt;/a&gt; and say "founding".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://saas-infrastructure.com/products/notify?utm_source=devto&amp;amp;utm_medium=launch&amp;amp;utm_campaign=launch-2026-08" rel="noopener noreferrer"&gt;https://saas-infrastructure.com/products/notify?utm_source=devto&amp;amp;utm_medium=launch&amp;amp;utm_campaign=launch-2026-08&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer anything — architecture, the template system, why Scriban, how the delivery log works, or what I'd do differently. Ask away.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>showdev</category>
      <category>api</category>
    </item>
  </channel>
</rss>
