Appearance
Notification Service API
Current Endpoints
GET /healthzGET /readyzGET /v1PUT /v1/notifications/{notificationId}/snapshotGET /v1/notificationsPOST /v1/notificationsPOST /v1/notifications/batchPOST /v1/notifications/read-allPOST /v1/notifications/delete-allGET /v1/notifications/{notificationId}POST /v1/notifications/{notificationId}/readPOST /v1/notifications/{notificationId}/deleteGET /v1/notifications/preferencesPATCH /v1/notifications/preferencesGET /v1/parent/alertsPOST /v1/parent/alerts/{notificationId}/readPOST /v1/parent/alerts/read-allPOST /v1/alerts/weak-topicPOST /v1/events/notification
Native Notification Foundation
Phase 9 starts with service-owned in-app notifications, preferences, parent alerts, and a small internal event/batch creation contract. Public /api/notifications*, /api/parent/alerts*, /api/alerts*, and /api/admin/inbox* remain legacy-proxied until gateway adapters enforce auth, recipient resolution, and legacy response compatibility.
Legacy evidence:
apps/api/src/modules/notifications/notifications.controller.ts:11-127maps notification inbox, parent alerts, weak-topic alert, and preferences routes.apps/api/src/modules/notifications/notification.service.ts:6-50delegates notification operations toAppDataService.apps/api/src/modules/app-data/app-data.notifications.ts:21-228implements inbox paging, filters, summary counts, mark-read, delete, parent alerts, weak-topic fanout, generic create, preferences, and preference gating.apps/api/prisma/schema.prisma:2703-2748definesNotification,NotificationPreference, andNotificationEvent.apps/api/prisma/schema.prisma:3160-3167defines notification event types.apps/api/src/modules/admin/inbox.controller.ts:32-146creates admin broadcast notifications/events/audit logs.apps/api/src/modules/app-data/app-data.classrooms-engagement.ts:511-583creates classroom notification fanout and notification events.apps/api/src/modules/app-data/app-data.exam-runtime-core.ts:807-925creates exam assigned, grade released, and attempt submitted notifications.apps/api/src/modules/support/support.service.ts:126-158andapps/api/src/modules/support/support.service.ts:351-361create support ticket events and admin notifications.apps/web/components/layout/app-shell.tsx:1094-1174consumes/api/notificationsfor the notification bell.apps/web/components/inbox/inbox-client.tsx:178-262consumes inbox paging, mark-read, and read-all routes.apps/web/components/admin/admin-inbox-client.tsx:143-225consumes admin inbox paging, user filtering, read/delete, and broadcast routes.apps/web/app/parent/alerts/page.tsx:92-123consumes parent alert list/read/read-all.apps/web/components/account/profile-settings-client.tsx:313-315andapps/web/components/account/profile-settings-client.tsx:603-607consume notification preferences at/notifications/preferences.
Native contract:
GET /v1/notificationssupportsuserId,organizationId,filter=ALL|UNREAD|READ|SUPPORT,q,page, andlimit.- Non-admin callers are scoped to
X-Actor-Id; admin callers may passuserIdor list all service-owned notifications. X-Organization-Idis used when creating rows if the input omitsorganizationId. It does not automatically filter inbox list/read-all/delete-all because legacy inbox behavior scopes primarily byuserIdand older rows can haveorganizationId = null.POST /v1/notificationscreates a single notification for an already-resolved recipient. The service stores ids only and does not query user-service.POST /v1/notifications/batchcreates many notifications for already-resolved recipients. This is the native equivalent of legacycreateManyfanout.POST /v1/events/notificationrecordsNotificationEventrows and can optionally create notifications in the same request.- Mark-read and delete routes preserve legacy ownership behavior: admin may operate globally, non-admin must own the notification.
- Preferences default to
enabledTypes={}and therefore allow all notification types unless a type is explicitly set tofalse. mutedUntilsuppressesshouldSenddecisions for future event fanout but does not hide already-created notifications.- Parent alerts are the same notification rows scoped to the parent actor; frontend category filtering remains type-name based.
- Weak-topic alert creation accepts resolved parent ids in native mode. Legacy still resolves
ParentStudentlinks; native user relationship lookup belongs to user-service/gateway adapters.
Known compatibility note:
- Legacy controller exposes preferences under
GET/PATCH /api/alerts/preferences, while current profile UI callsGET/PATCH /api/notifications/preferences. P9-001 exposes native/v1/notifications/preferences; gateway compatibility work must decide whether to preserve both public aliases.
Database:
services/notification-service/migrations/000002_notifications.sqlcreatesnotifications,notification_preferences, andnotification_events.- Cross-service ids such as
organization_id,user_id,actor_id,target_user_id,entity, andentity_idare stored as public ids only. - Admin audit logs stay
admin-serviceowned. Notification events are service-local delivery/audit inputs, not a replacement for admin audit history.
Rollback:
- Keep
/api/notifications*,/api/parent/alerts*,/api/alerts*, and/api/admin/inbox*routed to legacy. - Disable gateway callers for
/v1/notifications*,/v1/parent/alerts*,/v1/alerts*, and/v1/events/notification. - Drop notification-service local tables with the migration down step if local test data must be reset.
Gateway rehearsal:
deploy/gateway/routes.notifications-native-example.json and deploy/gateway/routes.notifications-native-localhost-example.json provide non-default route-table rehearsals for inbox and preference paths. They route only:
GET /api/notificationsGET /api/notifications/{notificationId}POST /api/notifications/{notificationId}/readPOST /api/notifications/{notificationId}/deletePOST /api/notifications/read-allPOST /api/notifications/delete-allGET /api/notifications/preferencesPATCH /api/notifications/preferencesGET /api/alerts/preferencesPATCH /api/alerts/preferences
to notification-service with gateway actor header injection. The route table does not require organization at the gateway because current shell notification fetches can send only Authorization. Notification create/batch/snapshot, parent alerts, weak-topic alerts, admin inbox, and broad notification/alert routes stay legacy-proxied until recipient resolution, support-ticket, role broadcast, and admin-audit adapters are explicit.
Run make test-notification-routes before any live or browser rehearsal.
Parent-alert gateway rehearsal:
deploy/gateway/routes.parent-alerts-native-example.json and deploy/gateway/routes.parent-alerts-native-localhost-example.json provide non-default route-table rehearsals for the parent-facing alert read surface. They route only:
GET /api/parent/alertsPOST /api/parent/alerts/{notificationId}/readPOST /api/parent/alerts/read-all
to notification-service with gateway actor header injection and a global PARENT role check. The route table does not require organization at the gateway because the parent alert UI is current-parent scoped. Weak-topic alert creation, parent-child relationship lookup, broad notification/alert/admin routes, and fallback stay legacy-proxied until recipient resolution and cross-service relationship adapters are explicit.
Run make test-parent-alert-routes before any live or browser rehearsal.
Non-goals for P9-001:
- Public gateway adapter/cutover.
- Email/SMS delivery provider.
- Recipient search and role broadcast resolution from user-service.
- Admin audit-log ownership.
- Support ticket storage.
- Analytics dashboards.