Notifications

Notifications

Notifications are generated server-side for events like new followers, incoming replies, and I Dig! reactions. The notification tray is the main consumer.

Endpoint table

MethodPathAuthDescription
GET/api/notificationsSession or BearerNotification tray. Required query: scope=tray. Returns { unreadCount, items }.
GET/api/notifications/:idSession or BearerGet a single notification by ID.
DELETE/api/notifications/:idSession or BearerDelete a notification.
PATCH/api/notifications/:id/readSession or BearerMark one notification read (idempotent). Returns { ok: true }.
POST/api/notifications/mark-all-readSession or BearerMark all unread notifications read. Returns { ok: true, updated: N }.
GET/api/user/notification-preferencesSession or BearerPer-event channel preferences. Returns { events: [ ... ] }.
PATCH/api/user/notification-preferencesSession or BearerToggle channels for one event. Body: { key, channels }. Returns the updated event.

Notification object

FieldTypeDescription
idstringUnique notification ID.
titlestringShort heading (e.g. "New follower").
bodystringFull notification text.
actionUrlstring | nullRelative URL to navigate to when clicked (e.g. /profile/someuser).
typestring | nullCategory string (e.g. "follow", "dig", "reply").
metadataobjectArbitrary structured data attached by the server.
createdAtstring (ISO 8601)When the notification was created.
readAtstring | nullWhen it was read; null if still unread.

Fetching the tray

GET /api/notifications?scope=tray
{
  "unreadCount": 3,
  "items": [
    {
      "id": "notif_abc001",
      "title": "New follower",
      "body": "someuser started following you.",
      "actionUrl": "/profile/someuser",
      "type": "follow",
      "metadata": {},
      "createdAt": "2025-06-11T10:00:00.000Z",
      "readAt": null
    }
  ]
}

unreadCount is the total count across all unread notifications. items contains up to the user's configured tray limit (default 20, max 40) of the most recent unread items. The scope=tray query parameter is required — omitting it returns 400.

Marking notifications read

Mark a single notification read (safe to call multiple times):

PATCH /api/notifications/notif_abc001/read

Response (200): { "ok": true }

Mark all unread notifications read at once:

POST /api/notifications/mark-all-read

Response (200): { "ok": true, "updated": 3 }updated is the count of notifications that were changed from unread to read.

Deletion

DELETE /api/notifications/notif_abc001

Deletes the notification entirely (rather than just marking it read). The action is irreversible.

Notification preferences

Preferences enable or disable individual delivery channels per event.

Preferences cover only the events the server actually emits, and only the channels that exist. There is no email channel, and follow supports only push. Earlier client assumptions of { push, inApp, email } for every event are not accurate.

Event keyChannelsCovers
digpush, inAppDigs on your messages.
pushpush, inAppPushes (reposts) of your messages, with or without commentary.
followpush onlyNew followers and follow requests.

When a user has never set a preference, every channel defaults to enabled. Toggling a channel off suppresses that delivery channel for that event.

Get preferences

GET /api/user/notification-preferences
{
  "events": [
    { "key": "dig",    "label": "Digs on your messages",        "description": "...", "channels": { "push": true, "inApp": true } },
    { "key": "push",   "label": "Pushes of your messages",      "description": "...", "channels": { "push": true, "inApp": true } },
    { "key": "follow", "label": "New followers & follow requests","description": "...", "channels": { "push": true } }
  ]
}

Each event's channels object contains only the keys that event supports (note follow has no inApp key, and no event has an email key).

Update one event

PATCH /api/user/notification-preferences
Content-Type: application/json

{ "key": "dig", "channels": { "push": false, "inApp": true } }

Returns the updated single event object:

{
  "key": "dig",
  "label": "Digs on your messages",
  "description": "...",
  "channels": { "push": false, "inApp": true }
}

Returns 400 for an unknown key, a missing/invalid channels object, a channel not supported by that event (e.g. inApp or email for follow), a non-boolean channel value, or when no valid channels are provided.