# OpenSpec Change: enterprise-api-plugin-notifications - **Change ID**: enterprise-api-plugin-notifications - **Package**: `@suaveplan/api-plugin-notifications` - **Category**: api/comms - **Target path**: `api/comms/api-plugin-notifications` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/api-plugin-notifications.md` --- ## Summary Deliver `@suaveplan/api-plugin-notifications` on the genesis branch from scratch at enterprise quality. Multi-channel notification delivery plugin: in-app (stored + delivered over SSE), email (via pluggable `EmailProvider`), SMS (via pluggable `SmsProvider`), and push (Web Push). Template rendering via `@suaveplan/templates`. User preference management (per-channel opt-in/out). Delivery tracking with retry via `@suaveplan/retry`. Routes under `/notifications`. Per-table SurrealQL. Typed bus events: `notification:sent`, `notification:failed`, `notification:read`. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/api` (`workspace:^`) | `definePlugin` | | `@suaveplan/api-shared` (`workspace:^`) | Helpers | | `@suaveplan/api-plugin-auth` (`workspace:^`) | `requireAuth` | | `@suaveplan/api-plugin-database` (`workspace:^`) | SurrealDB | | `@suaveplan/templates` (`workspace:^`) | Notification template rendering | | `@suaveplan/suppression` (`workspace:^`) | Email/SMS suppression | | `@suaveplan/retry` (`workspace:^`) | Delivery retry | | `@suaveplan/events` (`workspace:^`) | Event bus | | `@suaveplan/error` (`workspace:^`) | `NotificationError` hierarchy | | `@suaveplan/rate-limiter` (`workspace:^`) | Send rate limits | | `hono` + `@hono/zod-openapi` + `zod` | Routes | | `@opentelemetry/api` | OTel | --- ## Public API surface ```ts function createNotificationsPlugin(config?: NotificationsPluginConfig): PluginDescriptor interface NotificationService { send(notification: SendNotificationInput): Promise markRead(notificationId: string, userId: string): Promise listNotifications(userId: string, query: NotificationQuery): Promise getUnreadCount(userId: string): Promise updatePreferences(userId: string, prefs: NotificationPreferences): Promise getPreferences(userId: string): Promise } ``` ### Error codes ```ts type NotificationErrorCode = | "NOTIFICATION_NOT_FOUND" | "NOTIFICATION_CHANNEL_DISABLED" | "NOTIFICATION_SUPPRESSED" | "NOTIFICATION_DELIVERY_FAILED" | "NOTIFICATION_TEMPLATE_NOT_FOUND" ``` --- ## File layout ```text api/comms/api-plugin-notifications/ package.json tsconfig.json vitest.config.ts surql/ (notification, notification_preference tables) src/ index.ts events.ts types/ errors/ adapters/email.port.ts adapters/sms.port.ts adapters/push.port.ts services/notification/ services/preferences/ services/delivery/ routes/notifications/ tests/integration.test.ts ``` --- ## Impact Prerequisites: `@suaveplan/api`, `@suaveplan/api-plugin-auth`, `@suaveplan/api-plugin-database`, `@suaveplan/templates`, `@suaveplan/suppression`, `@suaveplan/retry`.