4.1 KiB
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:
{
"data": [ ... ],
"total": 42,
"limit": 20,
"offset": 0
}
Notification object:
{
"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.
Admin Broadcast (v0.28.6)
POST /admin/notifications/broadcast
Auth: Admin only.
Sends a system.announcement notification to all active users via
NotifyMany. Also emits a system.broadcast WebSocket event for
real-time visibility.
Request body:
{
"title": "Scheduled Maintenance",
"message": "Servers will be down from 2-4 AM EST.",
"level": "warning"
}
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | Short summary shown in bell dropdown |
message |
string | yes | Detail text |
level |
string | no | info (default), warning, or critical |
Response:
{ "message": "broadcast sent", "count": 42 }
count is the number of active users notified.
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 |
system.broadcast |
{"title", "message", "level"} |
Admin announcement (v0.28.6) |
These are targeted via Hub.SendToUser — no room subscription
required. Used for badge sync across tabs.
Notification Types
See enums.md for the canonical list.
Types are free-form strings (domain.action convention) — new types
don't require migration. Core types include system.announcement
(admin broadcast, v0.28.6).
Retention
Background cleanup prunes notifications older than 90 days (default,
configurable via WithRetention). Runs daily after a 5-minute
startup delay.