Back to Blog
Technology8 min read

How In-App Notifications Fan Out to Your Team

IN
Invoice Generator TeamAuthor
September 8, 2026Published
Also available in:NederlandsDeutsch

When something happens on an invoice inside a shared workspace — a payment comes in, an estimate gets approved, a late fee gets applied — every relevant member of that workspace gets their own individual notification, not just the person who happened to trigger the action or a single shared workspace-level alert. That fan-out is handled by one small, reused piece of logic across the whole product, and a few of its design choices are worth understanding if you're trying to figure out why your team sees the notifications it does, and why you sometimes don't see one for your own actions.

One Function, Every Notification Type

Every kind of notification the product generates — an invoice being paid, a payment being received, an estimate being approved or rejected, a client comment coming in, a recurring invoice being generated, a late fee being applied — funnels through the same underlying fan-out logic rather than each event type having its own separate notification code path. The function takes a workspace ID, a notification type, a title and optional body, and produces one notification row per active member of that workspace. This matters practically because it means notification behavior is consistent across every event type in the product — the same rules about who gets notified and how apply everywhere, rather than each feature having quietly drifted its own slightly different notification logic over time.

It's a Fan-Out, Not a Single Shared Record

A common simpler design would be to write one notification record per event and have every workspace member query against it. This product does the opposite: it looks up every active member of the workspace at the moment the event fires, and inserts a separate, independent notification row for each one of them. The reason is read-state. If five people share a workspace and a payment comes in, each of those five people needs to be able to mark that notification as read on their own, independently — one teammate dismissing the notification shouldn't make it disappear for the other four. Writing one row per member is what makes independent read/unread state possible without needing a separate join table just to track who's seen what.

Self-Notifications Are Deliberately Suppressed

The fan-out logic accepts an optional "exclude" parameter, which is used whenever the action being notified about was performed by a specific known user — most commonly, when someone manually records a payment on an invoice themselves. In that case, that person is excluded from the resulting notification, because being told "you just did the thing you're currently in the middle of doing" is noise, not information. Every other active member of the workspace still gets notified normally; only the actor themselves is skipped. Events that don't have a clear single human actor — a recurring invoice firing automatically, a late fee being applied by the scheduled sweep — have nothing to exclude, so every active member gets notified, since none of them personally triggered it.

"Active Member" Is Checked at Notification Time, Not Event Time

The list of who gets notified is pulled fresh at the moment the event happens, by querying current workspace membership — it isn't a static list decided in advance or cached from earlier. This means membership changes are reflected immediately and correctly: if someone was removed from a workspace five minutes ago, they won't receive a notification for something that happens now, and if someone was just added, they're included in fan-out for events from that point forward, with no separate "sync" step required to make new members start receiving notifications.

Comment Notifications Have Their Own Read-State Coupling

Comment notifications specifically have one extra behavior layered on top of the general system: opening a document's comment thread also clears the "new comment" notification for that same document, in addition to whatever happens in the comment thread's own read-state tracking. Without this coupling, a client leaving a comment would show up as unread in two separate places at once — the notification bell, and a separate unread-comment badge on the document itself — and there'd be no single action that clears both. Reading the actual comment thread is treated as sufficient acknowledgment that you've seen the notification about it, so it clears the notification bell entry automatically rather than requiring you to separately dismiss the notification and separately read the comment.

Failures Are Logged, Never Surfaced to the User

If the notification fan-out itself fails for any reason — a database issue, an unexpected membership query result — the error is caught and logged on the server side, but it never blocks or surfaces as an error to whoever triggered the underlying action. Recording a payment, approving an estimate, or any other action that happens to trigger a notification will always succeed or fail based on its own logic, completely independent of whether the notification side-effect happened to work. This mirrors the same best-effort philosophy used for audit logging elsewhere in the product: notifications are a helpful side channel, not a gate that the primary action depends on.

What This Means Day to Day

In a shared workspace, expect every teammate — except whoever personally performed a manual action — to see a notification for anything meaningful that happens on an invoice, in real time, with fully independent read state per person. If you're not seeing a notification for something you did yourself, that's the exclusion working as intended, not a bug. And if a teammate who was recently removed from the workspace still seems to be getting notified about things, or a newly added teammate isn't seeing anything from before they joined, both of those are also working exactly as designed — the notification list is always a live snapshot of current membership, not a fixed list decided once.

How This Relates to the Comment Digest Email System

Client comments specifically also feed a second, separate notification path: an email digest, distinct from the in-app notification row described above. The in-app notification fans out immediately, one row per active member, the moment a comment arrives. The email digest, by contrast, deliberately waits a short delay before sending anything, and resets that delay every time a new comment comes in from the same conversation — so a burst of several comments in a row produces one summary email rather than one email per comment. The two systems are complementary rather than redundant: the in-app notification is built for near-real-time visibility while you're actively working in the dashboard, and the email digest is built to reach you even when you're not, without flooding your inbox if a client happens to leave several comments back to back. Both are triggered by the same underlying event, but they're handled by two independent pieces of logic with different tolerances for immediacy versus batching.

Notification Types at a Glance

The current set of notification types covers the moments in an invoice's lifecycle that most directly need a team's attention: a payment being received against an invoice, an invoice reaching fully paid status, an estimate being approved by a client, an estimate being rejected, a new client comment arriving on a shared document, a recurring invoice being automatically generated from a schedule, and a late fee being automatically applied. Each of these maps to a specific, concrete moment rather than a vague activity summary — the design intent is that every notification a team member receives should be actionable or at least worth knowing about on its own, rather than the kind of low-value activity noise that trains people to stop reading their notifications altogether.

Read State Is Tracked Per Notification, Per User

Because each fan-out produces an independent row per member, marking a notification as read is similarly scoped to exactly one of those rows at a time — one teammate marking a specific notification read has no effect on the identical-looking notification sitting in another teammate's list, since they're genuinely separate database rows even though they originated from the same single event. A "mark all as read" action is also available, and it operates the same way: it clears every currently-unread notification belonging to the requesting user specifically, leaving every other member's unread state completely untouched. The unread count shown anywhere in the interface is likewise computed per user, as a live count of that user's own unread rows, rather than any kind of shared or workspace-wide counter.

Related Articles

Technology8 min read

How the Comment Notification Digest Batches Client Activity Into One Email

Why a burst of client comments produces exactly one email, not five — and how the rolling delay resets on every new comment.

IN
Invoice Generator TeamSeptember 11, 2026
Technology9 min read

The Invoice Audit Trail: Every Event Logged Behind the Scenes

What actually gets recorded when an invoice is viewed, commented on, or changes status — and why the logging never blocks the action itself.

IN
Invoice Generator TeamSeptember 4, 2026
Technology6 min read

How Two-Factor Authentication Protects Your Account

2FA generates a six-digit code that changes every 30 seconds using the TOTP standard — no live connection between your phone and the server is ever required.

IN
Invoice Generator TeamAugust 27, 2026
Technology6 min read

How API Keys Are Stored (And What to Do If You Lose One)

The raw value of your API key is never stored anywhere after the moment you create it — only a one-way hash is kept, which is why a lost key can't be recovered.

IN
Invoice Generator TeamAugust 26, 2026
Technology11 min read

Is It Safe to Put a QR Code on Your Invoice?

If you've thought about adding a QR code to your invoices, you've probably also seen a headline or two about "quishing" — QR code phishing — and wondered whether you'd be handing your clients a security risk along with your bill. That's...

IN
Invoice Generator TeamAugust 13, 2026
Technology10 min read

Building Reliable Integrations With Invoice Webhooks

Webhooks look simple from the outside: something happens, you get a POST request, you do something in response. The complexity shows up once you start asking what happens when that POST request doesn't arrive, arrives twice, or arrives w...

IN
Invoice Generator TeamAugust 11, 2026

Mastered Invoicing?

Put your knowledge into practice and create your first professional invoice today.

Create Your Invoice Now
How In-App Notifications Fan Out to Your Team | Invoice Generator