technical:features:27_announcements_and_notification_banners
Table of Contents
27 — Announcements and notification banners
Technical reference. For task-based help, see the corresponding user guide.
Documentation status: reviewed against implementation · Reviewed: 2026-07-21 · Viewers: family and coach · Authoring: global owner/super-admin only · Surfaces: mobile web, coach portal and platform API
Purpose
Notification banners deliver time-bound product or service announcements to a controlled audience. The platform decides eligibility; mobile web presents the highest-priority eligible message and records interaction; the coach portal provides a read-only current/past inbox for coach announcements.
User outcomes
| Persona | Outcome |
|---|---|
| Family or coach | Receives only an active announcement matching role, email targeting and device platform. |
| Viewer | Expands longer content, follows a safe CTA and dismisses when the author allows it. |
| Coach in portal | Reviews current and past coach announcements without being able to author them. |
| Owner/super-admin | Creates, schedules, publishes, pauses, archives and audits global banners. |
Banner model
| Dimension | Supported values / rule |
|---|---|
| Lifecycle | draft, scheduled, live, paused, expired, archived. |
| Audience | families, coaches or all. |
| Scope | Current production authoring is global; legacy/domain support also models coachClients. |
| Platform | ios, android, web or all. |
| Schedule | Optional start/end; end cannot precede start. |
| Priority | Integer 0–100; higher wins. Equal priority sorts by newest start, then creation. |
| Target overrides | Up to 200 unique normalized include emails and 200 exclude emails; exclusion wins. |
| Content | Required title (maximum 120), optional subtitle/message/icon, CTA label/URL. |
| Behaviour | Dismissible flag and optional push flag. A non-dismissible banner must have a CTA. |
Viewer flow
1. Resolve eligibility
- Mobile requests
GET /notification-banners/eligiblewithios,androidorwebfrom the current runtime. - The platform requires an active lifecycle state, the current schedule window, matching platform and audience/scope, and include/exclude eligibility.
- A prior
seen,dismissedorclickedreceipt removes that banner from the eligible feed for the actor. - Eligible banners are returned in priority/start/creation order. The host presents one banner at a time.
- The session cache is scoped to actor, authentication session and platform; home refreshes are throttled to avoid repeated requests.
2. Present and interact
- Tone/icon maps to info, success, warning or urgent presentation.
- Short copy remains compact. A CTA, newline or longer message enables expansion/detail.
- First display records
seenonce for that actor/banner. - The close control exists only when
dismissibleis true. Dismissal recordsdismissedand removes the cached item. - CTA selection records
clickedbefore navigation. app://feedbackandapp://getting-startedresolve to the viewer's role-specific in-app route. Other links use the native/browser external opener.- A banner reached from a push/deep-link query can be shown locally; a purely local fallback does not fabricate platform receipts.
3. Review in the coach portal
GET /coach/notification-bannersreturns global coach/all banners available to that active coach organization context.- The portal separates Current (started scheduled/live and not ended) from Past, shows audience/availability/published metadata and opens a detail modal.
- External CTA links render only when they parse as HTTP or HTTPS.
- The optional Family tab belongs to the family-activity notification inbox documented in Feature 22, not banner authoring.
Owner authoring lifecycle
- The owner creates a draft with content, audience, schedule, priority, targeting, CTA, dismissibility and push preference.
- Publishing/scheduling sets the status and publication audit metadata. Pausing removes it from active eligibility; expiry follows its lifecycle/end window; archive retains history.
- Owner APIs expose aggregate per-user receipts for operational review.
- All create/update/status/receipt-list/delete routes under
/coach/notification-bannersdeliberately return 403. Dormant coach banner editor components are not a supported product surface.
Validation and user-visible states
| State | Behaviour |
|---|---|
| No eligible banner | No banner space is reserved. |
| Loading/fetch failure | Core page remains usable; announcement loading is non-blocking. |
| Non-dismissible | No close button; author must provide a CTA. |
| Already received | Subsequent eligible fetches suppress it for that actor. |
| Paused, expired or outside dates | Not returned to viewers; may appear in portal history. |
| Unsafe external URL in portal | No actionable external link is rendered. |
| Seen-receipt write fails | Core page remains usable and the host may retry recording seen. |
| Dismiss/click receipt write fails | The interaction is not completed; the banner stays available so the viewer can retry. |
Platform API contract
| Method and route | Actor | Purpose |
|---|---|---|
GET /notification-banners/eligible | Authenticated viewer | Unreceived eligible banners for a platform. |
GET /notification-banners/active | Authenticated viewer | Active eligible banners without receipt suppression. |
POST /notification-banners/:id/receipts | Eligible viewer | Upsert seen, dismissed or clicked timestamps plus device/app metadata. |
GET /coach/notification-banners | Coach | Read coach-facing global announcement history. |
GET/POST/PATCH/DELETE /admin/notification-banners… | Super-admin | List, author, transition, delete and inspect receipts. |
POST/PATCH/DELETE /coach/notification-banners… | Coach | Explicitly denied. |
Authorization and privacy
- Authoring and receipt inspection are owner/super-admin operations; coach portal access is read-only.
- Eligibility is computed from the authenticated platform actor. Query parameters cannot select another audience or email.
- Receipts contain user/banner identity, interaction timestamps and optional device platform/app version; they do not grant targeting access.
- External URLs are not treated as trusted application routes. In-app schemes are mapped through an explicit allowlist.
- Banner push uses the registered-device system in Feature 32; the in-app eligibility checks remain authoritative when opened.
Coach-side authoring code exists in dormant components and domain helpers, but every coach mutation route is intentionally denied. Document and support owner-only global authoring until that boundary is deliberately changed end to end.
Acceptance checklist
- [ ] Audience, platform, schedule, lifecycle and email targeting are all enforced server-side.
- [ ] Highest-priority eligible banner is shown without blocking the host page.
- [ ] Seen/dismissed/clicked receipts are idempotently upserted.
- [ ] Dismissibility and CTA rules match the shared schema.
- [ ] In-app CTA schemes map only to supported routes; external links use a safe opener.
- [ ] Coach portal classifies current/past correctly and cannot mutate banners.
- [ ] Owner status transitions and receipt access produce audit events.
Source evidence
- Mobile web:
mobile-web/src/components/NotificationBannerHost.tsx,mobile-web/src/components/notification-banner-cta.ts - Coach portal:
admin-web/src/routes/NotificationsPanel.tsx - Platform:
platform/packages/shared/src/notification-banners.ts,platform/apps/api/src/routes/notification-banner-routes.ts,platform/apps/api/src/services/notification-banner-service.ts,platform/apps/api/src/services/prisma-notification-banner-service.ts
