openapi: 3.0.3 info: title: Transmittal Service API version: "1.0.0" description: | REST API сервиса **transmittal-api** (`pdm/transmittal-api`) — управление трансмитталами (передачей документации на согласование), их шаблонами, шагами/действиями маршрута согласования и генерацией актов. Сервис написан на Python (**FastAPI**). Приложение собирается фабрикой `get_application` в `src/transmittal_service/app/http_server.py`. Роутинг состоит из двух групп: - публичный API — префикс `/api/v1` (`controller/http/api/v1/api.py`); - внутренний API — префикс `/internal/v1` (`controller/http/internal/v1/api.py`), предназначен для вызовов внутри кластера (через ingress не публикуется). Интерактивная документация Swagger доступна по `/api/docs`, схема — по `/api/openapi.json` (с учётом `root_path`). ### Аутентификация Публичные эндпоинты (кроме `/api/v1/healthcheck`) требуют аутентификации. Токен передаётся заголовком `Authorization: Bearer ` (FastAPI `HTTPBearer`). Поддерживаются два режима (`controller/http/security.py`): 1. **Zitadel** — если передан дополнительный заголовок `identity` (`Identity `), полезная нагрузка берётся из этого токена (`urn:zitadel:iam:user:metadata`). Валидность проверяется на уровне Istio, подпись сервисом не проверяется. 2. **sarex-backend** — если заголовка `identity` нет, подпись основного токена проверяется публичным RSA-ключом (`TRANSMITTAL_SERVICE_AUTH__PUBLIC_KEY`, алгоритм `RS512`). Внутренние эндпоинты (`/internal/v1/*`) аутентификации на уровне приложения не требуют — ограничение доступа обеспечивается сетевым слоем. ### Пагинация Списочные ответы используют «bookmark»-пагинацию. Ответ оборачивается в `Page` — `{ result, next, prev }`, где `next`/`prev` — курсоры (bookmark). Размер страницы задаётся параметром `limit` (в теле или query, зависит от эндпоинта), продолжение — параметром `bookmark`. Часть ответов оборачивается в более простой `Result` — `{ result }` (без курсоров). ### Обработка ошибок Ошибки возвращаются в формате **RFC 7807** (`application/problem+json`), схема `ApiError` — `{ type, title, status, detail, instance }` (`controller/http/middleware.py`, класс `ExceptionHandler`). Идентификатор запроса возвращается в заголовке `X-Request-Id`, длительность обработки — в `Server-Timing`. ### Замечания (расхождения кода) - Ошибка «ресурс не найден» (`NotFound`) маппится на статус **`410 Gone`**, а не на привычный `404 Not Found`. - При отсутствии/некорректности `Authorization` FastAPI `HTTPBearer` возвращает `403`, тогда как ошибки разбора токена в middleware дают `401`. - Ошибки валидации тела/параметров запроса (Pydantic) отдаются FastAPI в стандартном формате `422` (не `application/problem+json`). - Конфликт имени шаблона (`TemplateNameIsNotUnique`) возвращает `409`. contact: name: transmittal-api url: https://gitlab/pdm/transmittal-api servers: - url: https://api.sarex.io/transmittals description: Production (ingress, root_path=/transmittals) - url: https://stage-api.sarex.io/transmittals description: Stage (ingress, root_path=/transmittals) - url: http://transmittal-service.transmittal-api-stage.svc.cluster.local description: Внутрикластерный адрес (ClusterIP, порт 80 → 8000). Единственный способ достучаться до /internal/v1 - url: http://localhost:8001 description: Локальный запуск (Uvicorn, порт по умолчанию 8001) tags: - name: infra description: Служебные эндпоинты (проверка доступности) - name: transmittals description: Трансмитталы — создание, просмотр, согласование, поиск, акты - name: transmittal_templates description: Шаблоны трансмитталов - name: steps description: Шаги маршрута согласования - name: internal description: Внутренние эндпоинты (только внутри кластера) security: - bearerAuth: [] paths: # ========================================================================== # Infra # ========================================================================== /api/v1/healthcheck: get: tags: [infra] summary: Проверка доступности зависимостей description: | Параллельно проверяет доступность всех внешних зависимостей (БД, S3, и HTTP-сервисов) и возвращает агрегированный статус `healthy` или `partially_healthy`. Аутентификация не требуется. operationId: healthcheck security: [] responses: '200': description: Статус доступности зависимостей content: application/json: schema: $ref: '#/components/schemas/HealthCheckResponse' # ========================================================================== # Steps # ========================================================================== /api/v1/steps: get: tags: [steps] summary: Список шагов operationId: getSteps parameters: - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 32767 default: 100 - name: bookmark in: query required: false schema: type: string nullable: true responses: '200': description: Страница шагов content: application/json: schema: $ref: '#/components/schemas/Page_StepReadResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/steps/{id}: get: tags: [steps] summary: Шаг по id operationId: getStep parameters: - name: id in: path required: true schema: type: integer minimum: 0 maximum: 32767 responses: '200': description: Шаг content: application/json: schema: $ref: '#/components/schemas/StepReadResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } # ========================================================================== # Transmittals # ========================================================================== /api/v1/transmittals: post: tags: [transmittals] summary: Список трансмитталов ресурса description: Пагинированный список трансмитталов по `resource_id` со сводной статистикой по статусам. operationId: listTransmittals requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalListRequestDto' responses: '200': description: Страница трансмитталов и статистика по статусам content: application/json: schema: $ref: '#/components/schemas/TransmittalListResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/create: post: tags: [transmittals] summary: Создать трансмиттал description: | Создаёт трансмиттал. Требуется хотя бы один документ (`documents_to_approve`) и хотя бы один получатель (`receivers`). operationId: createTransmittal requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalCreateRequestDto' responses: '200': description: Идентификатор созданного трансмиттала content: application/json: schema: $ref: '#/components/schemas/TransmittalCreateResponseDto' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/global_statuses: post: tags: [transmittals] summary: Глобальные статусы по набору ресурсов description: Возвращает статистику статусов трансмитталов по списку `resource_ids`. operationId: getGlobalStatuses requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalGetGlobalStatusesRequestDto' responses: '200': description: Статистика статусов по ресурсам content: application/json: schema: $ref: '#/components/schemas/TransmittalGetGlobalStatusesResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/status: get: tags: [transmittals] summary: Справочник статусов operationId: listStatuses responses: '200': description: Список типов статусов content: application/json: schema: $ref: '#/components/schemas/StatusesListResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } /api/v1/transmittals/count: get: tags: [transmittals] summary: Число открытых трансмитталов пользователя description: Количество незакрытых трансмитталов, требующих действия текущего пользователя. operationId: myOpenTransmittalsCount responses: '200': description: Количество открытых трансмитталов content: application/json: schema: $ref: '#/components/schemas/TransmittalOpenCountResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } /api/v1/transmittals/search: post: tags: [transmittals] summary: Поиск трансмитталов в ресурсе description: Поиск и фильтрация трансмитталов в рамках одного ресурса (`resource_id`). operationId: searchTransmittals requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalSearchFilterRequestDto' responses: '200': description: Страница трансмитталов и статистика по статусам content: application/json: schema: $ref: '#/components/schemas/TransmittalListResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/search/resources: post: tags: [transmittals] summary: Поиск по нескольким ресурсам description: Фильтрация трансмитталов по списку ресурсов (`resource_ids`); возвращает статистику статусов по ресурсам. operationId: searchTransmittalsResources requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalSearchFilterDto' responses: '200': description: Статистика статусов по ресурсам content: application/json: schema: $ref: '#/components/schemas/TransmittalGetGlobalStatusesResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}: parameters: - $ref: '#/components/parameters/TransmittalId' get: tags: [transmittals] summary: Трансмиттал по id operationId: getTransmittal responses: '200': description: Трансмиттал с историей действий и документами content: application/json: schema: $ref: '#/components/schemas/TransmittalReadResponseDto' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } delete: tags: [transmittals] summary: Удалить трансмиттал operationId: deleteTransmittal responses: '204': { description: Удалено } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}/approve: parameters: - $ref: '#/components/parameters/TransmittalId' put: tags: [transmittals] summary: Принять трансмиттал description: Фиксирует действие «принять» текущего пользователя на текущем шаге (с опциональным комментарием). По завершении может инициировать генерацию акта. operationId: approveTransmittal requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserActionCreateRequest' responses: '200': description: Созданное действие пользователя content: application/json: schema: $ref: '#/components/schemas/UserActionCreateResponseDto' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}/decline: parameters: - $ref: '#/components/parameters/TransmittalId' put: tags: [transmittals] summary: Отклонить трансмиттал description: Фиксирует действие «отклонить» текущего пользователя на текущем шаге (с опциональным комментарием). operationId: declineTransmittal requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserActionCreateRequest' responses: '200': description: Созданное действие пользователя content: application/json: schema: $ref: '#/components/schemas/UserActionCreateResponseDto' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}/act_available: parameters: - $ref: '#/components/parameters/TransmittalId' get: tags: [transmittals] summary: Доступность акта description: Признак готовности акта для скачивания по данному трансмитталу. operationId: actAvailable responses: '200': description: Готовность акта content: application/json: schema: $ref: '#/components/schemas/TransmittalGetActReadinessResponse' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}/download_act: parameters: - $ref: '#/components/parameters/TransmittalId' get: tags: [transmittals] summary: Скачать акт description: Возвращает presigned-URL для скачивания акта из S3. operationId: downloadAct responses: '200': description: Presigned-URL акта content: application/json: schema: $ref: '#/components/schemas/TransmittalDownloadActResponse' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittals/{transmittal_id}/link_review: parameters: - $ref: '#/components/parameters/TransmittalId' post: tags: [transmittals] summary: Привязать review к трансмитталу operationId: linkReview requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalLinkReviewRequest' responses: '204': { description: Review привязан } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } # ========================================================================== # Transmittal templates # ========================================================================== /api/v1/transmittal_templates: post: tags: [transmittal_templates] summary: Список шаблонов трансмитталов description: Пагинированный список шаблонов с фильтрами. Тело запроса опционально (при отсутствии применяются значения по умолчанию). operationId: listTransmittalTemplates requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/TransmittalTemplateListFiltersRequest' responses: '200': description: Страница шаблонов content: application/json: schema: $ref: '#/components/schemas/Page_TransmittalTemplateListResponse' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittal_templates/create: post: tags: [transmittal_templates] summary: Создать шаблон operationId: createTransmittalTemplate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalTemplateCreateRequestDto' responses: '200': description: Идентификатор созданного шаблона content: application/json: schema: $ref: '#/components/schemas/TransmittalTemplateCreateResponseDto' '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '409': { $ref: '#/components/responses/Conflict' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittal_templates/select: get: tags: [transmittal_templates] summary: Шаблоны для выбора description: Плоский список шаблонов (id/имя/автор), доступных для выбора в рамках ресурса. operationId: listTransmittalTemplatesForSelect parameters: - name: resource in: query required: true schema: type: string format: uuid - name: template_id in: query required: false description: Один или несколько id шаблонов для фильтрации schema: type: array items: type: string format: uuid nullable: true responses: '200': description: Список шаблонов для выбора content: application/json: schema: $ref: '#/components/schemas/Result_TransmittalTemplateSelectListResponse' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '422': { $ref: '#/components/responses/ValidationError' } /api/v1/transmittal_templates/{transmittal_template_id}: parameters: - $ref: '#/components/parameters/TransmittalTemplateId' get: tags: [transmittal_templates] summary: Шаблон по id operationId: getTransmittalTemplate parameters: - name: resource in: query required: true schema: type: string format: uuid responses: '200': description: Шаблон трансмиттала content: application/json: schema: $ref: '#/components/schemas/TransmittalTemplateGetSingleResponse' '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } patch: tags: [transmittal_templates] summary: Обновить шаблон operationId: updateTransmittalTemplate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalTemplateUpdateRequest' responses: '204': { description: Обновлено } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '409': { $ref: '#/components/responses/Conflict' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } delete: tags: [transmittal_templates] summary: Удалить шаблон operationId: deleteTransmittalTemplate responses: '204': { description: Удалено } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/Gone' } '422': { $ref: '#/components/responses/ValidationError' } # ========================================================================== # Internal (cluster-only) # ========================================================================== /internal/v1/transmittals: post: tags: [internal] summary: Список трансмитталов (внутренний) description: Внутренний список трансмитталов по фильтрам. Без аутентификации на уровне приложения; доступен только внутри кластера. operationId: internalGetTransmittals security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalListInternalRequest' responses: '200': description: Список трансмитталов content: application/json: schema: $ref: '#/components/schemas/Result_TransmittalReadListResponseDto' '422': { $ref: '#/components/responses/ValidationError' } /internal/v1/transmittals/by_bundle_ids: post: tags: [internal] summary: Трансмитталы по bundle id (внутренний) description: Возвращает трансмитталы, связанные с переданными `bundle_ids`. Без аутентификации на уровне приложения; доступен только внутри кластера. operationId: internalGetTransmittalsByBundleIds security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TransmittalGetByBundleIdsRequest' responses: '200': description: Трансмитталы по bundle id content: application/json: schema: $ref: '#/components/schemas/Result_TransmittalGetByBundleIdsResponse' '422': { $ref: '#/components/responses/ValidationError' } components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT в заголовке `Authorization: Bearer `. Проверяется публичным ключом (`RS512`) в режиме sarex-backend либо принимается как есть в режиме Zitadel (см. заголовок `identity`). identityToken: type: apiKey in: header name: identity description: | Опциональный заголовок `identity` (`Identity `) для режима Zitadel. При его наличии полезная нагрузка берётся из этого токена, а подпись основного токена сервисом не проверяется. parameters: TransmittalId: name: transmittal_id in: path required: true schema: type: string format: uuid TransmittalTemplateId: name: transmittal_template_id in: path required: true schema: type: string format: uuid responses: BadRequest: description: Некорректный запрос / бизнес-правило нарушено content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' Unauthorized: description: Токен не предоставлен или невалиден content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' Forbidden: description: Недостаточно прав content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' Conflict: description: Конфликт (например, имя шаблона уже занято) content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' Gone: description: Запрошенный ресурс не существует (маппинг `NotFound` → `410`) content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' ValidationError: description: Ошибка валидации тела/параметров запроса (FastAPI/Pydantic) content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' schemas: # --- Общие ------------------------------------------------------------ ApiError: type: object description: Ошибка в формате RFC 7807 (application/problem+json) properties: type: { type: string } title: { type: string } status: { type: integer } detail: { type: string } instance: type: string description: URL запроса, вызвавшего ошибку required: [type, title, status, detail, instance] HTTPValidationError: type: object properties: detail: type: array items: $ref: '#/components/schemas/ValidationErrorItem' ValidationErrorItem: type: object properties: loc: type: array items: anyOf: - type: string - type: integer msg: { type: string } type: { type: string } required: [loc, msg, type] ReceiversRequestDto: type: object description: Получатели трансмиттала (запрос). Хотя бы один из наборов должен быть непуст. properties: users: type: array items: { type: integer, minimum: 0, maximum: 9223372036854775807 } uniqueItems: true departments: type: array items: { type: integer } uniqueItems: true roles: type: array items: { type: integer } uniqueItems: true ReceiversReadResponseDto: type: object properties: users: { type: array, items: { type: integer } } departments: { type: array, items: { type: integer } } roles: { type: array, items: { type: integer } } required: [users, departments, roles] ReceiversFilterDto: type: object properties: users: { type: array, items: { type: integer } } departments: { type: array, items: { type: integer } } roles: { type: array, items: { type: integer } } DocumentVersionRequestDto: type: object properties: document_id: { type: integer, minimum: 0, maximum: 2147483647 } bundle_id: { type: string, format: uuid } required: [document_id, bundle_id] DocumentVersionReadResponse: type: object properties: document_id: { type: integer } bundle_id: { type: string, format: uuid } required: [document_id, bundle_id] StatusReadResponseDto: type: object properties: id: { type: integer } name: { type: string } slug: { type: string } required: [id, name, slug] StatusSimpleDto: type: object properties: slug: { type: string } name: { type: string } required: [slug, name] StatusesListResponseDto: type: object properties: statuses: type: array items: { $ref: '#/components/schemas/StatusSimpleDto' } required: [statuses] StepReadResponseDto: type: object properties: id: { type: integer } name: { type: string } slug: { type: string } required: [id, name, slug] ActionReadResponse: type: object properties: id: { type: integer } step_id: { type: integer } name: { type: string } slug: { type: string } required: [id, step_id, name, slug] ReviewLightResponse: type: object properties: id: { type: integer } name: { type: string } required: [id, name] UserActionCreateRequest: type: object properties: comment: { type: string, nullable: true } UserActionCreateResponseDto: type: object properties: id: { type: string, format: uuid } transmittal_id: { type: string, format: uuid } action_id: { type: integer } comment: { type: string, nullable: true } required: [id, transmittal_id, action_id, comment] UserActionReadResponse: type: object properties: id: { type: string, format: uuid } user_id: { type: integer } transmittal_id: { type: string, format: uuid } action_id: { type: integer } comment: { type: string, nullable: true } action: { $ref: '#/components/schemas/ActionReadResponse' } created_at: { type: string, format: date-time } required: [id, user_id, transmittal_id, action_id, comment, action, created_at] # --- Wrappers --------------------------------------------------------- Page_StepReadResponseDto: type: object properties: result: type: array items: { $ref: '#/components/schemas/StepReadResponseDto' } next: { type: string } prev: { type: string } required: [result, next, prev] Page_TransmittalReadListResponseDto: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalReadListResponseDto' } next: { type: string } prev: { type: string } required: [result, next, prev] Page_TransmittalTemplateListResponse: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalTemplateListResponse' } next: { type: string } prev: { type: string } required: [result, next, prev] Result_TransmittalTemplateSelectListResponse: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalTemplateSelectListResponse' } required: [result] Result_TransmittalReadListResponseDto: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalReadListResponseDto' } required: [result] Result_TransmittalGetByBundleIdsResponse: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalGetByBundleIdsResponse' } required: [result] # --- Transmittals ----------------------------------------------------- TransmittalCreateRequestDto: type: object properties: name: { type: string, minLength: 1, maxLength: 255 } company_id: { type: integer, minimum: 0, maximum: 18446744073709551615 } additional_information: { type: string, nullable: true } resource_id: { type: string, format: uuid } auto_send: { type: boolean } documents_to_approve: type: array description: Не менее одного документа minItems: 1 uniqueItems: true items: { $ref: '#/components/schemas/DocumentVersionRequestDto' } receivers: { $ref: '#/components/schemas/ReceiversRequestDto' } deadline_at: type: string format: date-time nullable: true description: Дедлайн в будущем required: [name, company_id, additional_information, resource_id, auto_send, documents_to_approve, receivers, deadline_at] TransmittalCreateResponseDto: type: object properties: id: { type: string, format: uuid } required: [id] TransmittalListRequestDto: type: object properties: limit: { type: integer, default: 1000 } bookmark: { type: string, nullable: true } resource_id: { type: string, format: uuid } required: [resource_id] TransmittalReadListResponseDto: type: object properties: id: { type: string, format: uuid } resource_id: { type: string, format: uuid } created_by: { type: integer } current_step_id: { type: integer } "no": { type: integer } name: { type: string } additional_information: { type: string, nullable: true } steps: { type: array, items: { type: integer } } auto_send: { type: boolean } receivers: { $ref: '#/components/schemas/ReceiversReadResponseDto' } deadline_at: { type: string, format: date-time, nullable: true } status: { $ref: '#/components/schemas/StatusReadResponseDto' } step: { $ref: '#/components/schemas/StepReadResponseDto' } sent_at: { type: string, format: date-time, nullable: true } received_at: { type: string, format: date-time, nullable: true } act_available: { type: boolean } reviews: type: array nullable: true items: { $ref: '#/components/schemas/ReviewLightResponse' } required: [id, resource_id, created_by, current_step_id, "no", name, additional_information, steps, auto_send, receivers, deadline_at, status, step, sent_at, received_at, act_available] TransmittalReadResponseDto: type: object properties: id: { type: string, format: uuid } resource_id: { type: string, format: uuid } created_by: { type: integer } current_step_id: { type: integer } "no": { type: integer } name: { type: string } additional_information: { type: string, nullable: true } steps: { type: array, items: { type: integer } } auto_send: { type: boolean } receivers: { $ref: '#/components/schemas/ReceiversReadResponseDto' } deadline_at: { type: string, format: date-time, nullable: true } sent_at: { type: string, format: date-time, nullable: true } received_at: { type: string, format: date-time, nullable: true } act_available: { type: boolean } status: { $ref: '#/components/schemas/StatusReadResponseDto' } step: { $ref: '#/components/schemas/StepReadResponseDto' } documents_to_approve: type: array items: { $ref: '#/components/schemas/DocumentVersionReadResponse' } history: type: array items: { $ref: '#/components/schemas/UserActionReadResponse' } required: [id, resource_id, created_by, current_step_id, "no", name, additional_information, steps, auto_send, receivers, deadline_at, sent_at, received_at, act_available, status, step, documents_to_approve, history] TransmittalStatusesStatResponseDto: type: object properties: status: { $ref: '#/components/schemas/StatusReadResponseDto' } count: { type: integer } required: [status, count] TransmittalListResponseDto: type: object properties: page: { $ref: '#/components/schemas/Page_TransmittalReadListResponseDto' } statuses_stat: type: array items: { $ref: '#/components/schemas/TransmittalStatusesStatResponseDto' } required: [page, statuses_stat] TransmittalGlobalStatusesStatResponseDto: type: object properties: resource_id: { type: string, format: uuid } statuses: type: array items: { $ref: '#/components/schemas/TransmittalStatusesStatResponseDto' } required: [resource_id, statuses] TransmittalGetGlobalStatusesRequestDto: type: object properties: resource_ids: type: array items: { type: string, format: uuid } required: [resource_ids] TransmittalGetGlobalStatusesResponseDto: type: object properties: result: type: array items: { $ref: '#/components/schemas/TransmittalGlobalStatusesStatResponseDto' } required: [result] TransmittalDownloadActResponse: type: object properties: presigned_url: { type: string } required: [presigned_url] TransmittalGetActReadinessResponse: type: object properties: act_available: { type: boolean } required: [act_available] TransmittalOpenCountResponseDto: type: object properties: count: { type: integer } required: [count] TransmittalLinkReviewRequest: type: object properties: review_id: { type: integer } required: [review_id] TransmittalSearchFilterRequestDto: type: object description: Фильтр поиска в рамках одного ресурса properties: q: { type: string, nullable: true } resource_id: { type: string, format: uuid } status_slug: { type: array, items: { type: string } } created_by: { type: array, items: { type: integer } } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversFilterDto' }] created_from: { type: string, format: date-time, nullable: true } created_to: { type: string, format: date-time, nullable: true } received_from: { type: string, format: date-time, nullable: true } received_to: { type: string, format: date-time, nullable: true } deadline_at: { type: string, format: date-time, nullable: true } unlimited: { type: boolean, nullable: true, default: false } auto_send: { type: array, items: { type: boolean } } limit: { type: integer, nullable: true, default: 100 } bookmark: { type: string, nullable: true } required: [resource_id] TransmittalSearchFilterDto: type: object description: Фильтр поиска по нескольким ресурсам properties: q: { type: string, nullable: true } resource_ids: { type: array, items: { type: string, format: uuid } } status_slug: { type: array, items: { type: string } } created_by: { type: array, items: { type: integer } } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversFilterDto' }] created_from: { type: string, format: date-time, nullable: true } created_to: { type: string, format: date-time, nullable: true } received_from: { type: string, format: date-time, nullable: true } received_to: { type: string, format: date-time, nullable: true } deadline_at: { type: string, format: date-time, nullable: true } unlimited: { type: boolean, nullable: true, default: false } auto_send: { type: array, items: { type: boolean } } limit: { type: integer, nullable: true, default: 100 } bookmark: { type: string, nullable: true } # --- Internal --------------------------------------------------------- TransmittalListInternalRequest: type: object properties: limit: { type: integer, default: 100 } transmittal_ids: { type: array, items: { type: string, format: uuid } } company_id: { type: integer, nullable: true } resource_id: { type: string, format: uuid, nullable: true } TransmittalGetByBundleIdsRequest: type: object properties: bundle_ids: { type: array, items: { type: string, format: uuid } } required: [bundle_ids] TransmittalGetByBundleIdsReadDto: type: object properties: id: { type: string, format: uuid } name: { type: string } status: { $ref: '#/components/schemas/StatusReadResponseDto' } created_at: { type: string, format: date-time } required: [id, name, status, created_at] TransmittalGetByBundleIdsResponse: type: object properties: bundle_id: { type: string, format: uuid } transmittals: type: array items: { $ref: '#/components/schemas/TransmittalGetByBundleIdsReadDto' } required: [bundle_id, transmittals] # --- Templates -------------------------------------------------------- TransmittalTemplateListFiltersRequest: type: object properties: include_wo_resources: { type: boolean, default: true } resources: { type: array, nullable: true, items: { type: string, format: uuid } } companies: { type: array, nullable: true, items: { type: integer } } q: { type: string, nullable: true } bookmark: { type: string, nullable: true } limit: { type: integer, default: 20 } TransmittalTemplateListResponse: type: object properties: id: { type: string, format: uuid } name: { type: string } is_active: { type: boolean } resource_id: { type: string, format: uuid, nullable: true } allowed_initiators: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversReadResponseDto' }] created_by: { type: integer } created_at: { type: string, format: date-time } has_deadline_interval: { type: boolean } deadline_interval: { type: integer, minimum: 1, nullable: true, description: Интервал дедлайна в днях } auto_send: { type: boolean, nullable: true } has_additional_information: { type: boolean } additional_information: { type: string, nullable: true } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversReadResponseDto' }] invalid_receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversReadResponseDto' }] required: [id, name, is_active, resource_id, allowed_initiators, created_by, created_at, has_deadline_interval, deadline_interval, auto_send, has_additional_information, additional_information, receivers, invalid_receivers] TransmittalTemplateCreateRequestDto: type: object properties: name: { type: string, minLength: 1, maxLength: 255 } resource_id: { type: string, format: uuid, nullable: true } allowed_initiators: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversRequestDto' }] company_id: { type: integer } has_deadline_interval: { type: boolean } deadline_interval: { type: integer, minimum: 1, nullable: true, description: Интервал дедлайна в днях } auto_send: { type: boolean, nullable: true } has_additional_information: { type: boolean } additional_information: { type: string, nullable: true } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversRequestDto' }] required: [name, resource_id, allowed_initiators, company_id, has_deadline_interval, deadline_interval, auto_send, has_additional_information, additional_information, receivers] TransmittalTemplateCreateResponseDto: type: object properties: id: { type: string, format: uuid } required: [id] TransmittalTemplateSelectListResponse: type: object properties: id: { type: string, format: uuid } name: { type: string } created_by: { type: integer } required: [id, name, created_by] TransmittalTemplateGetSingleResponse: type: object properties: name: { type: string } created_by: { type: integer } has_deadline_interval: { type: boolean } deadline_interval: { type: integer, minimum: 1, nullable: true, description: Интервал дедлайна в днях } auto_send: { type: boolean, nullable: true } has_additional_information: { type: boolean } additional_information: { type: string, nullable: true } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversReadResponseDto' }] invalid_receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversReadResponseDto' }] required: [name, created_by, has_deadline_interval, deadline_interval, auto_send, has_additional_information, additional_information, receivers, invalid_receivers] TransmittalTemplateUpdateRequest: type: object description: Частичное обновление. Все поля опциональны. properties: name: { type: string, minLength: 1, maxLength: 255, nullable: true } is_active: { type: boolean, nullable: true } resource_id: { type: string, format: uuid, nullable: true } allowed_initiators: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversRequestDto' }] has_deadline_interval: { type: boolean, nullable: true } deadline_interval: { type: integer, minimum: 1, nullable: true, description: Интервал дедлайна в днях } auto_send: { type: boolean, nullable: true } has_additional_information: { type: boolean, nullable: true } additional_information: { type: string, nullable: true } receivers: nullable: true allOf: [{ $ref: '#/components/schemas/ReceiversRequestDto' }] # --- Infra ------------------------------------------------------------ ResourceStatus: type: object properties: name: { type: string } available: { type: boolean } required: [name, available] HealthCheckResponse: type: object properties: availability: type: array items: { $ref: '#/components/schemas/ResourceStatus' } status: type: string enum: [healthy, partially_healthy] description: Вычисляемое поле — `healthy`, если доступны все зависимости readOnly: true required: [availability, status]