POST /api/messenger/conferences/{conferenceId}/recording/stop — Остановить запись конференции
Модератор конференции (conference-session.md) вручную останавливает
идущую запись (call-recording.md): перевод активной записи
Recording → Uploading, финализация — асинхронно через ingest. Ручного старта записи нет — старт
задаёт политика (RecordingPolicy); остановка нужна для согласия/комплаенса (участник-модератор может
прекратить запись чувствительной части встречи).
Назначение
Дать модератору контроль над записью: прекратить захват до штатного завершения конференции, зафиксировав
уже записанное. Захват может стартовать автоматически по политике; право остановить запись — механизм
согласия и комплаенса (запись без произвольного продолжения). После остановки файл дописывается и
выгружается тем же путём, что при завершении конференции. Owning-сервис — messenger, feature group
recordings.
Решение контракта: метод останавливает, но не запускает запись. Старт —
RecordingPolicy.ShouldAutoRecordпри старте сессии (INV-REC-3); отдельного «start recording» эндпоинта нет, чтобы политика записи оставалась единственным источником решения о старте.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | messenger |
| API area | recordings |
| Feature group | recordings |
| Статус | draft |
| Документ | docs/06-services/messenger/api/recordings/stop-recording.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User — модератор конференции |
| Auth policy / Permissions | permission messenger.recordings.manage |
| Scope (RBAC) | не применяется (доступ — по роли в конференции/сделке) |
| Record-level | модератор: actor.UserId == StartedByUserId конференции или владелец сделки (Deal.OwnerUserId) её ChatId |
| Tenant isolation | нет (кросс-tenant Deal-чата); изоляция — по роли модератора |
| Audit | update: переход записи Recording → Uploading, метка StoppedAt, инициатор остановки |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/messenger/conferences/{conferenceId}/recording/stop |
| Success status | 200 (OK) |
| Idempotency header | не применяется — повтор ловит guard состояния (шаг 5): вторая остановка застаёт Uploading/терминал → MSG_RECORDING_INVALID_STATE |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ConferenceId | route | Guid | Да | != Empty; существующая конференция, у неё активная запись в Recording, актор — модератор (шаги 3–5) | — |
ActorUserId | identity claims | Guid | Да | из JWT; модератор конференции | — |
Тела запроса нет.
Модель ответа RecordingModel
Проекция CallRecordingEntity (дом — сущность). Дельта: Status = Uploading,
проставлен StoppedAt; FileId/SizeBytes/DurationSeconds/Format ещё null (заполнятся при
финализации через ingest, → Available). Полное описание полей — get-recording.md.
Предусловия и постусловия
Предусловия. Конференция существует и активна (Active); по политике авто-записи (INV-REC-3) у неё есть
активная запись в статусе Recording; актор — модератор конференции (стартер или владелец сделки её ChatId);
RECORDINGS_ENABLED = true.
Постусловия (при успехе). Запись переведена Recording → Uploading, проставлен StoppedAt; внешнему
рекордеру подан сигнал остановки захвата; в hub-группу chat:{ChatId} доставлен RecordingState(Stopped) —
индикатор записи погас у участников; интеграционное событие не публикуется (готовность придёт позже, на
ingest). Файл и метаданные (FileId/SizeBytes/DurationSeconds/Format) в этой операции ещё не заполнены —
финализация асинхронна. При любом отказе состояние записи не меняется.
Алгоритм
- Контекст и доступ. Операцию выполняет модератор (
User);ActorUserId— из JWT; требуется правоmessenger.recordings.manage. Запись включена флагомRECORDINGS_ENABLED, иначеMSG_RECORDINGS_DISABLED. - Проверка входа.
ConferenceIdнепустой; иначе ошибка валидации. - Загрузка. Конференция по
ConferenceId; нет →MSG_CONFERENCE_NOT_FOUND. Её активная запись; нет →MSG_RECORDING_NOT_FOUND. - Право. Актор должен быть модератором:
ActorUserId == StartedByUserIdконференции или владелец сделки еёChatId. Иначе →MSG_CONFERENCE_FORBIDDEN. - Переход. Допустимый источник (INV-REC-2):
Status == Recording. Прочие статусы (Pending/Uploading/ терминал) →MSG_RECORDING_INVALID_STATE. При успехе внешнему рекордеру подаётся команда остановки, запись переводитсяRecording → Uploading, проставляетсяStoppedAt. Финализация (FileId/метаданные,→ Available) выполняется асинхронно ingest-эндпоинтом рекордера (../internal/jibri-recording-ingest.md) — не в этой операции. - Согласованность. Переход
→ Uploadingи намерение push фиксируются атомарно до push (persist-then-push). Конкурентные акторы, способные перевести ту же запись вUploading, — штатное завершение конференции (end-conference) и conference-room-reaper (сигнал остановки рекордера при закрытии осиротевшей комнаты): побеждает первый переход, проигравший видит на перечитанном состоянии не-Recording→MSG_RECORDING_INVALID_STATE(повтор — no-op). Итоговое состояние сходится к единственномуUploadingнезависимо от того, кто остановил запись. - Доставка сигнала. После сохранения —
RecordingState(Stopped)в hub-группуchat:{ChatId}(без PII/URL): индикатор записи гаснет у участников. - Результат.
RecordingModel(Status = Uploading,StoppedAt) → HTTP200 OK.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Integration events | none в этой операции — pin.messenger.call-recording-available.v1 публикует ingest при → Available |
| Realtime push (hub) | RecordingState(Stopped) → chat:{ChatId} (non-PII, без URL) — индикатор записи гаснет |
| External calls | внешний рекордер media-стека: команда остановки захвата (финализация — асинхронно) |
| Cache / projections / search | запись выходит из активной (ix_recording_active — по Uploading ещё активна до Available) |
| Notifications | системная строка «запись остановлена» — по политике чата (опционально) |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | ConferenceId = Empty | «Проверьте параметры запроса» | no retry |
MSG_CONFERENCE_NOT_FOUND | 404 | конференции нет | «Конференция не найдена» | no retry |
MSG_RECORDING_NOT_FOUND | 404 | у конференции нет активной записи | «Активная запись не найдена» | no retry |
MSG_CONFERENCE_FORBIDDEN | 403 | актор не модератор (не стартер и не владелец сделки) | «Недостаточно прав для остановки записи» | no retry |
MSG_RECORDING_INVALID_STATE | 409 | статус записи не Recording (уже Uploading/терминал, INV-REC-2) | «Запись уже остановлена» | no retry (перечитать) |
MSG_RECORDINGS_DISABLED | 503 | RECORDINGS_ENABLED = false | «Записи временно недоступны» | retry позже |
Acceptance Criteria
- Given конференция с активной записью
Recordingи актор-модератор, When останавливает запись, Then200,RecordingModelсStatus = UploadingиStoppedAt; рекордеру подан сигнал остановки;RecordingState(Stopped)доставлен вchat:{ChatId}— индикатор погас. - Given запись уже
Uploading/терминал (повтор или гонка сend-conference/reaper), When модератор повторно останавливает, Then409 MSG_RECORDING_INVALID_STATE(состояние не меняется, no-op). - Given актор не модератор (не стартер и не владелец сделки), When останавливает запись, Then
403 MSG_CONFERENCE_FORBIDDEN. - Given у конференции нет активной записи, When запрос остановки, Then
404 MSG_RECORDING_NOT_FOUND. - Given конференции нет, When запрос, Then
404 MSG_CONFERENCE_NOT_FOUND. - Given
RECORDINGS_ENABLED = false, When запрос, Then503 MSG_RECORDINGS_DISABLED.
Совместимость и наблюдаемость
- Новые optional-поля ответа — additive; смена маршрута/семантики перехода — breaking. Kafka-контракта у операции нет (событие готовности — на ingest).
- Logs:
recording stop requested/stopped/invalid-state(ConferenceId,RecordingId, модератор); metrics:messenger_recording_stop_total{result}, latency p95; correlation черезtraceparent; PII/URL/бинарь в логи не пишутся.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): GET /api/messenger/conferences/{conferenceId}/recordings — Записи конференции
- Use Cases (1): UC-MSG-007. Авто-запись звонка по сделке и её выдача участнику
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность (дом полей,
INV-REC-2, статусы,RecordingPolicy): call-recording.md; конференция-владелец: conference-session.md. - Соседние операции: get-recording.md, list-conference-recordings.md, get-recording-download.md; финализация — ../internal/jibri-recording-ingest.md.
- Событие готовности: ../../events/call-recording-available.md; ADR: 0062 медиа-стек и запись.