# Roadmap — what V3 onwards might include

Planning document for this internal notification service, not a delivery commitment and not a product we sell externally.

It is shared infrastructure for Inovus apps (Totum / Video Library, Assess, Review Tool, and whatever else adopts it). The question is only: what might our own products need next? Ideas below are informed by how mature notification systems are usually structured — useful as a checklist of needs, not a feature race against third-party vendors.

For architecture of the current system, see DESIGN.md. For resilience options already sketched, see High availability.


# Where we are (V2)

V2 is a standalone in-app notification inbox used by our applications:

Capability Status
Server/API-key publish to a user inbox Done
User list / unread / mark read / delete Done
Idempotent publish (dedupeKey) Done
OIDC auth + publisher scopes / API keys Done
Typed npm client (retry, ETag polling, React hooks) Done
Legacy product V1 HTTP compatibility Done (/api/Notification/*)
Email, SMS, push, chat (Slack/Teams) Out of scope
User preferences / mute / digest Out of scope
Realtime push (WebSocket / SSE) Out of scope (polling + ETag)
Shared bell UI / preference centre Out of scope (each app owns UI)
Workflows, templates, scheduled digests Out of scope

That is intentional. V2 extracted and hardened the inbox that used to live inside Video Library (auth, dedupe, standalone Worker, reliable client). It is the shared in-app feed + publish API for our estate — not a multi-channel engagement product and not something we intend to commercialise.

Current HTTP surface: /v2/notifications…. A future major API would likely live under /v3 (or stay compatible and add capabilities without a break) — naming is TBD when the first breaking change is real.


# What a fuller notification system usually covers

When teams grow an internal inbox service, needs tend to split into two layers:

┌─────────────────────────────────────────────────────────────┐
│  Our service (orchestration + inbox)                        │
│  decide who gets what · preferences · digests · in-app feed │
│  optional fan-out to other channels · delivery status       │
└───────────────────────────┬─────────────────────────────────┘
                            │ call out to (if we ever need them)
┌───────────────────────────▼─────────────────────────────────┐
│  Channel providers we already pay for / could plug in       │
│  Email (e.g. SES) · SMS · FCM/APNs · Slack / Teams webhooks │
└─────────────────────────────────────────────────────────────┘

Capabilities that often show up once an organisation has more than one product publishing notifications:

  1. One publish path — apps emit an event; the service applies policy (preferences, channels).
  2. Preferences — mute categories, quiet hours, choose email vs in-app only.
  3. Extra channels — email (most common), then push or staff chat when people aren’t in the SPA.
  4. Digests / delay-and-cancel — stop spamming; remind only if still unread.
  5. Templates — shared copy for email/push so each app doesn’t reinvent wording.
  6. Delivery visibility — support can answer “did it send / bounce?”
  7. Optional shared UI — one bell/preference panel reused across Totum apps.

We only build these when an Inovus product has a concrete need. Marketing-style campaigns, segments, and growth analytics are not in scope for this service.


# What we have vs what we might need

Need V2 today Worth considering later?
In-app inbox CRUD Yes Extend (archive, categories) if apps ask
Idempotent / deduped events Yes (dedupeKey) Keep as a hard invariant
Email / push / chat delivery No Only if a product needs reach outside the SPA
User preferences / quiet hours No Yes — before any second channel
Digests / delay / escalate No If several apps reinvent the same logic
Faster-than-poll inbox updates Polling + ETag If UX measures require it
Shared bell / preference UI Hooks only If two+ apps duplicate UI, or toast/inbox visuals diverge
Email/push templates No With channels, if copy should be central
Delivery timeline for support Logs only If support can’t answer from existing logs
Batch publish Loop + dedupe If fan-out volume hurts
Stronger org/tenant isolation Multi-issuer; one DB If business units need harder separation

# Guiding principles

  1. Internal shared service — designed for Inovus apps and operators, not external customers of a notification SaaS.
  2. Stay the system of record for the inbox — if we add email/push, the in-app row should still record what the user was told, unless we deliberately split that.
  3. Preferences before channels — outbound email without mute/category controls creates noise and support load (especially in healthcare-adjacent products).
  4. Reuse existing pipes — call SES / FCM / Slack webhooks etc.; don’t build SMTP or push infrastructure ourselves.
  5. Demand-drivenDESIGN.md’s YAGNI list still applies; name the app and the pain before scheduling work.
  6. Right-sized — solve our product problems; ignore feature checklists from commercial notification vendors.

# Candidate themes (V3 onwards)

Ordered by how often our apps are likely to hit them, not by a fixed release train. Many can ship as additive V2.x; only breaking contract changes need a hard /v3.

# 1. Richer inbox semantics

Why: Apps outgrow binary read/unread.

Possible additions:

  • Categories / topics (assessment, system, billing) for filtering and preferences
  • Archive / snooze (soft state; retention still applies)
  • Severity-driven “needs attention” views
  • Clearer actor / subject fields beyond opaque metadata
  • Per-notification expiry (in addition to global retention)

Trigger: Multiple apps need shared filters, or support asks for “hide without delete”.

# 2. User preferences and quiet hours

Why: Needed before anything leaves the in-app feed; also useful to mute noisy in-app categories.

Possible model:

  • Per user: global mute, per-category channel matrix (in-app / email / push)
  • Quiet hours + timezone
  • Defaults per source or category (e.g. security alerts cannot be fully muted)

Trigger: Before first outbound email/SMS/push goes live — or when users complain about inbox noise.

# 3. Extra channels (email first)

Why: Users aren’t always in the SPA; some workflows need email or mobile push.

Possible shape:

Publish event
    → create/update in-app notification (if preference allows)
    → enqueue channel jobs (email / push / …) honouring preferences
    → provider adapters + retries + dead-letter

Sensible internal order:

Phase Channel When it might matter
A Transactional email “Email me when X” from a product owner, with real volume
B Web / mobile push Native or PWA apps need OS notifications
C Slack / Teams Staff tools where people already live in chat
D SMS High-urgency only — cost and consent overhead

Even for email-only, plan for delivery status (queued / sent / bounced) so support isn’t blind.

Trigger: A named product requires reach outside the browser session.

# 4. Digests, delay, and cancellation

Why: Stops producers reinventing “remind later / don’t spam” in every app.

Examples that fit our domain:

  • Digest: “N new assessment comments in the last hour” → one inbox item (+ email if enabled)
  • Delay + cancel: remind in 24h unless the case was already opened
  • Escalate: in-app first; email if still unread after N hours
  • Fan-out: one domain event → many recipients (batch API or queue)

Prefer logic in the producer until several apps copy the same pattern; only then consider shared digest/delay primitives in this service.

Trigger: Three or more producers re-implement the same delay/digest/escalate behaviour.

# 5. Realtime delivery

Why: Polling is proven and cheap; some screens may want a faster bell.

Options already noted in DESIGN.md / HA docs:

  • SSE or WebSocket via Cloudflare Durable Objects (or similar), behind the existing client subscribe() API
  • Avoid coupling to Supabase Realtime unless we accept Cognito≠Supabase JWT constraints

Trigger: Measured UX need for low-latency updates — not “realtime for its own sake”.

# 6. Shared client UI (optional)

Why: If every Totum app builds its own bell — and a divergent toast look — we pay the cost repeatedly and the product feels inconsistent.

Possible direction:

  • Keep headless hooks as the default
  • Optional shared package for bell, feed, empty states, preference panel — and presentational primitives reusable by each app’s toast host
  • Better multi-tab sync (BroadcastChannel) and visibility-aware polling

Trigger: Two or more apps are copy-pasting the same notification UI, or toast and inbox look like different products.

# 7. Publish throughput and producer helpers

Why: Fan-out and spikes should not force every app to invent queues.

Possible additions:

  • Batch publish endpoint for many recipients
  • Queue-backed publish (e.g. Cloudflare Queues) for spikes and channel fan-out
  • Cancel/replace in-flight digests by key
  • Richer OpenAPI examples for any new payloads

Trigger: Sustained publish rate or fan-out size that makes per-row HTTP loops painful (DESIGN.md already cites ~>10/sec sustained as a revisit point).

# 8. Observability and support tooling

Why: Internal ops will ask “did the user get it?”

Possible additions:

  • Per-notification timeline (inbox created, email queued/sent/bounced)
  • Audited support lookup by userGuid + dedupeKey
  • Basic metrics: publish rate, unread age, channel failures
  • Correlation IDs from producer → inbox → channel send

Trigger: Support or engineering cannot answer delivery questions from Worker/Supabase logs alone.

# 9. Stronger isolation and compliance

Why: As more business units share the service, isolation and audit may need to harden.

Possible additions:

  • Explicit org/tenant dimension on notifications and preferences
  • Clearer staging vs prod separation (already partly true via Worker envs)
  • Retention / residency rules if required
  • Stronger audit trails for publish and preference changes
  • Whatever Inovus needs for SOC2 / clinical safety narratives

Trigger: A second business unit needs isolation beyond “different Cognito issuers”, or compliance asks for stronger evidence.


# Suggested sequencing (if we grow beyond the inbox)

A pragmatic order for our estate:

1. Categories + preferences (+ quiet hours)
2. Email + delivery status (transactional only)
3. Digests / delay-and-cancel for the noisiest producers
4. Shared UI only if apps are duplicating bells
5. Faster subscribe transport if UX requires it
6. Push / chat only with a named product need
7. Shared workflow primitives only if producers keep reinventing them
8. Deeper tenancy / compliance when org structure requires it

Skip or reorder freely. A thin batch publish or SSE slice can jump the queue if one producer is blocked.


# Explicit non-goals

  • Selling or operating this as an external notification product
  • Company-wide marketing automation (newsletters, promotional journeys, ad audiences)
  • Building and running email/SMS/push provider infrastructure ourselves
  • Mobile push without the relevant app team owning FCM/APNs credentials and consent UX
  • Matching any commercial vendor’s full feature list

# How to use this document

Audience Use
Product Prioritise against real pain in our apps; don’t schedule channels before preferences
Engineering Option sketches only — still need DESIGN updates (+ changesets if the npm client changes)
Agents / implementers Do not build from this file alone — wait for an explicit implementation brief

When a theme is green-lit, update DESIGN.md (scope + trade-offs), add acceptance criteria, and decide whether the HTTP API stays on /v2 (additive) or needs a /v3 break.


  • DESIGN.md — current V2 architecture and explicit out-of-scope list
  • Why V2 — why the standalone inbox exists
  • High availability — resilience options that overlap with scale/realtime work
  • App integration — how producers and SPAs use V2 today