Notifications#
These endpoints back the Notifications and Sounds settings screen. All require authentication.
Preferences are two-layer:
- Account-level (
NotifPrefs) — synced across every device the user is signed in on. Covers per-category defaults (private / group chats), the global message-preview switch, and the badge-counter rules. - Per-device (
DeviceNotifPref) — scoped to the current session (thes_…token's device). Theenabledfield is the master kill switch for that one device;in_app.*describes in-app banner behaviour and is client-enforced (the server stores and echoes it but never consults it when building an APNs payload).
Per-conversation exceptions are not here — they stay in the existing mute system (POST /api/setChatNotifications).
Everything defaults to on, so a brand-new user/device receives notifications out of the box without writing any prefs.
POST /api/getNotificationPrefs#
Returns the account-level prefs plus this device's prefs (resolved from the caller's session). Takes no body.
curl -X POST http://localhost:4000/api/getNotificationPrefs \
-H "authorization: Bearer s_…"{
"ok": true,
"result": {
"account": {
"private_chats": { "enabled": true, "preview": true, "sound": true },
"group_chats": { "enabled": true, "preview": true, "sound": true },
"message_preview": true,
"badge": { "enabled": true, "include_groups": true, "count_messages": true }
},
"device": {
"enabled": true,
"in_app": { "sounds": true, "vibrate": true, "preview": true }
},
"vapid_public_key": "BLgvK3lCr09ssJ6dsmxX9kFD…"
}
}vapid_public_key is present only when the server has Web Push configured (the VAPID_* env vars). The web client passes it to PushManager.subscribe({ applicationServerKey }); when it's absent the client hides the "enable browser push" affordance.
POST /api/setNotificationPrefs#
Replace the account-level prefs (send the full NotifPrefs object). Returns the stored account.
curl -X POST http://localhost:4000/api/setNotificationPrefs \
-H "authorization: Bearer s_…" -H "content-type: application/json" \
-d '{
"private_chats": { "enabled": false, "preview": true, "sound": true },
"group_chats": { "enabled": true, "preview": true, "sound": true },
"message_preview": true,
"badge": { "enabled": true, "include_groups": true, "count_messages": true }
}'POST /api/setDeviceNotificationPrefs#
Replace this device's prefs (the current session). Returns the stored device. Fails with 400 invalid_argument if the caller has no session (e.g. a dev: or bot mb_ token).
curl -X POST http://localhost:4000/api/setDeviceNotificationPrefs \
-H "authorization: Bearer s_…" -H "content-type: application/json" \
-d '{ "enabled": true, "in_app": { "sounds": true, "vibrate": false, "preview": true } }'Push delivery & device tokens#
POST /api/registerPushToken ({ token, platform? }) registers a push endpoint and binds it to the caller's current session. Revoking that session (terminateSession / terminateOtherSessions) drops its token, so that device stops receiving push. Tokens registered with a session-less token (dev: / mb_) are stored unbound and still deliver, just without a per-device switch.
platform selects the transport:
ios(or omitted) —tokenis the APNs device token; delivered over APNs.web—tokenis the browser'sPushSubscriptionserialized as JSON ({ "endpoint": …, "keys": { "p256dh": …, "auth": … } }); delivered as an encrypted Web Push (RFC 8291aes128gcm+ VAPID). The payload is{ "title", "body" }, which the service worker reads offevent.data.json().
# web push registration (token is the stringified subscription)
curl -X POST http://localhost:4000/api/registerPushToken \
-H "authorization: Bearer s_…" -H "content-type: application/json" \
-d '{ "platform": "web", "token": "{\"endpoint\":\"https://…\",\"keys\":{\"p256dh\":\"…\",\"auth\":\"…\"}}" }'A dead web subscription (the push service returns 404/410 Gone) is pruned automatically on the next send. The server needs the VAPID_PRIVATE_PEM_BASE64, VAPID_PUBLIC_KEY, and VAPID_SUBJECT env vars; without them web pushes are silently skipped (APNs is unaffected).
When a human recipient is offline (no live WebSocket), the server pushes to each of the recipient's registered endpoints (dispatched by platform) only if all of the following hold:
- the recipient's category for that conversation (
private_chatsfor a DM,group_chatsfor a group) hasenabled: true; - the conversation is not muted for the recipient (
is_muted); - the recipient's device pref for the token's session has
enabled: true(unbound/legacy tokens are treated as on).
The notification body shows the message text only when both the global message_preview and that category's preview are on; otherwise it falls back to a generic "New message". This gate is shared by APNs and Web Push.