openapi: 3.0.0
info:
  title: MyNCA API
  version: 1.0.0
  description: |
    API для работы с электронными цифровыми подписями (ЭЦП) Республики Казахстан.
    
    Сервис предоставляет возможности:
    - Создание и проверка CMS подписей
    - Подписание и проверка XML документов
    - Работа с сертификатами X.509 и PKCS#12
    - Процесс подписания через NCALayer
    - Конвертация документов
    - Мониторинг состояния сервиса
    
    ## Rate Limiting
    
    API защищен rate limiting для предотвращения злоупотреблений:
    
    - **Подписание документов** (`/sign/*`, `/cms/sign`, `/xml/sign`): 10 запросов/минуту
    - **Проверка подписей** (`/cms/verify`, `/xml/verify`): 30 запросов/минуту
    - **Остальные endpoints**: 60 запросов/минуту
    - **Health checks**: 120 запросов/минуту
    
    При превышении лимита вернется HTTP 429 (Too Many Requests) с заголовками:
    - `X-RateLimit-Limit` - максимум запросов
    - `X-RateLimit-Remaining` - осталось запросов
    - `Retry-After` - через сколько секунд можно повторить
    
    ## Аутентификация
    
    API может быть защищён системой API ключей (опционально):
    
    - Если `API_AUTH_ENABLED=false` (по умолчанию) — API работает **без защиты**
    - Если `API_AUTH_ENABLED=true` — требуется API ключ клиента в каждом запросе
    
    ### Управление API ключами
    
    API ключи создаются и управляются через **админ-панель** (`/admin/api-clients`).
    Каждый клиент получает уникальный 64-символьный ключ.
    
    ### Способы передачи ключа
    
    - Заголовок `Authorization: Bearer {api_key}`
    - Заголовок `X-API-Token: {api_key}`
    
    ### Endpoints без аутентификации (всегда доступны)
    
    - `/api/health*` — health checks (лимит: 120 запросов/мин)
    - `/swagger` — документация (лимит: 60 запросов/мин)

servers:
  - url: /api
    description: Текущий сервер (автоматически определяется)

security:
  - bearerAuth: []
  - apiKeyAuth: []
  - {} # Опционально - если API_TOKEN не установлен

tags:
  - name: Health
    description: Проверка состояния сервиса (Kubernetes probes)
  - name: Подпись (одиночная)
    description: |
      Подписание одного документа через NCALayer.
      Поддерживает множественные подписи через group_id.
  - name: Подпись (массовая)
    description: |
      Массовое подписание нескольких документов (batch).
      Пользователь подписывает все документы за один раз.
  - name: CMS
    description: Создание, проверка и работа с CMS подписями
  - name: XML
    description: Подписание и проверка XML документов
  - name: Certificate
    description: Информация о сертификатах и ключах
  - name: Document
    description: Конвертация и обработка документов

paths:
  # ==================== Health Checks ====================
  /health:
    get:
      tags:
        - Health
      summary: Общая проверка здоровья сервиса
      description: Проверяет доступность всех компонентов системы (БД, Kalkan, Python, Ghostscript)
      responses:
        '200':
          description: Сервис работает нормально
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [ready, not_ready]
                  timestamp:
                    type: string
                    format: date-time
                  checks:
                    type: object
                    properties:
                      kalkan:
                        type: object
                      storage:
                        type: object
                      temp:
                        type: object
                      python:
                        type: object
                      ghostscript:
                        type: object
        '503':
          description: Сервис недоступен

  /health/startup:
    get:
      tags:
        - Health
      summary: Startup probe
      description: Проверяет, что приложение запустилось (для Kubernetes)
      responses:
        '200':
          description: Приложение запущено
        '503':
          description: Приложение не запустилось

  /health/readiness:
    get:
      tags:
        - Health
      summary: Readiness probe
      description: Проверяет, что приложение готово обрабатывать запросы (для Kubernetes)
      responses:
        '200':
          description: Приложение готово
        '503':
          description: Приложение не готово

  /health/liveness:
    get:
      tags:
        - Health
      summary: Liveness probe
      description: Проверяет, что приложение живо и не зависло (для Kubernetes)
      responses:
        '200':
          description: Приложение живо
        '503':
          description: Приложение не отвечает

  /health/crl:
    get:
      tags:
        - Health
      summary: Статус CRL и OCSP
      description: |
        Показывает статус локальных CRL файлов и доступность OCSP сервера.
        
        CRL файлы обновляются автоматически каждые 6 часов.
        
        ### Статусы файлов:
        - `ok` — файл свежий (< 12 часов)
        - `stale` — файл устаревший (> 12 часов)
        - `missing` — файл отсутствует
      responses:
        '200':
          description: Статус CRL и OCSP
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [healthy, degraded]
                    description: Общий статус (healthy если все файлы свежие и OCSP доступен)
                  timestamp:
                    type: string
                    format: date-time
                  crl:
                    type: object
                    properties:
                      directory:
                        type: string
                        description: Путь к директории с CRL файлами
                      update_interval:
                        type: string
                        description: Интервал автоматического обновления
                      files:
                        type: object
                        additionalProperties:
                          type: object
                          properties:
                            exists:
                              type: boolean
                            size:
                              type: string
                              description: Размер файла (человекочитаемый)
                            size_bytes:
                              type: integer
                            modified:
                              type: string
                              description: Дата последнего изменения
                            age_hours:
                              type: number
                              description: Возраст файла в часах
                            status:
                              type: string
                              enum: [ok, stale, missing]
                            url:
                              type: string
                              format: uri
                              description: URL источника CRL
                  ocsp:
                    type: object
                    properties:
                      url:
                        type: string
                        format: uri
                      available:
                        type: boolean
                      status:
                        type: string
                        enum: [ok, unavailable, error]
              example:
                status: healthy
                timestamp: "2026-01-07T20:50:30+05:00"
                crl:
                  directory: "/opt/crl-files"
                  update_interval: "every 6 hours"
                  files:
                    nca_gost_2022.crl:
                      exists: true
                      size: "21.59 MB"
                      size_bytes: 22635547
                      modified: "2026-01-07 20:42:00"
                      age_hours: 0.1
                      status: "ok"
                      url: "http://crl.pki.gov.kz/nca_gost_2022.crl"
                ocsp:
                  url: "http://ocsp.pki.gov.kz"
                  available: true
                  status: "ok"

  # ==================== Подпись (одиночная) ====================
  /sign/create:
    post:
      tags:
        - Подпись (одиночная)
      summary: Создать процесс подписания
      description: |
        Создает новый процесс подписания документа через NCALayer.
        Возвращает URL для подписания и уникальный ID процесса.
        
        ---
        
        ## Параметры file_content и group_id
        
        | file_content | group_id | Поведение |
        |--------------|----------|-----------|
        | ✅ передан | ❌ не передан | Первый подписант. Создаётся новый `group_id`. |
        | ✅ передан | ✅ передан | Второй+ подписант. Проверяется что файл совпадает с файлом в группе. |
        | ❌ не передан | ✅ передан | Второй+ подписант. Файл берётся из группы автоматически (экономия трафика). |
        | ❌ не передан | ❌ не передан | Ошибка 422. |
        
        ---
        
        ## Валидация group_id
        
        - `group_id` должен существовать в БД (нельзя передать произвольный UUID)
        - Если передан `file_content` + `group_id` — проверяется что файлы совпадают (по MD5)
        - Если файлы не совпадают — ошибка 422: "Переданный файл не совпадает с файлом в группе"
        
        ---
        
        ## Групповое подписание (несколько подписантов)
        
        **Шаг 1 — Первый подписант:**
        ```json
        {
          "file_name": "contract.pdf",
          "file_content": "JVBERi0xLjQ..."
        }
        ```
        Ответ содержит `group_id` — сохраните его.
        
        **Шаг 2 — Второй подписант (без файла):**
        ```json
        {
          "group_id": "uuid-from-step-1"
        }
        ```
        Файл берётся автоматически из группы — экономия трафика!
        
        Все подписи в группе объединяются в один CMS при скачивании.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                file_name:
                  type: string
                  description: |
                    Имя файла.
                    - Обязательно для первого подписанта
                    - Необязательно если берём из группы (будет использовано имя из группы)
                  example: "document.pdf"
                file_content:
                  type: string
                  format: byte
                  description: |
                    Содержимое файла в base64.
                    - Обязательно для первого подписанта
                    - Необязательно для второго+ если передан `group_id` — файл берётся из группы
                    - Если передан вместе с `group_id` — проверяется совпадение файлов
                group_id:
                  type: string
                  format: uuid
                  description: |
                    UUID группы для множественного подписания.
                    
                    **Важно:**
                    - Должен существовать в БД (нельзя передать произвольный UUID)
                    - Если передан без `file_content` — файл берётся из предыдущей подписи в группе
                    - Если передан с `file_content` — проверяется что файлы совпадают
                ext_id:
                  type: integer
                  description: Внешний ID для связи с вашей системой
                back_url:
                  type: string
                  format: uri
                  description: URL для callback после подписания
                return_url:
                  type: string
                  format: uri
                  description: URL для возврата пользователя после подписания
                exp_minutes:
                  type: integer
                  minimum: 1
                  maximum: 1440
                  default: 30
                  description: Срок действия ссылки в минутах (от 1 до 1440)
      responses:
        '201':
          description: Процесс подписания создан
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      sign_process_id:
                        type: string
                        format: uuid
                      group_id:
                        type: string
                        format: uuid
                      sign_url:
                        type: string
                        format: uri
                      expires_at:
                        type: string
                        format: date-time
                      signatures_count:
                        type: integer
        '422':
          description: Ошибка валидации
        '500':
          description: Внутренняя ошибка сервера

  /sign/{signProcessId}/status:
    get:
      tags:
        - Подпись (одиночная)
      summary: Получить статус процесса подписания
      description: Возвращает текущий статус процесса подписания и информацию о подписантах
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Статус получен
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      sign_process_id:
                        type: string
                      group_id:
                        type: string
                      file_name:
                        type: string
                      is_signed:
                        type: boolean
                      signed_at:
                        type: string
                        format: date-time
                        nullable: true
                      expires_at:
                        type: string
                        format: date-time
                      is_expired:
                        type: boolean
                      dn_name:
                        type: string
                        nullable: true
                      error:
                        type: string
                        nullable: true
                      group_signatures_count:
                        type: integer
                      group_signers:
                        type: array
                        items:
                          type: object
        '404':
          description: Процесс не найден

  /sign/{signProcessId}/file:
    get:
      tags:
        - Подпись (одиночная)
      summary: Получить файл для подписания
      description: Возвращает оригинальный файл в base64 для подписания через NCALayer
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Файл получен
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      file_name:
                        type: string
                      file_content:
                        type: string
                        format: byte
        '404':
          description: Файл не найден

  /sign/{signProcessId}/preview:
    get:
      tags:
        - Подпись (одиночная)
      summary: Предпросмотр документа с водяными знаками
      description: Возвращает PDF с водяными знаками "ДЛЯ ОЗНАКОМЛЕНИЯ"
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: PDF с водяными знаками
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '404':
          description: Документ не найден

  /sign/{signProcessId}/process:
    post:
      tags:
        - Подпись (одиночная)
      summary: Обработать подписание
      description: |
        Принимает подписанные данные от NCALayer и валидирует подпись.
        При успешной валидации отправляет callback на back_url (если указан).
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - signed_data
                - certificate_info
              properties:
                signed_data:
                  type: string
                  description: CMS подпись в base64
                certificate_info:
                  type: object
                  required:
                    - subject
                  properties:
                    subject:
                      type: string
                      description: DN имя из сертификата
      responses:
        '200':
          description: Документ успешно подписан
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      sign_process_id:
                        type: string
                      group_id:
                        type: string
                      signed_at:
                        type: string
                        format: date-time
                      return_url:
                        type: string
                      signatures_count:
                        type: integer
        '400':
          description: Ошибка валидации подписи или callback
        '422':
          description: Документ уже подписан или срок истек

  /sign/{signProcessId}/download:
    get:
      tags:
        - Подпись (одиночная)
      summary: Скачать подписанный документ
      description: |
        Возвращает PDF с информацией о всех подписантах группы.
        PDF включает оригинальный документ с колонтитулами и отдельную страницу с деталями подписей.
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Подписанный PDF документ
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '404':
          description: Процесс не найден
        '422':
          description: Документ еще не подписан

  /sign/{signProcessId}/download-cms:
    get:
      tags:
        - Подпись (одиночная)
      summary: Скачать CMS подпись
      description: Возвращает объединенную CMS подпись всех подписантов группы (для отладки)
      parameters:
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: CMS подпись
          content:
            application/pkcs7-signature:
              schema:
                type: string
                format: binary
        '404':
          description: Процесс не найден
        '422':
          description: Документ еще не подписан

  /sign/group/{groupId}/download:
    get:
      tags:
        - Подпись (одиночная)
      summary: Скачать документ по group_id
      description: Возвращает PDF со всеми подписями группы
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Подписанный PDF документ
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '404':
          description: Группа не найдена
        '422':
          description: Документ еще не подписан

  # ==================== Подпись (массовая) ====================
  /sign/batch/create:
    post:
      tags:
        - Подпись (массовая)
      summary: Создать batch для массовой подписи
      description: |
        Создает batch для массового подписания нескольких документов.
        
        ---
        
        ## Параметры file_content и group_id (для каждого документа)
        
        | file_content | group_id | Поведение |
        |--------------|----------|-----------|
        | ✅ передан | ❌ не передан | Первый подписант. Создаётся новый `group_id`. |
        | ✅ передан | ✅ передан | Второй+ подписант. Проверяется что файл совпадает с файлом в группе. |
        | ❌ не передан | ✅ передан | Второй+ подписант. Файл берётся из группы автоматически. |
        | ❌ не передан | ❌ не передан | Ошибка 422. |
        
        ---
        
        ## Валидация group_id
        
        - `group_id` должен существовать в БД (нельзя передать произвольный UUID)
        - Если передан `file_content` + `group_id` — проверяется что файлы совпадают (по MD5)
        - Если файлы не совпадают — ошибка 422
        
        ---
        
        ## Групповое подписание (несколько подписантов)
        
        **Шаг 1 — Первый подписант:**
        ```json
        {
          "documents": [
            {"file_name": "doc1.pdf", "file_content": "...", "ext_id": 1},
            {"file_name": "doc2.pdf", "file_content": "...", "ext_id": 2}
          ],
          "back_url": "https://..."
        }
        ```
        Ответ содержит `group_id` для каждого документа — сохраните их.
        
        **Шаг 2 — Второй подписант (без файлов):**
        ```json
        {
          "documents": [
            {"group_id": "uuid-doc-1", "ext_id": 1},
            {"group_id": "uuid-doc-2", "ext_id": 2}
          ],
          "back_url": "https://..."
        }
        ```
        Файлы берутся автоматически — **экономия трафика!**
        
        ---
        
        ## Параметр atomic (режим транзакции)
        
        | atomic | Поведение при ошибке от back_url |
        |--------|----------------------------------|
        | `false` (по умолчанию) | Откатываются только отклонённые документы |
        | `true` | Если хоть один отклонён — откатываются ВСЕ документы |
        
        Используйте `atomic: true` если все документы должны быть подписаны вместе или не подписаны вообще.
        
        ---
        
        ## Callback на back_url
        
        После подписания всех документов отправляется POST на `back_url`.
        
        **Формат ответа от back_url:**
        ```json
        {
          "status": "success",
          "documents": [
            {"sign_process_id": "uuid1", "status": "ok"},
            {"sign_process_id": "uuid2", "status": "error", "error": "Причина"}
          ]
        }
        ```
        
        **Логика отката:**
        
        | Ситуация | Действие |
        |----------|----------|
        | back_url недоступен | Откат ВСЕХ документов |
        | HTTP 4xx/5xx | Откат ВСЕХ документов |
        | `status: error` без `documents` | Откат ВСЕХ документов |
        | `documents` с детализацией | Откат только ошибочных (или всех если `atomic: true`) |
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - documents
              properties:
                documents:
                  type: array
                  minItems: 1
                  maxItems: 20
                  description: |
                    Массив документов для подписания.
                    
                    **Лимиты (настраиваются через env):**
                    - Максимум документов: 20 (BATCH_MAX_DOCUMENTS)
                    - Максимум размер файла: 10 MB (BATCH_MAX_FILE_SIZE_MB)
                    - Максимум общий размер: 50 MB (BATCH_MAX_TOTAL_SIZE_MB)
                  items:
                    type: object
                    properties:
                      file_name:
                        type: string
                        description: |
                          Имя файла.
                          - Обязательно для первого подписанта
                          - Необязательно если берём из группы
                        example: "document1.pdf"
                      file_content:
                        type: string
                        format: byte
                        description: |
                          Содержимое файла в base64.
                          - Обязательно для первого подписанта
                          - Необязательно если передан `group_id`
                          - Если передан с `group_id` — проверяется совпадение файлов
                      group_id:
                        type: string
                        format: uuid
                        description: |
                          UUID группы подписантов.
                          
                          **Важно:**
                          - Должен существовать в БД
                          - Без `file_content` — файл берётся автоматически
                          - С `file_content` — проверяется совпадение файлов
                        example: "550e8400-e29b-41d4-a716-446655440000"
                      ext_id:
                        type: integer
                        description: Внешний ID для связи с вашей системой
                back_url:
                  type: string
                  format: uri
                  description: |
                    URL для callback после подписания всех документов.
                    Должен вернуть JSON с детализацией по каждому документу.
                return_url:
                  type: string
                  format: uri
                  description: URL для возврата пользователя после подписания
                exp_minutes:
                  type: integer
                  minimum: 1
                  maximum: 1440
                  default: 30
                  description: Срок действия ссылки в минутах
                atomic:
                  type: boolean
                  default: false
                  description: |
                    Режим "всё или ничего". **По умолчанию `false` — можно не передавать.**
                    
                    - `false` (по умолчанию) — при ошибке откатываются только отклонённые документы
                    - `true` — если хотя бы один документ отклонён, откатываются ВСЕ
                    
                    Используйте `true` для транзакционного подписания пакета документов.
            examples:
              first_signer:
                summary: Первый подписант (с файлами)
                value:
                  documents:
                    - file_name: "contract.pdf"
                      file_content: "JVBERi0xLjQKJeLj..."
                      ext_id: 1
                    - file_name: "appendix.pdf"
                      file_content: "JVBERi0xLjQKJcOk..."
                      ext_id: 2
                  back_url: "https://your-system.kz/callback"
                  return_url: "https://your-system.kz/documents"
                  exp_minutes: 60
                  atomic: true
              second_signer:
                summary: Второй подписант (без файлов, только group_id)
                value:
                  documents:
                    - group_id: "550e8400-e29b-41d4-a716-446655440000"
                      ext_id: 1
                    - group_id: "660e8400-e29b-41d4-a716-446655440001"
                      ext_id: 2
                  back_url: "https://your-system.kz/callback"
                  return_url: "https://your-system.kz/documents"
                  exp_minutes: 60
                  atomic: true
      responses:
        '201':
          description: Batch создан
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      batch_id:
                        type: string
                        format: uuid
                        description: Уникальный ID batch
                      sign_url:
                        type: string
                        format: uri
                        description: URL страницы массовой подписи
                      documents_count:
                        type: integer
                        description: Количество документов в batch
                      documents:
                        type: array
                        description: Массив созданных документов с их ID
                        items:
                          type: object
                          properties:
                            sign_process_id:
                              type: string
                              format: uuid
                              description: ID записи в БД
                            group_id:
                              type: string
                              format: uuid
                              description: UUID группы подписания (для скачивания PDF)
                            file_name:
                              type: string
                              description: Имя файла
                            ext_id:
                              type: integer
                              nullable: true
                              description: Внешний ID
                      expires_at:
                        type: string
                        format: date-time
              example:
                success: true
                data:
                  batch_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  sign_url: "https://nca.example.kz/sign/batch/019b3c4d-5e6f-7890-abcd-ef1234567890"
                  documents_count: 2
                  documents:
                    - sign_process_id: "019b3c4d-1111-7890-abcd-ef1234567890"
                      group_id: "019b3c4d-aaaa-7890-abcd-ef1234567890"
                      file_name: "contract.pdf"
                      ext_id: 1
                    - sign_process_id: "019b3c4d-2222-7890-abcd-ef1234567890"
                      group_id: "019b3c4d-bbbb-7890-abcd-ef1234567890"
                      file_name: "appendix.pdf"
                      ext_id: 2
                  expires_at: "2026-01-05T13:30:00+05:00"
        '422':
          description: Ошибка валидации
        '500':
          description: Внутренняя ошибка сервера

  /sign/batch/{batchId}/status:
    get:
      tags:
        - Подпись (массовая)
      summary: Получить статус batch
      description: Возвращает статус batch и информацию о всех документах
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Статус получен
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      batch_id:
                        type: string
                        format: uuid
                      documents_count:
                        type: integer
                      signed_count:
                        type: integer
                      is_fully_signed:
                        type: boolean
                      is_expired:
                        type: boolean
                      expires_at:
                        type: string
                        format: date-time
                      documents:
                        type: array
                        items:
                          type: object
                          properties:
                            sign_process_id:
                              type: string
                              format: uuid
                            group_id:
                              type: string
                              format: uuid
                              description: UUID группы подписания
                            file_name:
                              type: string
                            ext_id:
                              type: integer
                            is_signed:
                              type: boolean
                            dn_name:
                              type: string
                              nullable: true
                            signed_at:
                              type: string
                              format: date-time
                              nullable: true
        '404':
          description: Batch не найден

  /sign/batch/{batchId}/files:
    get:
      tags:
        - Подпись (массовая)
      summary: Получить неподписанные документы batch
      description: Возвращает список документов, которые еще не подписаны (для NCALayer)
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Документы получены
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      batch_id:
                        type: string
                      documents:
                        type: array
                        items:
                          type: object
                          properties:
                            sign_process_id:
                              type: string
                            file_name:
                              type: string
                            file_content:
                              type: string
                              format: byte
        '404':
          description: Batch не найден

  /sign/batch/{batchId}/process/{signProcessId}:
    post:
      tags:
        - Подпись (массовая)
      summary: Обработать подписание документа из batch
      description: |
        Принимает подписанные данные для одного документа из batch.
        Когда все документы batch подписаны, отправляется callback на back_url.
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: signProcessId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - signed_data
              properties:
                signed_data:
                  type: string
                  description: CMS подпись в base64
      responses:
        '200':
          description: Документ подписан
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      sign_process_id:
                        type: string
                      batch_id:
                        type: string
                      signed_count:
                        type: integer
                      total_count:
                        type: integer
                      all_signed:
                        type: boolean
                        description: true если все документы batch подписаны
                      expires_at:
                        type: string
                        format: date-time
                        description: Срок действия ссылки на подписание
        '400':
          description: Ошибка валидации подписи
        '404':
          description: Документ не найден в batch
        '422':
          description: Документ уже подписан или срок истек

  /sign/batch/{batchId}/download-all:
    get:
      tags:
        - Подпись (массовая)
      summary: Скачать все CMS подписи batch как ZIP
      description: Возвращает ZIP архив со всеми CMS подписями из batch
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: ZIP архив с CMS подписями
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '404':
          description: Batch не найден или нет подписанных документов

  /sign/batch/{batchId}/download-pdf-all:
    get:
      tags:
        - Подпись (массовая)
      summary: Скачать все PDF документы batch как ZIP
      description: |
        Возвращает ZIP архив со всеми PDF документами из batch.
        Каждый PDF включает визуальную информацию о подписи (колонтитулы, страница с деталями подписи).
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: ZIP архив с PDF документами
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '404':
          description: Batch не найден или нет подписанных документов
        '500':
          description: Ошибка при генерации PDF документов

  # ==================== CMS ====================
  /cms/sign:
    post:
      tags:
        - CMS
      summary: Создать CMS подпись
      description: |
        Подписывает данные в формате CMS (PKCS#7).
        Поддерживает attached и detached подписи, с TSP и без.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
                - signer
              properties:
                data:
                  type: string
                  format: byte
                  description: Данные для подписания в base64
                signer:
                  type: object
                  required:
                    - key
                    - password
                  properties:
                    key:
                      type: string
                      format: byte
                      description: PKCS#12 ключ в base64
                    password:
                      type: string
                      description: Пароль от ключа
                    keyAlias:
                      type: string
                      description: Алиас ключа (опционально)
                withTsp:
                  type: boolean
                  default: false
                  description: Добавить метку времени (TSP)
                detached:
                  type: boolean
                  default: false
                  description: Создать detached подпись
      responses:
        '200':
          description: CMS подпись создана
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    example: 200
                  message:
                    type: string
                    example: "OK"
                  cms:
                    type: string
                    format: byte
                    description: CMS подпись в base64 (DER)
        '400':
          description: Ошибка создания подписи

  /cms/sign-save:
    post:
      tags:
        - CMS
      summary: Создать CMS подпись и сохранить в БД
      description: |
        Подписывает данные в формате CMS, сохраняет результат в БД и возвращает ответ в формате callback'а.
        Аналог `/cms/sign`, но с сохранением и проверкой подписи.
        
        Возвращает информацию о подписи в том же формате, что и callback при подписании через NCALayer.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
                - signer
              properties:
                data:
                  type: string
                  format: byte
                  description: Данные для подписания в base64
                signer:
                  type: object
                  required:
                    - key
                    - password
                  properties:
                    key:
                      type: string
                      format: byte
                      description: PKCS#12 ключ в base64
                    password:
                      type: string
                      description: Пароль от ключа
                    keyAlias:
                      type: string
                      description: Алиас ключа (опционально)
                withTsp:
                  type: boolean
                  default: false
                  description: Добавить метку времени (TSP)
                detached:
                  type: boolean
                  default: false
                  description: Создать detached подпись
                file_name:
                  type: string
                  description: Имя файла (опционально)
                  example: "document.pdf"
                ext_id:
                  type: integer
                  description: Внешний ID для связи с вашей системой
                  example: 12345
                group_id:
                  type: string
                  format: uuid
                  description: UUID группы для множественного подписания (опционально). Если не передан, создается новый. Передайте существующий group_id для добавления подписи в группу.
                  example: "019ac07b-2b05-7341-9c97-3ce8598952f6"
      responses:
        '200':
          description: CMS подпись создана и сохранена
          content:
            application/json:
              schema:
                type: object
                properties:
                  sign_process_id:
                    type: string
                    format: uuid
                    description: ID записи в БД
                  group_id:
                    type: string
                    format: uuid
                    description: UUID группы подписания. Используйте этот ID для добавления следующих подписей в ту же группу.
                    example: "019ac07b-2b05-7341-9c97-3ce8598952f6"
                  ext_id:
                    type: integer
                    nullable: true
                    description: Внешний ID
                  status:
                    type: string
                    example: "SUCCESS"
                  file_name:
                    type: string
                    example: "document.pdf"
                  is_signed:
                    type: boolean
                    example: true
                  signed_at:
                    type: string
                    format: date-time
                    example: "2023-10-27T10:45:00+06:00"
                  dn_name:
                    type: string
                    example: "CN=ИВАНОВ ИВАН ИВАНОВИЧ,OU=НИЦ,O=ГО,L=Астана,C=KZ"
                  signatures_count:
                    type: integer
                    description: Количество подписей в группе
                    example: 1
                  signers:
                    type: array
                    description: Массив информации о всех подписантах
                    items:
                      type: object
                      properties:
                        valid:
                          type: boolean
                        subject:
                          type: object
                        issuer:
                          type: object
                        notBefore:
                          type: object
                        notAfter:
                          type: object
                        serialNumber:
                          type: string
                        keyUser:
                          type: array
                          items:
                            type: string
                        revocations:
                          type: array
                          description: Результаты проверки отзыва (OCSP/CRL)
                          items:
                            type: object
                            properties:
                              type:
                                type: string
                                enum: [OCSP, CRL]
                              status:
                                type: string
                                enum: [ACTIVE, REVOKED, UNKNOWN]
                              url:
                                type: string
                              error:
                                type: string
                        tsp:
                          type: object
                  signer:
                    type: object
                    description: Информация о первом подписанте (для удобства)
        '400':
          description: Ошибка создания или проверки подписи
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "ERROR"
                  error:
                    type: string
                    example: "Подпись не прошла валидацию"

  /cms/verify:
    post:
      tags:
        - CMS
      summary: Проверить CMS подпись
      description: |
        Проверяет CMS подпись и возвращает информацию о всех подписантах.
        Поддерживает проверку через OCSP и CRL.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cms
              properties:
                cms:
                  type: string
                  format: byte
                  description: CMS подпись в base64 (DER)
                data:
                  type: string
                  format: byte
                  description: Оригинальные данные в base64 (для detached подписи)
                revocationCheck:
                  type: array
                  items:
                    type: string
                    enum: [OCSP, CRL]
                  description: Методы проверки отзыва сертификата
      responses:
        '200':
          description: Подпись проверена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  valid:
                    type: boolean
                  signers:
                    type: array
                    items:
                      type: object
                      properties:
                        valid:
                          type: boolean
                        subject:
                          type: object
                        issuer:
                          type: object
                        notBefore:
                          type: object
                        notAfter:
                          type: object
                        serialNumber:
                          type: string
                        keyUser:
                          type: array
                          items:
                            type: string
                        revocations:
                          type: array
                          description: Результаты проверки отзыва (OCSP/CRL)
                          items:
                            type: object
                            properties:
                              type:
                                type: string
                                enum: [OCSP, CRL]
                              status:
                                type: string
                                enum: [ACTIVE, REVOKED, UNKNOWN]
                              url:
                                type: string
                              error:
                                type: string
                        tsp:
                          type: object
                          description: Информация о метке времени
                  signerCount:
                    type: integer
        '400':
          description: Ошибка проверки подписи

  /cms/extract:
    post:
      tags:
        - CMS
      summary: Извлечь данные из CMS
      description: Извлекает оригинальные данные из attached CMS подписи
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cms
              properties:
                cms:
                  type: string
                  format: byte
                  description: CMS подпись в base64 (DER)
      responses:
        '200':
          description: Данные извлечены
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  data:
                    type: string
                    format: byte
                    description: Извлеченные данные в base64
        '400':
          description: Ошибка извлечения данных

  /cms/pdf:
    post:
      tags:
        - CMS
      summary: Создать PDF с информацией о подписи
      description: |
        Извлекает данные из CMS и создает PDF с колонтитулом (dn_name подписанта).
        Если данные уже являются PDF, добавляет колонтитул к существующему документу.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cms
              properties:
                cms:
                  type: string
                  format: byte
                  description: CMS подпись в base64 (DER)
      responses:
        '200':
          description: PDF создан
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '400':
          description: Ошибка создания PDF

  /cms/merge:
    post:
      tags:
        - CMS
      summary: Объединить несколько CMS подписей
      description: |
        Объединяет массив CMS подписей в одну общую CMS подпись.
        Используется для создания документа с множественными подписями.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cmsList
              properties:
                cmsList:
                  type: array
                  minItems: 2
                  items:
                    type: string
                    format: byte
                  description: Массив CMS подписей в base64 (DER)
      responses:
        '200':
          description: CMS подписи объединены
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  cms:
                    type: string
                    format: byte
                    description: Объединенная CMS подпись в base64
        '400':
          description: Ошибка объединения

  # ==================== XML ====================
  /xml/sign:
    post:
      tags:
        - XML
      summary: Подписать XML документ
      description: |
        Подписывает XML документ по стандарту XMLDSig.
        Поддерживает очистку существующих подписей и нормализацию XML.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - xml
                - signer
              properties:
                xml:
                  type: string
                  description: XML документ
                signer:
                  type: object
                  required:
                    - key
                    - password
                  properties:
                    key:
                      type: string
                      format: byte
                      description: PKCS#12 ключ в base64
                    password:
                      type: string
                    keyAlias:
                      type: string
                clearSignatures:
                  type: boolean
                  default: false
                  description: Удалить существующие подписи перед подписанием
                trimXml:
                  type: boolean
                  default: false
                  description: Нормализовать XML (убрать лишние пробелы)
      responses:
        '200':
          description: XML подписан
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  xml:
                    type: string
                    description: Подписанный XML
        '400':
          description: Ошибка подписания

  /xml/verify:
    post:
      tags:
        - XML
      summary: Проверить XML подпись
      description: Проверяет XML подпись и возвращает информацию о подписанте
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - xml
              properties:
                xml:
                  type: string
                  description: Подписанный XML документ
                revocationCheck:
                  type: array
                  items:
                    type: string
                    enum: [OCSP, CRL]
      responses:
        '200':
          description: Подпись проверена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  valid:
                    type: boolean
                  signer:
                    type: object
        '400':
          description: Ошибка проверки

  # ==================== Certificate ====================
  /pkcs12/info:
    post:
      tags:
        - Certificate
      summary: Получить информацию о PKCS#12 ключе
      description: |
        Извлекает информацию о сертификате из PKCS#12 контейнера.
        Поддерживает проверку отзыва через OCSP и CRL.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - key
                - password
              properties:
                key:
                  type: string
                  format: byte
                  description: PKCS#12 ключ в base64
                password:
                  type: string
                  description: Пароль от ключа
                keyAlias:
                  type: string
                  description: Алиас ключа (для контейнеров с несколькими ключами)
                revocationCheck:
                  type: array
                  items:
                    type: string
                    enum: [OCSP, CRL]
      responses:
        '200':
          description: Информация получена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  valid:
                    type: boolean
                  certificate:
                    type: object
        '400':
          description: Ошибка чтения ключа

  /pkcs12/aliases:
    post:
      tags:
        - Certificate
      summary: Получить список алиасов в PKCS#12
      description: Возвращает список всех алиасов (ключей) в PKCS#12 контейнере
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - keys
              properties:
                keys:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - key
                      - password
                    properties:
                      key:
                        type: string
                        format: byte
                      password:
                        type: string
      responses:
        '200':
          description: Алиасы получены
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  aliases:
                    type: array
                    items:
                      type: array
                      items:
                        type: string
        '400':
          description: Ошибка чтения ключей

  /x509/info:
    post:
      tags:
        - Certificate
      summary: Получить информацию о X.509 сертификате
      description: Парсит X.509 сертификат и возвращает информацию о нем
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cert
              properties:
                cert:
                  type: string
                  format: byte
                  description: X.509 сертификат в base64 (DER)
                revocationCheck:
                  type: array
                  items:
                    type: string
                    enum: [OCSP, CRL]
      responses:
        '200':
          description: Информация получена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  valid:
                    type: boolean
                  certificate:
                    type: object
        '400':
          description: Ошибка парсинга сертификата

  # ==================== Document ====================
  /document/docx-to-pdf:
    post:
      tags:
        - Document
      summary: Конвертировать DOCX в PDF
      description: |
        Конвертирует DOCX файл в PDF используя LibreOffice.
        Поддерживает возврат как base64, так и бинарного файла.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - docx
              properties:
                docx:
                  type: string
                  format: byte
                  description: DOCX файл в base64
                is_file:
                  type: boolean
                  default: false
                  description: Вернуть PDF файлом (true) или base64 (false)
      responses:
        '200':
          description: Конвертация выполнена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  message:
                    type: string
                  pdf:
                    type: string
                    format: byte
                    description: PDF в base64 (если is_file=false)
            application/pdf:
              schema:
                type: string
                format: binary
                description: PDF файл (если is_file=true)
        '400':
          description: Ошибка конвертации

  /document/xlsx-to-pdf:
    post:
      tags:
        - Document
      summary: Конвертировать XLSX в PDF
      description: |
        Конвертирует Excel файл (XLSX) в PDF используя LibreOffice Calc.
        Поддерживает возврат как base64, так и бинарного файла.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - xlsx
              properties:
                xlsx:
                  type: string
                  format: byte
                  description: XLSX файл в base64
                is_file:
                  type: boolean
                  default: false
                  description: Вернуть PDF файлом (true) или base64 (false)
      responses:
        '200':
          description: Конвертация выполнена
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    example: 200
                  message:
                    type: string
                    example: OK
                  pdf:
                    type: string
                    format: byte
                    description: PDF в base64 (если is_file=false)
            application/pdf:
              schema:
                type: string
                format: binary
                description: PDF файл (если is_file=true)
        '400':
          description: Ошибка конвертации
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    example: 400
                  message:
                    type: string
                    example: Не удалось декодировать base64 данные

  /document/extract-pdf-with-signatures:
    post:
      tags:
        - Document
      summary: Извлечь PDF с информацией о подписях
      description: |
        Извлекает PDF из CMS подписи и добавляет страницу с детальной информацией о всех подписантах.
        Включает колонтитулы на каждой странице и отдельную страницу с полной информацией о подписях.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cms
              properties:
                cms:
                  type: string
                  format: byte
                  description: CMS подпись в base64 (DER)
      responses:
        '200':
          description: PDF с подписями
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '400':
          description: Ошибка извлечения

  # ==================== Callback Formats (back_url) ====================
  /callbacks/single-signature:
    post:
      tags:
        - Подпись (одиночная)
      summary: "[Callback] Формат ответа на back_url для одиночной подписи"
      description: |
        **Это НЕ endpoint API, а формат данных, которые отправляются на ваш back_url.**
        
        После успешного подписания документа система отправляет POST запрос на указанный `back_url` с данными о подписи.
        
        ### Статусы:
        - `SUCCESS` — документ успешно подписан
        - `ERROR` — ошибка при подписании
        - `EXPIRED` — срок подписания истёк
        
        ### Ожидаемый ответ:
        Ваш сервер должен вернуть JSON с полем `status`:
        - `{ "status": true }` или `{ "status": "ok" }` — подпись принята
        - `{ "status": false, "error": "причина" }` — подпись отклонена (будет откат)
      requestBody:
        description: Данные, отправляемые на ваш back_url
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SingleSignatureCallback'
            examples:
              success:
                summary: Успешная подпись
                value:
                  sign_process_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  group_id: "019b3c4d-aaaa-7890-abcd-ef1234567890"
                  ext_id: 12345
                  status: "SUCCESS"
                  file_name: "contract.pdf"
                  is_signed: true
                  signed_at: "2026-01-07T15:30:00+05:00"
                  dn_name: "CN=ИВАНОВ ИВАН,SERIALNUMBER=IIN123456789012,C=KZ"
                  signatures_count: 1
                  signer:
                    valid: true
                    subject:
                      commonName: "ИВАНОВ ИВАН"
                      iin: "123456789012"
                      dn: "CN=ИВАНОВ ИВАН,SERIALNUMBER=IIN123456789012,C=KZ"
                    notAfter:
                      iso: "2027-01-07T00:00:00+05:00"
                    tsp:
                      genTime: "2026-01-07T15:30:00+05:00"
              error:
                summary: Ошибка подписания
                value:
                  sign_process_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  ext_id: 12345
                  status: "ERROR"
                  error: "Сертификат отозван"
              expired:
                summary: Срок истёк
                value:
                  sign_process_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  ext_id: 12345
                  status: "EXPIRED"
      responses:
        '200':
          description: Ваш ответ на callback
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                    description: true — подпись принята, false — отклонена
                  error:
                    type: string
                    description: Причина отклонения (если status=false)
              examples:
                accepted:
                  summary: Подпись принята
                  value:
                    status: true
                rejected:
                  summary: Подпись отклонена
                  value:
                    status: false
                    error: "Документ уже был подписан другим пользователем"

  /callbacks/batch-signature:
    post:
      tags:
        - Подпись (массовая)
      summary: "[Callback] Формат ответа на back_url для массовой подписи"
      description: |
        **Это НЕ endpoint API, а формат данных, которые отправляются на ваш back_url.**
        
        Callback отправляется **один раз** когда **все** документы batch подписаны.
        
        ### Статусы:
        - `SUCCESS` — все документы успешно подписаны
        - `ERROR` — ошибка при подписании
        - `EXPIRED` — срок подписания истёк
        
        ### Ожидаемый ответ:
        Ваш сервер должен вернуть JSON с полем `status`:
        - `{ "status": true }` — подписи приняты
        - `{ "status": false, "error": "причина" }` — подписи отклонены (откат последней подписи)
      requestBody:
        description: Данные, отправляемые на ваш back_url
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchSignatureCallback'
            examples:
              success:
                summary: Все документы подписаны
                value:
                  batch_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  status: "SUCCESS"
                  documents_count: 2
                  signed_count: 2
                  expires_at: "2026-01-07T16:00:00+05:00"
                  documents:
                    - sign_process_id: "019b3c4d-1111-7890-abcd-ef1234567890"
                      group_id: "019b3c4d-aaaa-7890-abcd-ef1234567890"
                      ext_id: 1
                      file_name: "contract.pdf"
                      is_signed: true
                      dn_name: "CN=ИВАНОВ ИВАН,C=KZ"
                      signed_at: "2026-01-07T15:30:00+05:00"
                    - sign_process_id: "019b3c4d-2222-7890-abcd-ef1234567890"
                      group_id: "019b3c4d-bbbb-7890-abcd-ef1234567890"
                      ext_id: 2
                      file_name: "appendix.pdf"
                      is_signed: true
                      dn_name: "CN=ИВАНОВ ИВАН,C=KZ"
                      signed_at: "2026-01-07T15:30:05+05:00"
              expired:
                summary: Срок истёк
                value:
                  batch_id: "019b3c4d-5e6f-7890-abcd-ef1234567890"
                  status: "EXPIRED"
                  documents_count: 2
                  signed_count: 0
                  expires_at: "2026-01-07T14:00:00+05:00"
      responses:
        '200':
          description: Ваш ответ на callback
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                  error:
                    type: string
              examples:
                accepted:
                  summary: Подписи приняты
                  value:
                    status: true
                rejected:
                  summary: Подписи отклонены
                  value:
                    status: false
                    error: "Batch уже был обработан"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API ключ клиента (если API_AUTH_ENABLED=true). Ключи создаются в админ-панели /admin/api-clients
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Token
      description: API ключ клиента (если API_AUTH_ENABLED=true). Ключи создаются в админ-панели /admin/api-clients
  
  schemas:
    SingleSignatureCallback:
      type: object
      description: Формат callback для одиночной подписи
      properties:
        sign_process_id:
          type: string
          format: uuid
          description: ID процесса подписания
        group_id:
          type: string
          format: uuid
          description: UUID группы подписантов (для множественных подписей)
        ext_id:
          type: integer
          nullable: true
          description: Внешний ID (ваш идентификатор)
        status:
          type: string
          enum: [SUCCESS, ERROR, EXPIRED]
          description: Статус подписания
        file_name:
          type: string
          description: Имя файла (только для SUCCESS)
        is_signed:
          type: boolean
          description: Подписан ли документ (только для SUCCESS)
        signed_at:
          type: string
          format: date-time
          description: Дата/время подписания (только для SUCCESS)
        dn_name:
          type: string
          description: Distinguished Name подписанта (только для SUCCESS)
        signatures_count:
          type: integer
          description: Количество подписей в группе (только для SUCCESS)
        signer:
          $ref: '#/components/schemas/SignerInfo'
        error:
          type: string
          description: Текст ошибки (только для ERROR)

    BatchSignatureCallback:
      type: object
      description: Формат callback для массовой подписи
      properties:
        batch_id:
          type: string
          format: uuid
          description: ID batch
        status:
          type: string
          enum: [SUCCESS, ERROR, EXPIRED]
          description: Статус подписания
        documents_count:
          type: integer
          description: Общее количество документов в batch
        signed_count:
          type: integer
          description: Количество подписанных документов
        expires_at:
          type: string
          format: date-time
          description: Срок действия ссылки
        documents:
          type: array
          description: Список документов (только для SUCCESS)
          items:
            type: object
            properties:
              sign_process_id:
                type: string
                format: uuid
              group_id:
                type: string
                format: uuid
                description: UUID группы подписантов
              ext_id:
                type: integer
                nullable: true
              file_name:
                type: string
              is_signed:
                type: boolean
              dn_name:
                type: string
                nullable: true
              signed_at:
                type: string
                format: date-time
                nullable: true
        error:
          type: string
          description: Текст ошибки (только для ERROR)

    Error:
      type: object
      properties:
        status:
          type: integer
        message:
          type: string
        
    SignerInfo:
      type: object
      properties:
        valid:
          type: boolean
        subject:
          type: object
          properties:
            commonName:
              type: string
            iin:
              type: string
            bin:
              type: string
            organization:
              type: string
            dn:
              type: string
        issuer:
          type: object
        notBefore:
          type: object
          properties:
            iso:
              type: string
              format: date-time
            formatted:
              type: string
        notAfter:
          type: object
          properties:
            iso:
              type: string
              format: date-time
            formatted:
              type: string
        serialNumber:
          type: string
        keyUser:
          type: array
          items:
            type: string
            enum: [INDIVIDUAL, ORGANIZATION]
        revocations:
          type: array
          description: Результаты проверки отзыва сертификата (OCSP/CRL)
          items:
            type: object
            required:
              - type
              - status
            properties:
              type:
                type: string
                enum: [OCSP, CRL]
                description: Тип проверки
              status:
                type: string
                enum: [ACTIVE, REVOKED, UNKNOWN]
                description: |
                  Статус сертификата (соответствует RFC 6960 OCSP):
                  - ACTIVE — сертификат действителен (OCSP: GOOD)
                  - REVOKED — сертификат отозван (OCSP: REVOKED)
                  - UNKNOWN — проверка не удалась (ошибка OCSP/CRL, проблемы с цепочкой, CA не знает о сертификате)
                  
                  Примечание: просрочка сертификата определяется по полям notBefore/notAfter, а не через OCSP/CRL
              url:
                type: string
                description: URL OCSP сервера или путь к CRL файлу
              algorithm:
                type: string
                description: Алгоритм сертификата (GOST/RSA), только для CRL
              error:
                type: string
                description: Сообщение об ошибке (если status не ACTIVE)
        tsp:
          type: object
          description: Информация о метке времени
          properties:
            genTime:
              type: string
              format: date-time
            genTimeFormatted:
              type: string
            serialNumber:
              type: string
            policy:
              type: string
            hash:
              type: string
            tspHashAlgorithm:
              type: string
