GET /api/public/notifications/inbox — Мои in-app уведомления
Назначение
Авторизованный пользователь читает свои in-app уведомления
(NotificationEntity, Channel = InApp) — страницу ленты-колокольчика с
пагинацией и число непрочитанных для бейджа. Owning-контекст — notifications, feature group inbox.
| Поле | Значение |
|---|
| Сервис/контекст | notifications |
| API area | public (JWT пользователя) |
| Feature group | inbox |
| Статус | draft |
| Документ | docs/06-services/notifications/api/public/list-my-notifications.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User |
| Auth policy / Permissions | notifications.inbox.read (own) |
| Record-level | владелец = RecipientUserId (актор) |
| Tenant isolation | доступ по RecipientUserId; TenantId не фильтрует (уведомления получателя) |
| Audit | read (тело — PII, наружу только владельцу) |
HTTP-контракт
| Поле | Значение |
|---|
| Method | GET |
| Route | /api/public/notifications/inbox |
| Success status | 200 |
| Idempotency header | не применяется |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
RecipientUserId | identity claims (JWT sub) | Guid | Да | не Guid.Empty | владелец |
Paging | query | PagingQuery | Да | PageSize 1..100 (default 20), keyset по CreatedAt desc | — |
UnreadOnly | query | bool | Нет | default false | — |
Kind | query | NotificationKind? | Нет | значение enum (фильтр) | — |
Модель ответа InboxPageModel
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
Items | страница NotificationItemModel | Да | лента уведомлений | — | Страница уведомлений получателя. |
UnreadCount | int | Да | число Status != Read при Channel = InApp | — | Число непрочитанных (бейдж). |
NotificationItemModel — проекция NotificationEntity: NotificationId
(Guid), Kind (NotificationKind), Title (string?), Body (string), DeepLink (string?),
IsRead (bool, = Status == Read), CreatedAt (DateTimeOffset).
Алгоритм
Операцию выполняет авторизованный пользователь (RecipientUserId из JWT; нет валидного JWT →
UNAUTHENTICATED); доступ — только к собственным уведомлениям. Проверяется корректность параметров: Paging
в границах, Kind — допустимое значение (нарушение → VALIDATION_FAILED). Возвращается страница in-app
уведомлений получателя (Channel = InApp, не удалённые, при UnreadOnly — только Status != Read,
опционально фильтр по Kind), отсортированных по CreatedAt убыв., плюс отдельно счётчик непрочитанных для
бейджа. Каждый элемент маппится в NotificationItemModel (IsRead = Status == Read).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
UNAUTHENTICATED | 401 | нет/невалидный JWT | «Требуется авторизация» | no retry |
VALIDATION_FAILED | 400 | Paging/Kind вне границ | «Проверьте параметры» | no retry |
Полнота выборки и границы
- В ленту входят только уведомления получателя с
Channel = InApp и DeletedAt = null; внешние каналы
(email/sms/push/…) в inbox не показываются. Доступ строго по RecipientUserId = actor — чужие записи
недоступны (INV-NTF-6), кросс-tenant фильтра нет.
UnreadCount — число in-app уведомлений в статусе != Read (то есть Pending/Sent; терминальный для
in-app — Read); считается по всему инбоксу получателя независимо от UnreadOnly/Kind-фильтра страницы.
- Фильтры комбинируются по «И»:
UnreadOnly = true оставляет Status != Read; Kind сужает по виду.
- Пустой инбокс или страница за пределами набора — не ошибка:
200, пустой Items, UnreadCount = 0.
- Keyset-пагинация по
CreatedAt desc устойчива к вставке новых уведомлений между запросами страниц
(курсор по CreatedAt, не смещается при дозаписи).
Соотношение с realtime
Новые in-app уведомления и изменения бейджа приходят push-ом через SignalR
(../../providers/in-app.md) сразу после persist. Этот endpoint — догоняющее
чтение: первичная загрузка ленты, восстановление после reconnect/пропуска push. Промежуточное состояние: до
получения push клиент может недосчитаться самого свежего уведомления — повторный list/reconnect его
подтянет (eventual, окно — секунды).
Совместимость и наблюдаемость
- Новые фильтры/поля — additive. Realtime-доставка новых уведомлений — через SignalR
(../../providers/in-app.md); этот endpoint — догоняющее чтение/reconnect.
- Logs:
inbox-read (RecipientUserId, page size); metrics: notifications_inbox_read_total,
notifications_inbox_unread_gauge.
Acceptance criteria
- Given пользователь с несколькими in-app уведомлениями, when запрашивает inbox, then
200,
страница по CreatedAt desc, UnreadCount = число непрочитанных.
- Given
UnreadOnly = true, when запрос, then только Status != Read; UnreadCount совпадает с
полным числом непрочитанных инбокса.
- Given пустой инбокс, when запрос, then
200, Items пуст, UnreadCount = 0 (не 404).
- Given
PageSize вне 1..100 или неизвестный Kind, when запрос, then 400
VALIDATION_FAILED.
- Given отсутствует/просрочен JWT, when запрос, then
401 UNAUTHENTICATED.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы