The notification engine

One server-side dispatcher decides who hears about an event, on which channels, in whose words — and enforces the rules that stop it becoming noise.

What it is

Callers fire an event after their own state write succeeds; the dispatcher resolves recipients, applies the org grid and each person's personal mutes, renders any custom wording, and writes an in-app row, an email and a text as permitted. There are three entry points: notifyEvent (role-routed), notifyUsers (a named set of people) and notifyStageRecipients (the per-stage automation engine).

Also called: alerts · who gets told what · notification system · team alerts

See it
The notification engine
Area
Operations
Group
Notifications
System
Notifications
Solves
2 named problems
01Recipients resolve in order, deduped by user id: roles in the org's preference row for this event, explicitly picked individuals, and every assignee of the project when the payload names one.
02The org roster comes from the org_members mirror in one indexed query, falling back to a Clerk scan if the mirror is not seeded.
03Channel gates come from the org's preference row, then the registry's per-event defaults, and can be hard-overridden per dispatch.
A fan-out diagram: one event → recipient resolution → three channels, with the mute and actor gates drawn as filters. notify.ts. Sample data — no customer information appears here.
How it works
  1. 1Recipients resolve in order, deduped by user id: roles in the org's preference row for this event, explicitly picked individuals, and every assignee of the project when the payload names one.
  2. 2The org roster comes from the org_members mirror in one indexed query, falling back to a Clerk scan if the mirror is not seeded.
  3. 3Channel gates come from the org's preference row, then the registry's per-event defaults, and can be hard-overridden per dispatch.
  4. 4Each person's own overrides can only QUIET a channel, never grant one.
  5. 5Failures are logged, never thrown — 'a notify hiccup doesn't break the user action'.
  6. 6Email routes through the org's own sender rather than a shared platform address.
Why we built it

The file header records the protocol it implements: 'customer-journey milestones (form submitted, contract signed, etc.) fire notifications only — never auto-start projects. The Start Project gate stays human-controlled.' The three entry points exist because rules implemented in only one of them silently did not apply to the others: 'That meant a builder who completed a stage still got their own "stage complete" email, and a person holding two accounts in the org got it twice.'

The problem
  • Rules that applied to one dispatcher and not the others
  • Recipient resolution scattered across emit sites
Sound familiar?
What you get
One place that decides who hears what
A failed notification can never break the action it followed
What's inside
Nothing sends until the account turns notifications on
A new account hears nothing — no bell, no email, no text — until somebody there deliberately switches notifications on for themselves.
Never tell someone what they just did
The person who performed the action is removed from the recipient list before anything is sent.
One email per person, not per account
When the same person holds two accounts in an org, the email is claimed by the first one and the second is silently dropped — the in-app row still lands on both.
Who gets told — roles, named people, and project assignees
Recipients come from three sources — the roles subscribed to that event, individually named people, and anyone assigned to the project — and assignment is itself an opt-in.
Re-detectable events fire once, not forever
Events a sweep can re-detect every few minutes carry a dedupe key and a window, so the same fact is announced once rather than each pass.
The event catalogue
One typed catalogue of every notification event — its category, severity, label, plain-English description, default recipients, default channels and deep link — used by the server, the bell, the toaster and both settings screens.
Notifications that open the right screen
Every notification's click-through is built from one canonical builder, so it lands on a real view with the right card, job or project already open.
Notification emails that are useful without opening the app
Each notification email carries the event label, a tone matched to its severity, an at-a-glance card of the facts, and a single button into the record — sent from the account's own sender.
Texts short enough to read on a lock screen
A text gets the same facts stacked one per line, with anything the title already said dropped, capped from the front so the junction's ordering decides what makes the cut.

See it on your own jobs

Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.

or keep browsing features →