GET /api/messenger/calls/{callId}/recordings — Записи 1:1-звонка
Список записей конкретного 1:1-звонка (call-session.md). Каждая запись —
проекция CallRecordingEntity (дом полей, статусы, инварианты — там).
Запись стартует по политике (Trigger = AutoPolicy); ручного старта нет.
Назначение
Вернуть участнику звонка (Caller/Callee) записи этого звонка — метаданные без бинаря и без ссылки на
скачивание (ссылка выдаётся отдельным методом get-recording-download.md). Обычно
на звонок приходится одна запись, но контракт возвращает коллекцию (устойчив к сегментации/повтору). Owning-
сервис — messenger, feature group recordings.
| Поле | Значение |
|---|
| Сервис/контекст | messenger |
| API area | recordings |
| Feature group | recordings |
| Статус | draft |
| Документ | docs/06-services/messenger/api/recordings/list-call-recordings.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User — участник звонка (Caller или Callee) |
| Auth policy / Permissions | permission messenger.recordings.view |
| Scope (RBAC) | платформа (person-level) |
| Record-level | доступ по membership звонка: actor.UserId ∈ {CallerUserId, CalleeUserId} |
| Tenant isolation | не применяется (ADR-0052); изоляция — по паре Caller/Callee |
| Audit | read (нечувствительный): выдаются метаданные без FileId-ссылки/URL |
HTTP-контракт
| Поле | Значение |
|---|
| Method | GET |
| Route | /api/messenger/calls/{callId}/recordings |
| Success status | 200 (OK) |
| Idempotency header | не применяется (чтение) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
CallId | route | Guid | Да | != Empty; существующий звонок, актор — участник (шаги 3–4) | — |
ActorUserId | identity claims | Guid | Да | из JWT; ∈ {Caller, Callee} | — |
Take | query | int | Нет | 1..100, default 20 | — |
Offset | query | int | Нет | >= 0, default 0 | — |
Модель ответа RecordingListModel
Коллекция никогда не возвращается «сырой» — оборачивается в RecordingListModel.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
Items | RecordingEntry[] | Да | проекция записей звонка | — | Записи звонка (обычно 0–1). |
Total | int | Да | computed | — | Всего записей по звонку (для пагинации). |
Take | int | Да | вход/echo | — | Размер страницы. |
Offset | int | Да | вход/echo | — | Смещение страницы. |
Элемент RecordingEntry
Компактная проекция CallRecordingEntity для списков (без ссылки на файл).
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
RecordingId | Guid | Да | CallRecordingEntity.Id | — | Идентификатор записи. |
SessionKind | SessionKind | Да | .SessionKind | — | Direct для 1:1-звонка. |
SessionId | Guid | Да | .CallSessionId | — | Сессия-владелец (= CallId). |
Status | RecordingStatus | Да | .Status | — | Pending/Recording/Uploading/Available/Failed/Deleted. |
Trigger | RecordingTrigger | Да | .Trigger | — | AutoPolicy для авто-записи. |
MediaType | CallMediaType | Да | .MediaType | — | Audio/Video. |
DurationSeconds | int? | Нет | .DurationSeconds | — | Длительность; null до Available. |
SizeBytes | long? | Нет | .SizeBytes | — | Размер файла; null до Available. |
Format | RecordingFormat? | Нет | .Format | — | Контейнер (Mp4/Webm/Mkv/Mp3); null до Available. |
CreatedAt | DateTimeOffset | Да | .CreatedAt | — | Момент провизии записи. |
AvailableAt | DateTimeOffset? | Нет | .AvailableAt | — | Момент готовности (→ Available). |
Ссылка FileId/URL в список не входит — скачивание идёт через
get-recording-download.md (аудит ReadSensitive).
Алгоритм
- Контекст и доступ. Операцию выполняет участник звонка (
User, Caller или Callee); ActorUserId
— из JWT; требуется право messenger.recordings.view. Запись включена флагом RECORDINGS_ENABLED, иначе
MSG_RECORDINGS_DISABLED.
- Проверка входа.
CallId непустой; Take/Offset в допустимых диапазонах — иначе ошибка валидации.
- Загрузка звонка. Звонок по
CallId; нет → MSG_CALL_NOT_FOUND.
- Право. Актор должен быть участником:
ActorUserId ∉ {Caller, Callee} → MSG_CALL_FORBIDDEN (либо
MSG_CALL_NOT_FOUND для сокрытия чужого звонка).
- Что читаем. Записи с
SessionKind = Direct и CallSessionId == CallId, сортировка по CreatedAt
убыв., страница Take/Offset; Total — общее число. Порядок стабилен (по CreatedAt убыв.); на звонок
обычно 0–1 записи, поэтому конкурентное появление новой записи в ходе пагинации на практике не создаёт
сдвига страниц. Статусы записей — снимок на момент чтения (запись, дозревающая до Available, при повторном
запросе покажет обновлённые DurationSeconds/SizeBytes/Format/AvailableAt).
- Результат.
RecordingListModel (метаданные без ссылок) → HTTP 200 OK. Пустой список — валидный
ответ (записи ещё нет / политика не включала авто-запись).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
VALIDATION_FAILED | 400 | CallId = Empty / Take/Offset вне диапазона | «Проверьте параметры запроса» | no retry |
MSG_CALL_NOT_FOUND | 404 | звонка нет или актор не участник (сокрытие) | «Звонок не найден» | no retry |
MSG_CALL_FORBIDDEN | 403 | актор не Caller/Callee | «Недостаточно прав» | no retry |
MSG_RECORDINGS_DISABLED | 503 | RECORDINGS_ENABLED = false | «Записи временно недоступны» | retry позже |
Acceptance Criteria
- Given звонок с одной
Available-записью, актор — участник, When запрашивает список, Then 200,
Items содержит 1 элемент с метаданными без FileId-ссылки, Total = 1.
- Given звонок без записей (политика не включала авто-запись), When участник запрашивает список,
Then
200, Items = [], Total = 0 (пустой — валидный ответ, не ошибка).
- Given актор не
Caller/Callee, When запрашивает список, Then 404 MSG_CALL_NOT_FOUND
(сокрытие чужого звонка).
- Given
Take/Offset вне диапазона (Take ∉ 1..100 / Offset < 0), When запрос, Then 400 VALIDATION_FAILED.
- Given
RECORDINGS_ENABLED = false, When любой запрос, Then 503 MSG_RECORDINGS_DISABLED.
Совместимость и наблюдаемость
- Новые optional-поля
RecordingEntry — additive; удаление/смена типа полей — breaking. Kafka-контракта нет.
- Logs:
call recordings listed (CallId, Total, участник); metrics: messenger_recordings_list_total{scope="call"},
latency p95; correlation через traceparent; ссылки/URL/бинарь в логи не пишутся.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы