# Notifications Real-time notification infrastructure with WebSocket delivery and optional email transport. **Auth:** All endpoints require authentication (JWT via `sb_token` cookie or `Authorization: Bearer` header). ### CRUD ``` GET /notifications → paginated list GET /notifications/unread-count → { "count": 5 } PATCH /notifications/:id/read → mark one as read POST /notifications/mark-all-read → mark all as read DELETE /notifications/:id ``` **`GET /notifications`** — paginated list for the authenticated user. Query parameters: | Param | Type | Default | Description | |-------|------|---------|-------------| | `limit` | int | 20 | 1–100, clamped | | `offset` | int | 0 | Pagination offset | | `unread_only` | bool | false | Filter to unread only | Response: ```json { "data": [ ... ], "total": 42, "limit": 20, "offset": 0 } ``` Notification object: ```json { "id": "uuid", "user_id": "uuid", "type": "role.fallback", "title": "Role Fallback Triggered", "body": "Primary model unavailable, using fallback", "resource_type": "channel", "resource_id": "uuid", "is_read": false, "created_at": "2025-01-15T12:00:00Z" } ``` `resource_type` and `resource_id` are optional — used for click-to-navigate. The resource may be deleted (no FK constraint). ### Preferences Users control per-type delivery: ``` GET /notifications/preferences → { "data": [...] } PUT /notifications/preferences/:type ← { "in_app": true, "email": false } DELETE /notifications/preferences/:type → reset to default ``` **`GET /notifications/preferences`** returns all preferences set by the user. Empty array if none are configured. **`PUT /notifications/preferences/:type`** creates or updates a preference. Both `in_app` and `email` fields are optional (merges with existing values if set). **`DELETE /notifications/preferences/:type`** removes the preference, falling back to the next level in the resolution chain. Idempotent — deleting a non-existent preference returns 200. Resolution: specific type pref → user wildcard `*` pref → system default (`in_app=true`, `email=false`). Returns 503 if the preference store is not available (unmanaged mode). ### Admin ``` POST /admin/notifications/test-email → send test email to requesting admin ``` Requires admin role. Loads SMTP config from platform settings, sends a test message to the admin's registered email address. ### WebSocket Events | Event | Payload | Description | |-------|---------|-------------| | `notification.new` | Full notification object | New notification created | | `notification.read` | `{"id": "uuid"}` | Single notification marked read | | `notification.read` | `{"action": "mark_all_read"}` | All notifications marked read | These are targeted via `Hub.SendToUser` — no room subscription required. Used for badge sync across tabs. ### Notification Types See [enums.md](enums.md#notification-types) for the canonical list. Types are free-form strings (`domain.action` convention) — new types don't require migration. ### Retention Background cleanup prunes notifications older than 90 days (default, configurable via `WithRetention`). Runs daily after a 5-minute startup delay. ---