Skip to content

Notification Service API

Current Endpoints

  • GET /healthz
  • GET /readyz
  • GET /v1
  • PUT /v1/notifications/{notificationId}/snapshot
  • GET /v1/notifications
  • POST /v1/notifications
  • POST /v1/notifications/batch
  • POST /v1/notifications/read-all
  • POST /v1/notifications/delete-all
  • GET /v1/notifications/{notificationId}
  • POST /v1/notifications/{notificationId}/read
  • POST /v1/notifications/{notificationId}/delete
  • GET /v1/notifications/preferences
  • PATCH /v1/notifications/preferences
  • GET /v1/parent/alerts
  • POST /v1/parent/alerts/{notificationId}/read
  • POST /v1/parent/alerts/read-all
  • POST /v1/alerts/weak-topic
  • POST /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-127 maps notification inbox, parent alerts, weak-topic alert, and preferences routes.
  • apps/api/src/modules/notifications/notification.service.ts:6-50 delegates notification operations to AppDataService.
  • apps/api/src/modules/app-data/app-data.notifications.ts:21-228 implements 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-2748 defines Notification, NotificationPreference, and NotificationEvent.
  • apps/api/prisma/schema.prisma:3160-3167 defines notification event types.
  • apps/api/src/modules/admin/inbox.controller.ts:32-146 creates admin broadcast notifications/events/audit logs.
  • apps/api/src/modules/app-data/app-data.classrooms-engagement.ts:511-583 creates classroom notification fanout and notification events.
  • apps/api/src/modules/app-data/app-data.exam-runtime-core.ts:807-925 creates exam assigned, grade released, and attempt submitted notifications.
  • apps/api/src/modules/support/support.service.ts:126-158 and apps/api/src/modules/support/support.service.ts:351-361 create support ticket events and admin notifications.
  • apps/web/components/layout/app-shell.tsx:1094-1174 consumes /api/notifications for the notification bell.
  • apps/web/components/inbox/inbox-client.tsx:178-262 consumes inbox paging, mark-read, and read-all routes.
  • apps/web/components/admin/admin-inbox-client.tsx:143-225 consumes admin inbox paging, user filtering, read/delete, and broadcast routes.
  • apps/web/app/parent/alerts/page.tsx:92-123 consumes parent alert list/read/read-all.
  • apps/web/components/account/profile-settings-client.tsx:313-315 and apps/web/components/account/profile-settings-client.tsx:603-607 consume notification preferences at /notifications/preferences.

Native contract:

  • GET /v1/notifications supports userId, organizationId, filter=ALL|UNREAD|READ|SUPPORT, q, page, and limit.
  • Non-admin callers are scoped to X-Actor-Id; admin callers may pass userId or list all service-owned notifications.
  • X-Organization-Id is used when creating rows if the input omits organizationId. It does not automatically filter inbox list/read-all/delete-all because legacy inbox behavior scopes primarily by userId and older rows can have organizationId = null.
  • POST /v1/notifications creates a single notification for an already-resolved recipient. The service stores ids only and does not query user-service.
  • POST /v1/notifications/batch creates many notifications for already-resolved recipients. This is the native equivalent of legacy createMany fanout.
  • POST /v1/events/notification records NotificationEvent rows 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 to false.
  • mutedUntil suppresses shouldSend decisions 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 ParentStudent links; 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 calls GET/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.sql creates notifications, notification_preferences, and notification_events.
  • Cross-service ids such as organization_id, user_id, actor_id, target_user_id, entity, and entity_id are stored as public ids only.
  • Admin audit logs stay admin-service owned. 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/notifications
  • GET /api/notifications/{notificationId}
  • POST /api/notifications/{notificationId}/read
  • POST /api/notifications/{notificationId}/delete
  • POST /api/notifications/read-all
  • POST /api/notifications/delete-all
  • GET /api/notifications/preferences
  • PATCH /api/notifications/preferences
  • GET /api/alerts/preferences
  • PATCH /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/alerts
  • POST /api/parent/alerts/{notificationId}/read
  • POST /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.

Go-platform documentation is generated from repository Markdown.