--- title: Notification API tags: [api, notification, reference] --- # Notification API > **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)) Endpoints live under `/api/notifications/*`. Two routers are mounted: - New controller pattern: [`notificationControllerRoutes.ts`](../../backend/src/services/notification/notificationControllerRoutes.ts) (controller-backed, requires auth) - Legacy: [`notification/routes.ts`](../../backend/src/services/notification/routes.ts) (no auth gate — userId is passed in query/body) Both routers are mounted at `/api`, so the paths collide; the controller router wins for the shared paths (it is mounted first). The legacy router is still used by background scripts and admin tools that have no JWT context. Model: [[Notification]]. Notifications are **auto-deleted after 90 days**. Real-time delivery is via `new-notification` and `unread-count-update` Socket.IO events on `user-`. See [[Socket Events]]. ## List ### GET /api/notifications **Description:** Paginated notifications for the caller. **Auth required:** Bearer JWT (controller route); legacy variant takes `?userId=...`. **Query params:** - `page` (default 1) - `limit` (default 20) - `unreadOnly` (`true` | `false`, default `false`) **Response 200:** ```json { "success": true, "data": { "notifications": [Notification, ...], "pagination": { "page": 1, "limit": 20, "total": 42, "hasMore": true } } } ``` ### GET /api/notifications/unread-count **Description:** Just the integer unread count. **Auth required:** Bearer JWT **Response 200:** `{ "success": true, "data": { "unreadCount": 5 } }` ### GET /api/notifications/:id **Description:** Single notification by id. Available on the controller router. **Auth required:** Bearer JWT **Errors:** `404` not found, `403` not owner. > ⚠️ **KNOWN BUG:** The controller fetches only the 1 most-recent notification for the user and does an in-memory ID match. Any notification that is not the user's single latest will return `404` even if it exists and belongs to the user. Do not rely on this endpoint for fetching arbitrary notifications by id. ## Mutations ### PATCH /api/notifications/:id/read **Description:** Mark one notification as read. **Auth required:** Bearer JWT (controller); legacy variant requires `{ userId }` in body. **Response 200:** `{ success: true, data: { /* updated notification */ } }` **Side effects:** Emits `unread-count-update` to `user-`. ### PATCH /api/notifications/mark-all-read **Description:** Mark every notification for the caller as read. **Auth required:** Bearer JWT **Response 200:** `{ "success": true, "data": { "modifiedCount": 12 } }` > **Note:** Earlier versions of this documentation incorrectly listed this as `POST /api/notifications/read-all`. The correct path and method are `PATCH /notifications/mark-all-read`. ### PATCH /api/notifications/bulk/mark-read **Description:** Mark a list of notifications as read. **Auth required:** Bearer JWT **Request body:** `{ ids: string[] }` **Response 200:** `{ success, data: { modifiedCount } }` ### DELETE /api/notifications/:id **Description:** Delete a notification. **Auth required:** Bearer JWT **Errors:** `404` not found. ### DELETE /api/notifications/bulk/delete **Description:** Bulk delete. **Auth required:** Bearer JWT **Request body:** `{ ids: string[] }` ### POST /api/notifications **Description:** Create a notification. Primarily used by other services and admin tools. **Auth required:** Bearer JWT (controller); legacy variant is open. **Request body:** ```ts { userId: string; type: string; // e.g. "order_update", "chat_message", "payment_received" title: string; body?: string; data?: Record; channel?: "in_app" | "email" | "push"; } ``` **Response 201:** `{ success, data: { notification } }` **Side effects:** Emits `new-notification` to `user-`; also increments unread count via `unread-count-update`. ## Real-time socket events ### `new-notification` Emitted to `user-` when a new notification is created for that user. ### `unread-count-update` Emitted to `user-` whenever the unread notification count changes (e.g. after marking one or all as read, or after a new notification arrives). This is the canonical cross-tab sync event. > **Note:** Earlier docs referenced a `notification-read` socket event for cross-tab sync. That event does not exist. The real event is `unread-count-update`. ## Preferences Notification preferences live on [[User]] (`preferences.notifications.email | sms | push`). They are read and written through the [[User API]] (`GET /api/user/profile`, `PUT /api/user/profile`). ## Data retention Notifications are automatically deleted after **90 days**. ## Related - [[Notification]] - [[Notification Flow]] - [[Socket Events]]