Notifications for B2B SaaS
Notifications,made simple.
One API call to send them. A ready-made feed your users can read. Push to every device they own. Live in your product this quarter, not after six weeks of wiring.
- REST + WebSocket
- JS SDK
- 19 languages
- No personal data
One
Easy to send
One request. We deal with the fan-out, the retries and the ordering.
Two
Easy to drop in
It lives inside your app, not in some separate tool your users have to go and visit.
Three
You get the feed
Not just delivery. The history, what's been read, the unread count. Everything you need to draw a bell icon.
One API call puts a notification in the feed., on the phone., in the browser.
Most teams need two or three vendors to do that.
01The problem we built it for
Nineteen languages.One forty-line switch statement.
This one happened to us. You pick a notification vendor. You need nineteen languages. The vendor has nowhere sensible to put them, so all nineteen end up jammed into one template, one branch per language, each with its own nested ifs.
- ✕It lives in the vendor's web UI, so you can't review it in a pull request, diff it, or test it.
- ✕A translator can't touch it without editing code.
- ✕You can't see which languages are missing or out of date. You find out by reading the switch.
- ✕One typo breaks all nineteen languages at once.
So the web team gives up and writes all the copy again, in their own files. Now the same sentence exists twice, kept by two different people, and the apps slowly drift until they tell the same person two different things.
Before · one template, nineteen branchesvendor-template.hbs
1{{! invoice_approved · edited in the vendor's web UI }}2{{#switch user.locale}}3 {{#case "en"}}4 {{#if count == 1}}{{actor}} approved an invoice5 {{else}}{{actor}} approved {{count}} invoices{{/if}}6 {{/case}}7 {{#case "pl"}}8 {{#if count == 1}}{{actor}} zatwierdził(a) fakturę9 {{else if (mod count 10) >= 2 && (mod count 10) <= 4}}10 {{actor}} zatwierdził(a) {{count}} faktury11 {{else}}{{actor}} zatwierdził(a) {{count}} faktur{{/if}}12 {{/cas}}13 {{#case "de"}}14 {{#if count == 1}}{{actor}} hat eine Rechnung freigegeben15 {{else}}{{actor}} hat {{count}} Rechnungen freigegeben{{/if}}16 {{/case}}17 {{#case "ar"}}18 {{! six plural forms. good luck }}19 {{/case}}20 {{#case "fr"}} …21 {{#case "es"}} …22 {{#case "ja"}} …
+ 16 more cases, each with its own nested ifs
With Bzzt · one row per languageinvoice.approved
| en | {actor} approved invoice {invoice} | ✓ |
| pl | Faktura {invoice} została zatwierdzona przez {actor} | ✓ |
| de | Rechnung {invoice} wurde von {actor} freigegeben | ✓ |
| fr | La facture {invoice} a été approuvée par {actor} | ✓ |
| es | {actor} aprobó la factura {invoice} | ✓ |
| it | {actor} ha approvato la fattura {invoice} | ✓ |
| pt | {actor} aprovou a fatura {invoice} | ✓ |
| nl | {actor} heeft factuur {invoice} goedgekeurd | ✓ |
| sv | {actor} godkände faktura {invoice} | ✓ |
| da | {actor} godkendte faktura {invoice} | ✓ |
| nb | {actor} godkjente faktura {invoice} | ✓ |
| fi | {actor} hyväksyi laskun {invoice} | ✓ |
| cs | Faktura {invoice} byla schválena uživatelem {actor} | ✓ |
| sk | No copy yet | Missing |
| hu | {actor} jóváhagyta a(z) {invoice} számlát | ✓ |
| ro | {actor} a aprobat factura {invoice} | ✓ |
| tr | {actor}, {invoice} numaralı faturayı onayladı | Stale |
| ja | {actor}さんが請求書{invoice}を承認しました | ✓ |
| ar | وافق {actor} على الفاتورة {invoice} | ✓ |
Copy is data, not code. One row per language. Reviewable, diffable, and a translator can edit it. Plus a grid showing exactly what's missing.
02What it does
Six things, each one properly configurable.
Everything here works today. Each claim comes with the reason you can believe it.
One call reaches one person or a whole company.
Address a user, or every user at a customer. We take it from there.
attempt 1 → id nt_7c1e… sent
attempt 2 → id nt_7c1e… already sent
Why it holds
A retry can't send the same thing twice. Ids come from the content, not from the attempt.
Works with any stack.
A REST API and a live socket, plus a JavaScript SDK if you want it.
Why it holds
You get the finished text and the raw data, so you can render whichever suits you.
Nineteen languages, one row each.
Edit copy with a live preview, one language at a time.
1 nowe zadanie
3 nowe zadania
5 nowych zadań
Why it holds
Proper plural rules, not find-and-replace. Polish has four plural forms. Arabic has six.
In-app feed plus push to every device.
Phones and browsers, from the same call that fills the feed.
Why it holds
Notifications turn up as your app, with your icon and name. We set that up with you.
A console your customers run themselves.
Types, templates, broadcasts and a delivery log, without filing a ticket with you.
push · ios · 14:02
✕ Token expired. The user reinstalled the app.
Why it holds
The delivery log tells you what a failure actually means, not just an error code.
A permanent record of what was sent.
To who, when, and in which language.
user_c41e · pl · 14 Oct 2025, 09:12
“Masz 5 nowych zadań w projekcie Q4”
Why it holds
It keeps the finished sentence, so a year later you can show exactly what someone read.
03Why teams switch
Most tools hand you their idea of a notification.We take yours.
As customisable as we can make it. When something's missing we'd rather add it than tell you to work around it.
- 01
It turns up as your app, not ours.
A push lands with your icon, your name, your wording. Nothing hints that anyone else was involved, because nobody was. Each customer sends through their own account with Apple and Google, so it really is their app doing the talking.
→We set that up with you when you join. Nobody on your team needs to know what Firebase is, let alone configure it.
- 02
Your translators stop waiting for engineers.
Copy gets edited in the console, one language at a time, with a grid showing what's missing or stale. Someone who writes for a living can fix a Polish sentence without a developer, a release, or a ticket.
- 03
It looks like your product, because you build it.
We hand back the finished text and the data behind it. Your designers build the feed so it matches your product. You're not dropping in someone else's component and then fighting its CSS.
- 04
Almost nothing is fixed by us.
You decide what a notification even is. The types, the data each one carries, which inbox it lands in, what fields a template fills, what labels you hang on your users. A template isn't forced into a title and a body, because your type says what shape its content has.
- 05
Nobody gets told the same thing twice.
If a send gets retried because the network hiccupped, it can't reach the same phone twice. Sounds obvious. It's also the single most common complaint about notification systems.
- 06
Read it once, read it everywhere.
Open a notification on your phone and the badge clears on your laptop. Read state is shared across every device someone uses.
04Small on purpose
Few features.Lots of options.
Notification tools grow by piling on features. Campaign builders. Behavioural segmentation. Send-time optimisation. A/B tests. Digests. Quiet hours.
Most teams use almost none of it. They pay for all of it anyway, in the price, in setup time, and in a console nobody can find their way around.
We do the bits you actually use, each one properly configurable, with nothing to ignore. You should be sending real notifications the day you sign up, not after a discovery call.
We left this out so the rest stays simple.
Every one of these is a choice
—Campaign builders, segmentation
Different product, different buyer. Your marketing team already has one.
—Analytics dashboards, open rates
Your product already has analytics. We'd be building a worse version of it.
—Per-user notification preferences
The type decides which channels fire. One place to look instead of two that can disagree.
—Quiet hours, send-time optimisation
Needs everyone's timezone. We don't hold personal data, and we're not going to start for a feature hardly anyone turns on.
—Digests and bundlingRoadmap
Genuinely on the roadmap. We'd rather build it when someone needs it than guess the rules now.
—Email and SMS
Push and in-app only. Email means addresses, bounces, unsubscribes and deliverability. That's a whole product.
05Privacy
No names.No emails.No phone numbers.
We don't hold anything about your users. Just an id you already use, a language, and whatever labels you decide to add. There's very little for a privacy review to even look at.
The whole user record
{ "id": "user_8f2c", "locale": "pl", "labels": { "plan": "pro", "region": "eu" } }
06Who it's for
Built for product engineers at B2B SaaS companies. Can you ship it this quarter, and does it keep your customers properly separated? Yes.
A good fit if
- ✓Your customers are companies, not individuals.
- ✓Each of them wants their own branding, their own push setup and their own settings.
- ✓You ship in more than one language and want translators to own the copy.
- ✓You'd rather build the feed to match your product than restyle someone else's widget.
Probably not us if
- —You sell to consumers. Your needs are simpler and there are cheaper options.
- —You want campaigns, segments or subject-line tests. That's a marketing tool for a growth team. Different product.
- —You need email or SMS. We do push and in-app, properly, and nothing else.
Send your first notification today.
Not after a discovery call and a solutions engineer. Sign up, make one request, and watch it land in the feed, on the phone and in the browser.
$ curl -X POST $BZZT_API/v1/notifications \ -H "Authorization: Bearer $BZZT_KEY" \ -d '{"type":"invoice.approved", "to":"user_8f2c"}' { "status": "accepted", "channels": ["feed", "ios", "web"] }