Skip to content

How notifications travel

The architecture is opaque to most coaches, but understanding the plumbing helps when things look slow or weird.

The pipeline

[Coach edits event] → [worker writes audit_log row]
[fanoutNotifications]
├── if change is "material" (time/location/coach-note/cancel/…)
│ ├── if ≤48h: enqueue 'immediate' rows per family
│ └── if >48h: enqueue 'digest' rows per family,
│ scheduled for next 6pm club tz
└── for each other coach with access + notify-on for
this calendar: fire web push directly (no queue)
[background sender, every 5 min]
├── for 'immediate' rows:
│ ├── fire Telegram channel post (once per channel)
│ ├── fire web push to every family subscription
│ └── markSent — NO email
└── for 'digest' rows (only at 6pm):
└── one combined email per opted-in contact

Why ≤48h has no email

To keep inbox noise down. Phone push and Telegram channels cover the urgency case; email goes back to its useful role as a daily summary at end of day.

Why digests are at 6pm

It’s late enough that the day’s edits are usually done, and early enough that families still have time to react before the next morning. Club-tz-aware (America/Denver), so it always lands at 6pm local even under DST.

What if a family has push, Telegram, AND email enabled?

For a ≤48h change: push fires + Telegram posts + no email. They see the change three places (lock screen, Telegram channel, the in-app banner next time they open) but only get one phone buzz from push (Telegram has its own).

For a >48h change: just the daily digest email at 6pm. Family page banner is also there but doesn’t push.

What if a family has none of those?

They see changes the next time they open their family page (the “what’s changed since last visit” banner + the chat panel below). This is the no-notification baseline.

The 5-minute sender

A background sender runs every 5 minutes and pushes out anything queued up. So a ≤48h coach edit can wait up to 5 minutes for its push to fire. Most of the time it’s faster (median ~2.5 min latency).

The daily brief uses the same 5-minute sender

For each coach with a brief_time set, the sender checks if the current club-tz HH:MM falls in the 5-min window from their target. If yes, it fires the brief push. A “last sent” stamp on the coach row prevents double-firing within the day.