openapi: 3.1.0

info:
  title: AutoCall.kz API
  version: "1.0"
  description: |
    REST API сервиса автообзвона AutoCall.kz.

    ## Публичный API
    Эндпоинты `/api/public/*` доступны без аутентификации — тарифы и тестовый звонок.

    ## API v1 (авторизованный)
    Все эндпоинты `/api/v1/*` требуют заголовок:
    ```
    Authorization: Bearer ВАШ_API_КЛЮЧ
    ```
    API ключ доступен в разделе **Настройки → API** на портале autocall.kz.

    ## Лимиты
    По умолчанию — **300 запросов в минуту**. Для увеличения обратитесь в поддержку.

    ## Форматы дат
    - Дата: `Y-m-d` (например: `2024-03-15`)
    - Время: `H:i:s` (например: `13:00:00`)
    - Дата + время: `Y-m-d H:i:s`
  contact:
    name: Поддержка AutoCall.kz
    email: info@autocall.kz
    url: https://autocall.kz

servers:
  - url: https://autocall.kz/api/v1
    description: API v1 (требует Bearer токен)
  - url: https://autocall.kz/api/public
    description: Публичный API (без аутентификации)

security:
  - bearerAuth: []

tags:
  - name: Публичный API
    description: Эндпоинты без аутентификации — тарифы и тестовый звонок.
  - name: Списки контактов
    description: Создание и управление списками контактов для обзвонов и рассылок.
  - name: Контакты
    description: Просмотр и удаление отдельных контактов внутри списков.
  - name: Аудиозаписи
    description: Загрузка аудиофайлов и синтез речи для использования в обзвонах.
  - name: Сценарии
    description: Интерактивные сценарии для обзвонов с распознаванием речи и ветвлением логики.
  - name: Обзвоны
    description: Создание и управление автоматическими обзвонами.
  - name: SMS рассылки
    description: Создание и управление SMS рассылками.
  - name: Настройки
    description: Управление настройками аккаунта, номерами и визитной карточкой.
  - name: Платежи
    description: Управление балансом и просмотр транзакций.
  - name: Панель мониторинга
    description: Сводная статистика и активность аккаунта.
  - name: Уведомления
    description: Получение и управление уведомлениями аккаунта.
  - name: Flash Calls
    description: |
      Верификация пользователей через звонок (Flash Call / Missed Call OTP).

      Сервис совершает звонок на номер абонента. Последние N цифр номера звонящего и являются кодом подтверждения.
      Звонок сбрасывается автоматически — биллинг на стороне абонента не возникает.

      **Два режима:**
      - **Авто-код** (`digits`) — сервис сам генерирует случайный код из N цифр. Работает всегда.
      - **Свой код** (`code`) — вы передаёте нужный код. Может вернуть 422 если нужного номера нет в пуле.

      **Rate limit:** не более 3 попыток на один номер в течение 5 минут.

paths:

  # ─────────────────────────────────────────
  # ПУБЛИЧНЫЙ API (без авторизации)
  # ─────────────────────────────────────────

  /tariffs:
    servers:
      - url: https://autocall.kz/api/public
    get:
      operationId: getTariffs
      summary: Получить актуальные тарифы
      tags: [Публичный API]
      security: []
      description: Возвращает список тарифных планов с ценами на звонки, SMS и синтез речи. Данные кешируются на 1 час.
      responses:
        "200":
          description: Список тарифов
          content:
            application/json:
              schema:
                type: object
                properties:
                  currency:
                    type: string
                    example: KZT
                  note:
                    type: string
                  tariffs:
                    type: array
                    items:
                      $ref: "#/components/schemas/Tariff"
              example:
                currency: KZT
                note: "Цены за единицу: звонок (секунда тарификации), SMS (1 сообщение)."
                tariffs:
                  - name: BRONZE
                    threshold: 0
                    call_price: 3.5
                    sms_price: 18
                    synthesis_price: 2
                    speech_price: 2
                    recording_price: 1
                  - name: SILVER
                    threshold: 50000
                    call_price: 2.8
                    sms_price: 16
                    synthesis_price: 1.8
                    speech_price: 1.8
                    recording_price: 0.8
                  - name: GOLD
                    threshold: 200000
                    call_price: 2.2
                    sms_price: 14
                    synthesis_price: 1.5
                    speech_price: 1.5
                    recording_price: 0.5

  /test-call:
    servers:
      - url: https://autocall.kz/api/public
    post:
      operationId: sendTestCall
      summary: Отправить тестовый звонок
      tags: [Публичный API]
      security: []
      description: |
        Инициирует реальный тестовый звонок от AutoCall.kz на указанный казахстанский номер телефона.

        Ограничения:
        - Один звонок на номер в сутки
        - Доступно с 09:00 до 21:30 по Алматы (UTC+6)
        - Только казахстанские мобильные номера (7xxxxxxxxxx)
        - Rate limit: 2 запроса в минуту с одного IP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - phone
              properties:
                phone:
                  type: string
                  description: Казахстанский мобильный номер без +7, только цифры после 7
                  pattern: "^(700|701|702|705|706|707|708|747|771|775|776|777|778|710|711|712|713|714|715|716|717|718|721|722|723|724|725|726|727|728|729|736)\\d{7}$"
                  example: "7771234567"
            example:
              phone: "7771234567"
      responses:
        "200":
          description: Звонок успешно инициирован
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  number:
                    type: string
              example:
                message: "Тестовый звонок отправлен на номер +77771234567. Ожидайте звонка в течение 1–2 минут."
                number: "+77771234567"
        "422":
          description: Валидация не прошла или звонки недоступны сейчас
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorMessage"
        "429":
          description: Номер уже получил тестовый звонок сегодня или превышен rate limit
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorMessage"
        "403":
          description: Номер в чёрном списке или запрос заблокирован
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorMessage"

  # ─────────────────────────────────────────
  # СПИСКИ КОНТАКТОВ
  # ─────────────────────────────────────────

  /lists:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить все списки контактов
      operationId: get-lists
      tags: [Списки контактов]
      description: Возвращает все списки контактов с постраничным выводом. Поддерживает фильтрацию по дате и client_id.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/DateFrom'
        - $ref: '#/components/parameters/DateTo'
        - $ref: '#/components/parameters/Order'
        - $ref: '#/components/parameters/ClientIdFilter'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Объект постраничного вывода со списками контактов.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pagination'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/ContactList'
              example:
                current_page: 1
                data:
                  - id: 73
                    client_id: "123e4567-e89b-12d3-a456-426655440000"
                    name: "Клиенты март"
                    importing: false
                    nonexistent: null
                    created_at: "2024-03-31 05:55:19"
                    updated_at: "2024-03-31 05:55:19"
                    contacts_count: 250
                    used: 3
                first_page_url: "https://autocall.kz/api/v1/lists?page=1"
                from: 1
                last_page: 2
                last_page_url: "https://autocall.kz/api/v1/lists?page=2"
                next_page_url: "https://autocall.kz/api/v1/lists?page=2"
                path: "https://autocall.kz/api/v1/lists"
                per_page: 23
                prev_page_url: null
                to: 23
                total: 27
        '401': { $ref: '#/components/responses/401' }
        '404':
          description: Список не найден по указанному `client_id`.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorMessage' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Создать новый список контактов
      operationId: post-lists
      tags: [Списки контактов]
      description: |
        Создаёт новый список контактов. Максимум 10 000 номеров в одном запросе.
        Для загрузки большего количества — создайте список с первыми 10 000 контактами, затем дополните через `/lists/append/{id}`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: Параметры нового списка контактов.
        content:
          application/json:
            schema:
              type: object
              required: [name, numbers]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                  description: Название списка
                numbers:
                  type: array
                  maxItems: 10000
                  description: Массив с номерами телефонов
                  items:
                    type: object
                    required: [number]
                    properties:
                      number:
                        type: string
                        minLength: 12
                        maxLength: 12
                        pattern: '^\+7(700|701|702|705|707|708|747|771|775|776|777|778|710|711|712|713|714|715|716|717|718|721|722|723|724|725|726|727|728|729|736)\d{7}$'
                        description: Номер телефона в формате +7XXXXXXXXXX
                      extra:
                        type: string
                        maxLength: 30
                        description: Дополнительное описание (ФИО, ИИН и т.п.)
                client_id:
                  type: string
                  maxLength: 256
                  description: ID в Вашей системе
            examples:
              Простой список:
                value:
                  name: "Клиенты апрель"
                  numbers:
                    - number: "+77010000001"
                    - number: "+77010000002"
              С client_id и описанием:
                value:
                  name: "VIP клиенты"
                  client_id: "vip-2024"
                  numbers:
                    - number: "+77010000001"
                      extra: "Иванов И.И."
                    - number: "+77010000002"
                      extra: "Петров П.П."
      responses:
        '201':
          description: Список успешно создан.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactList'
              example:
                id: 74
                name: "Клиенты апрель"
                client_id: "vip-2024"
                contacts_count: 2
                created_at: "2024-04-11 22:14:09"
                updated_at: "2024-04-11 22:14:09"
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /lists/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID списка
    get:
      summary: Показать список
      operationId: get-lists-id
      tags: [Списки контактов]
      description: Возвращает детальную информацию о списке с контактами (постраничный вывод).
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Объект списка с контактами.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ContactList'
                  - type: object
                    properties:
                      contacts:
                        allOf:
                          - $ref: '#/components/schemas/Pagination'
                          - type: object
                            properties:
                              data:
                                type: array
                                items:
                                  $ref: '#/components/schemas/Contact'
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    put:
      summary: Редактировать список
      operationId: put-lists-id
      tags: [Списки контактов]
      description: Изменяет название или client_id списка контактов.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                client_id:
                  type: string
                  maxLength: 256
      responses:
        '200':
          description: Обновлённый объект списка.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContactList' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    delete:
      summary: Удалить список
      operationId: delete-lists-id
      tags: [Списки контактов]
      description: Удаляет список контактов и все контакты в нём.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Список удалён.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /lists/append/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID списка, который нужно дополнить
    post:
      summary: Дополнить список контактов
      operationId: post-lists-append-id
      tags: [Списки контактов]
      description: Добавляет номера в уже существующий список контактов.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [numbers]
              properties:
                numbers:
                  type: array
                  maxItems: 10000
                  items:
                    type: object
                    required: [number]
                    properties:
                      number:
                        type: string
                        minLength: 12
                        maxLength: 12
                        pattern: '^\+7(700|701|702|705|707|708|747|771|775|776|777|778|710|711|712|713|714|715|716|717|718|721|722|723|724|725|726|727|728|729|736)\d{7}$'
                      extra:
                        type: string
                        maxLength: 30
      responses:
        '201':
          description: Список успешно дополнен.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  client_id: { type: [string, "null"] }
                  contacts_count: { type: integer }
                  created_at: { type: string }
                  updated_at: { type: string }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }

  /lists/nonexistent/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID списка
    delete:
      summary: Удалить несуществующие номера из списка
      operationId: delete-lists-nonexistent-id
      tags: [Списки контактов]
      description: |
        Удаляет из списка номера, которые оператор пометил как несуществующие в ходе обзвонов.
        Несуществующие номера появляются в поле `nonexistent` после завершения обзвона.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Несуществующие номера удалены.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    put:
      summary: Проигнорировать несуществующие номера
      operationId: put-lists-nonexistent-id
      tags: [Списки контактов]
      description: Сбрасывает флаг несуществующих номеров в списке без их удаления.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Флаг сброшен, возвращается обновлённый объект списка.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContactList' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # КОНТАКТЫ
  # ─────────────────────────────────────────

  /contacts/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID контакта
    get:
      summary: Показать контакт
      operationId: get-contacts-id
      tags: [Контакты]
      description: Возвращает информацию о конкретном контакте по его ID.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект контакта.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Contact' }
              example:
                id: 49
                list_id: 73
                number: "+77010000001"
                extra: "Иванов И.И."
                created_at: "2024-03-31 05:55:19"
                updated_at: "2024-03-31 05:55:19"
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    delete:
      summary: Удалить контакт
      operationId: delete-contacts-id
      tags: [Контакты]
      description: Удаляет контакт из списка контактов.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Контакт удалён.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # АУДИОЗАПИСИ
  # ─────────────────────────────────────────

  /sounds:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить все аудиозаписи
      operationId: get-sounds
      tags: [Аудиозаписи]
      description: Возвращает все аудиозаписи аккаунта с постраничным выводом.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Постраничный вывод аудиозаписей.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pagination'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Sound'
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Создать аудиозапись
      operationId: post-sounds
      tags: [Аудиозаписи]
      description: |
        Создаёт аудиозапись двумя способами:
        - `synthesis` — синтез речи из текста
        - `upload` — загрузка файла mp3/wav/ogg в формате Base64
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: Параметры новой аудиозаписи.
        content:
          application/json:
            schema:
              type: object
              required: [name, type, data]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                  description: Название аудиозаписи
                type:
                  type: string
                  enum: [synthesis, upload]
                  description: Тип — синтез речи или загрузка файла
                data:
                  type: array
                  minItems: 1
                  maxItems: 1
                  description: Массив с одним объектом параметров аудио
                  items:
                    type: object
                    properties:
                      text:
                        type: string
                        minLength: 1
                        maxLength: 400
                        description: "Если type=synthesis: текст для синтеза (обязательно)"
                      voice:
                        type: string
                        default: ru-RU-Wavenet-B
                        description: "Если type=synthesis: голос для синтеза"
                        enum:
                          - ru-RU-Wavenet-A
                          - ru-RU-Wavenet-B
                          - ru-RU-Wavenet-C
                          - ru-RU-Wavenet-D
                          - alyss
                          - jane
                          - oksana
                          - omazh
                          - zahar
                          - ermil
                      speed:
                        type: number
                        default: 1
                        minimum: 0.25
                        maximum: 4
                        description: "Если type=synthesis: скорость речи"
                      pitch:
                        type: number
                        default: 0
                        minimum: -20
                        maximum: 20
                        description: "Если type=synthesis: высота тона"
                      file:
                        type: string
                        description: "Если type=upload: файл в формате Base64 (mp3/wav/ogg, обязательно)"
                client_id:
                  type: string
                  maxLength: 60
                  description: ID в Вашей системе
            examples:
              Синтез речи:
                value:
                  name: "Уведомление об оплате"
                  type: synthesis
                  data:
                    - text: "Здравствуйте! Напоминаем об оплате до конца месяца."
                      voice: ru-RU-Wavenet-B
                      speed: 1
                      pitch: 0
              Загрузка файла:
                value:
                  name: "Моё приветствие"
                  type: upload
                  data:
                    - file: "base64_строка_файла_mp3"
      responses:
        '201':
          description: Аудиозапись создана и отправлена на модерацию.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Sound' }
        '401': { $ref: '#/components/responses/401' }
        '402':
          description: Недостаточно средств для синтеза речи.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorMessage' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /sounds/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID аудиозаписи
    get:
      summary: Показать аудиозапись
      operationId: get-sounds-id
      tags: [Аудиозаписи]
      description: Возвращает детальную информацию об аудиозаписи.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект аудиозаписи.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Sound' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    put:
      summary: Редактировать аудиозапись
      operationId: put-sounds-id
      tags: [Аудиозаписи]
      description: Изменяет название или client_id аудиозаписи.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                client_id:
                  type: string
                  maxLength: 60
      responses:
        '200':
          description: Обновлённый объект аудиозаписи.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Sound' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    delete:
      summary: Удалить аудиозапись
      operationId: delete-sounds-id
      tags: [Аудиозаписи]
      description: Удаляет аудиозапись из аккаунта.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Аудиозапись удалена.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /sounds/listen/{file}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: file
        in: path
        required: true
        schema: { type: string }
        description: Имя аудиофайла из поля `data` интерактивных обзвонов
    get:
      summary: Получить запись из интерактивного обзвона
      operationId: get-sounds-listen-file
      tags: [Аудиозаписи]
      description: |
        Доступ к записям голосовых ответов абонентов в интерактивных обзвонах (элемент типа `record`).

        В зависимости от параметра `type`:
        - `download` — сервер вернёт файл для скачивания
        - `text` — сервер вернёт JSON с распознанным текстом (тарифицируется)
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - name: type
          in: query
          required: true
          schema:
            type: string
            enum: [download, text]
          description: Способ получения записи
      responses:
        '200':
          description: Файл для скачивания или объект с текстом.
          content:
            application/json:
              schema:
                type: object
                required: [text]
                properties:
                  text:
                    type: [string, "null"]
                    description: Распознанный текст (только при type=text)
        '401': { $ref: '#/components/responses/401' }
        '402':
          description: Недостаточно средств для распознавания речи.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorMessage' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }

  # ─────────────────────────────────────────
  # СЦЕНАРИИ
  # ─────────────────────────────────────────

  /scenarios:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить все сценарии
      operationId: get-scenarios
      tags: [Сценарии]
      description: Возвращает все сценарии аккаунта с постраничным выводом.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Постраничный вывод сценариев.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pagination'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Scenario'
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Создать сценарий
      operationId: post-scenarios
      tags: [Сценарии]
      description: Создаёт новый интерактивный сценарий для обзвонов. Сценарий передаётся как JSON-строка в параметре `data`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: Параметры нового сценария.
        content:
          application/json:
            schema:
              type: object
              required: [name, data]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                  description: Название сценария
                data:
                  type: string
                  description: Сценарий в виде JSON-строки (json_encode)
                client_id:
                  type: string
                  maxLength: 60
                  description: ID в Вашей системе
      responses:
        '201':
          description: Сценарий создан и отправлен на модерацию.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Scenario' }
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /scenarios/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID сценария
    get:
      summary: Показать сценарий
      operationId: get-scenarios-id
      tags: [Сценарии]
      description: Возвращает детальную информацию о сценарии, включая его структуру.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект сценария с полной структурой.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ScenarioDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    put:
      summary: Редактировать сценарий
      operationId: put-scenarios-id
      tags: [Сценарии]
      description: Изменяет название, структуру или client_id сценария.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, minLength: 1, maxLength: 40 }
                data: { type: string }
                client_id: { type: string, maxLength: 60 }
      responses:
        '200':
          description: Обновлённый объект сценария.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ScenarioDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    delete:
      summary: Удалить сценарий
      operationId: delete-scenarios-id
      tags: [Сценарии]
      description: Удаляет сценарий из аккаунта.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Сценарий удалён.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # ОБЗВОНЫ
  # ─────────────────────────────────────────

  /autocalls:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить все обзвоны
      operationId: get-autocalls
      tags: [Обзвоны]
      description: |
        Возвращает все обзвоны аккаунта с постраничным выводом.

        #### Статусы обзвонов
        | Статус | Значение | Окончательный |
        |--------|----------|---------------|
        | `running` | выполняется | нет |
        | `completed` | завершён успешно | да |
        | `awaiting` | ожидает своего времени | нет |
        | `moderation` | на модерации | нет |
        | `denied` | запрещён модераторами | да |
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/DateFrom'
        - $ref: '#/components/parameters/DateTo'
        - $ref: '#/components/parameters/Order'
        - $ref: '#/components/parameters/ClientIdFilter'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Постраничный вывод обзвонов.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pagination'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/AutocallSummary'
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Создать обзвон
      operationId: post-autocalls
      tags: [Обзвоны]
      description: |
        Создаёт новый обзвон. Поддерживает два типа:
        - `regular` — обычный обзвон с аудиозаписью
        - `interactive` — интерактивный обзвон по сценарию

        В `list_id` можно передавать как ID существующих списков, так и объекты с номерами напрямую (будет создан автоматический список).
        В `audio_id` можно передавать как ID существующих аудиозаписей, так и объекты для создания новых (синтез или загрузка).

        Если общее количество номеров ≤ 5000 — ответ `201` (создан сразу).
        Если > 5000 — ответ `202` (создаётся в фоне).
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: Параметры нового обзвона.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AutocallCreate'
            examples:
              Обычный обзвон:
                value:
                  name: "Уведомление об акции"
                  type: regular
                  time_from: "10:00:00"
                  time_to: "20:00:00"
                  audio_id: [5]
                  list_id: [10]
              Интерактивный обзвон:
                value:
                  name: "Опрос удовлетворённости"
                  type: interactive
                  time_from: "10:00:00"
                  time_to: "18:00:00"
                  scenario_id: 512
                  list_id: [15]
                  max_tries: 3
                  tries_interval: 60
                  record: true
                  callback_url: "https://example.com/reports"
              С новыми номерами и синтезом:
                value:
                  name: "Код подтверждения"
                  type: regular
                  time_from: "00:00:00"
                  time_to: "23:59:59"
                  audio_id:
                    - type: synthesis
                      data:
                        text: "Ваш код подтверждения: 3, 0, 7, 5."
                  list_id:
                    - number: "+77010000001"
                  sms_unanswered: true
                  text_unanswered: "Ваш код: 3075"
      responses:
        '201':
          description: Обзвон создан (до 5000 номеров).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutocallDetail' }
        '202':
          description: Обзвон принят в обработку (более 5000 номеров, создаётся в фоне).
        '401': { $ref: '#/components/responses/401' }
        '403':
          description: Обзвон отклонён из-за подозрения в спаме (слишком много номеров с короткой аудиозаписью).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ValidationError' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }

  /autocalls/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID обзвона
    get:
      summary: Показать обзвон
      operationId: get-autocalls-id
      tags: [Обзвоны]
      description: |
        Возвращает детальную информацию об обзвоне, включая статистику и постраничный список всех звонков.

        #### Статусы звонков
        | Статус | Значение | Окончательный |
        |--------|----------|---------------|
        | `in_queue` | в очереди | нет |
        | `success` | успешно | да |
        | `recalled` | перезвонил сам | да |
        | `no_answer` | нет ответа | да |
        | `busy` | занято | да |
        | `error` | не существует | да |
        | `spam` | пожаловался на спам | да |
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Детальный объект обзвона со статистикой и звонками.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutocallDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    put:
      summary: Редактировать обзвон
      operationId: put-autocalls-id
      tags: [Обзвоны]
      description: |
        Изменяет параметры обзвона. Доступно только для обзвонов в статусах `awaiting` и `moderation`.
        Редактируются только перечисленные ниже поля.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, minLength: 1, maxLength: 40 }
                time_from: { type: string, minLength: 8, maxLength: 8 }
                time_to: { type: string, minLength: 8, maxLength: 8 }
                tries_interval:
                  type: integer
                  enum: [15, 30, 60, 120]
                date_from: { type: string, minLength: 10, maxLength: 10 }
                client_id: { type: string, maxLength: 60 }
                callback_url: { type: string, maxLength: 256 }
      responses:
        '200':
          description: Обновлённый объект обзвона.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutocallDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    delete:
      summary: Удалить обзвон
      operationId: delete-autocalls-id
      tags: [Обзвоны]
      description: Удаляет обзвон. Нельзя удалить обзвон в статусе `running`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Обзвон удалён.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /autocalls/calculate:
    servers:
      - url: https://autocall.kz/api/v1
    post:
      summary: Узнать стоимость обзвона
      operationId: post-autocalls-calculate
      tags: [Обзвоны]
      description: Рассчитывает максимальную стоимость обзвона до его создания. Принимает те же параметры, что и создание обзвона.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AutocallCreate' }
      responses:
        '201':
          description: Максимальная стоимость обзвона.
          content:
            application/json:
              schema:
                type: object
                required: [max_cost]
                properties:
                  max_cost: { type: number }
              example:
                max_cost: 245.60
        '401': { $ref: '#/components/responses/401' }
        '403':
          description: Обзвон отклонён из-за подозрения в спаме.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ValidationError' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }

  /autocalls/relaunch/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID завершённого обзвона
    get:
      summary: Перезапустить обзвон
      operationId: get-autocalls-relaunch-id
      tags: [Обзвоны]
      description: |
        Создаёт новый список контактов из номеров завершённого обзвона, которые **не были успешно обзвонены**.
        Удобно для повторного обзвона недозвонившихся.
        Работает только для обзвонов со статусом `completed`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '201':
          description: Новый список контактов создан.
          content:
            application/json:
              schema:
                type: object
                required: [id, name, updated_at, created_at, used, contacts_count, importing]
                properties:
                  id: { type: integer, description: ID нового списка }
                  name: { type: string }
                  created_at: { type: string }
                  updated_at: { type: string }
                  used: { type: integer }
                  contacts_count: { type: integer }
                  importing: { type: boolean }
        '401': { $ref: '#/components/responses/401' }
        '403':
          description: Обзвон ещё не завершён.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ValidationError' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }

  /calls/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID звонка
    get:
      summary: Показать звонок
      operationId: get-calls-id
      tags: [Обзвоны]
      description: Возвращает детальную информацию о конкретном звонке по его ID.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект звонка.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Call' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }

  /calls/recordings/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID звонка
    get:
      summary: Скачать запись разговора
      operationId: get-calls-recordings-id
      tags: [Обзвоны]
      description: |
        Возвращает аудиофайл записи разговора для указанного звонка.
        Записи доступны только если при создании обзвона был указан параметр `record: true`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Аудиофайл записи разговора (бинарный контент).
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }

  # ─────────────────────────────────────────
  # SMS РАССЫЛКИ
  # ─────────────────────────────────────────

  /bulks:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить все рассылки
      operationId: get-bulks
      tags: [SMS рассылки]
      description: Возвращает все SMS рассылки аккаунта с постраничным выводом.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/DateFrom'
        - $ref: '#/components/parameters/DateTo'
        - $ref: '#/components/parameters/Order'
        - $ref: '#/components/parameters/ClientIdFilter'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Постраничный вывод рассылок.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pagination'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/BulkSummary'
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Создать рассылку
      operationId: post-bulks
      tags: [SMS рассылки]
      description: |
        Создаёт новую SMS рассылку.
        В `list_id` можно передавать ID существующих списков или объекты с номерами напрямую (максимум 5000).
        Для большего количества — сначала создайте список контактов через `/lists`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: Параметры рассылки.
        content:
          application/json:
            schema:
              type: object
              required: [name, text, list_id]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 40
                  description: Название рассылки
                text:
                  type: string
                  minLength: 1
                  maxLength: 1600
                  description: Текст SMS сообщения
                list_id:
                  type: array
                  minItems: 1
                  maxItems: 5000
                  description: ID списков контактов и/или объекты с номерами
                  items:
                    oneOf:
                      - type: integer
                        description: ID существующего списка
                      - type: object
                        required: [number]
                        properties:
                          number: { type: string, minLength: 12, maxLength: 12 }
                          extra: { type: string, maxLength: 60 }
                date_from:
                  type: string
                  minLength: 10
                  maxLength: 10
                  description: "День запуска в формате Y-m-d (по умолчанию: сегодня)"
                time_from:
                  type: string
                  minLength: 8
                  maxLength: 8
                  description: "Время запуска в формате H:i:s (по умолчанию: сейчас)"
                client_id:
                  type: string
                  maxLength: 60
                  description: ID в Вашей системе
                callback_url:
                  type: string
                  maxLength: 256
                  description: URL для доставки отчётов о рассылке и сообщениях
            example:
              name: "Акция — ноябрь"
              text: "Скидка 20% только сегодня! Промокод: SALE20"
              list_id: [10, 15]
              time_from: "10:00:00"
              callback_url: "https://example.com/sms-reports"
      responses:
        '201':
          description: Рассылка создана.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BulkDetail' }
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /bulks/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
        description: ID рассылки
    get:
      summary: Показать рассылку
      operationId: get-bulks-id
      tags: [SMS рассылки]
      description: Возвращает детальную информацию о рассылке и постраничный список всех сообщений.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Детальный объект рассылки.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BulkDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    put:
      summary: Редактировать рассылку
      operationId: put-bulks-id
      tags: [SMS рассылки]
      description: Изменяет параметры рассылки. Доступно только для рассылок в статусах `awaiting` и `moderation`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, minLength: 1, maxLength: 40 }
                time_from: { type: string, minLength: 8, maxLength: 8 }
                date_from: { type: string, minLength: 10, maxLength: 10 }
                client_id: { type: string, maxLength: 60 }
                callback_url: { type: string, maxLength: 256 }
      responses:
        '200':
          description: Обновлённый объект рассылки.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BulkDetail' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }
    delete:
      summary: Удалить рассылку
      operationId: delete-bulks-id
      tags: [SMS рассылки]
      description: Удаляет рассылку. Нельзя удалить рассылку в статусе `running`.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Рассылка удалена.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /bulks/calculate:
    servers:
      - url: https://autocall.kz/api/v1
    post:
      summary: Узнать стоимость рассылки
      operationId: post-bulks-calculate
      tags: [SMS рассылки]
      description: Рассчитывает максимальную стоимость рассылки до её создания. Принимает те же параметры, что и создание рассылки.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, text, list_id]
              properties:
                name: { type: string, minLength: 1, maxLength: 40 }
                text: { type: string, minLength: 1, maxLength: 1600 }
                list_id:
                  type: array
                  minItems: 1
                  maxItems: 6000
                  items:
                    oneOf:
                      - type: integer
                      - type: object
                        required: [number]
                        properties:
                          number: { type: string, minLength: 12, maxLength: 12 }
                          extra: { type: string, maxLength: 60 }
                date_from: { type: string, minLength: 10, maxLength: 10 }
                time_from: { type: string, minLength: 8, maxLength: 8 }
                client_id: { type: string, maxLength: 60 }
                callback_url: { type: string, maxLength: 256 }
      responses:
        '201':
          description: Максимальная стоимость рассылки.
          content:
            application/json:
              schema:
                type: object
                properties:
                  max_cost: { type: number }
              example:
                max_cost: 120.00
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /messages/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: string }
        description: ID сообщения
    get:
      summary: Показать сообщение
      operationId: get-messages-id
      tags: [SMS рассылки]
      description: Возвращает детальную информацию о конкретном SMS сообщении.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект сообщения.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Message' }
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }

  # ─────────────────────────────────────────
  # НАСТРОЙКИ
  # ─────────────────────────────────────────

  /settings:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить настройки аккаунта
      operationId: get-settings
      tags: [Настройки]
      description: Возвращает текущие настройки аккаунта, тарифы, подтверждённые номера и общую статистику.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект с настройками аккаунта.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AccountSettings' }
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    put:
      summary: Редактировать настройки
      operationId: put-settings
      tags: [Настройки]
      description: Изменяет имя, подписки и визитную карточку аккаунта.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        description: Отправьте только те поля, которые нужно изменить.
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 2
                  maxLength: 40
                  description: Имя пользователя
                subscribe:
                  type: boolean
                  description: Подписка на новости через звонки
                notifications_subscribe:
                  type: boolean
                  description: Дублирование уведомлений через SMS
                visit_card_audio:
                  type: integer
                  description: ID аудиозаписи для визитной карточки
                visit_card_text:
                  type: string
                  maxLength: 160
                  description: Текст SMS визитной карточки (до 1 SMS)
            example:
              name: "Жандос"
              subscribe: true
              notifications_subscribe: false
              visit_card_text: "Компания AutoCall.kz, тел: +77470000000"
      responses:
        '200':
          description: Обновлённые настройки аккаунта.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AccountSettings' }
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /settings/{visit_card_type}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: visit_card_type
        in: path
        required: true
        schema:
          type: string
          enum: [visit_card_audio, visit_card_text]
        description: Тип визитной карточки
    delete:
      summary: Удалить визитную карточку
      operationId: delete-settings-visit-card
      tags: [Настройки]
      description: Удаляет аудио или текстовую визитную карточку аккаунта.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Визитная карточка удалена.
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  /settings/confirmed_numbers:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить подтверждённые номера
      operationId: get-settings-confirmed-numbers
      tags: [Настройки]
      description: Возвращает все номера телефонов, добавленные в аккаунт.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Массив с номерами.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ConfirmedNumber'
              example:
                - id: 1
                  number: "+77010000001"
                  verified_at: "2019-12-08 23:46:06"
                  created_at: "2019-12-08 23:46:06"
                  updated_at: "2019-12-08 23:46:06"
                - id: 3
                  number: "+77010000002"
                  verified_at: null
                  created_at: "2024-03-19 17:10:27"
                  updated_at: "2024-03-19 17:10:27"
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Добавить или подтвердить номер
      operationId: post-settings-confirmed-numbers
      tags: [Настройки]
      description: |
        Двухшаговый процесс:
        1. Отправьте `number` — система позвонит на этот номер, последние 5 цифр номера звонящего = код подтверждения
        2. Отправьте `verification_code` — номер будет подтверждён

        Неподтверждённые номера удаляются автоматически через 10 минут.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                number:
                  type: string
                  minLength: 12
                  maxLength: 12
                  pattern: '^\+7(700|701|702|705|707|708|747|771|775|776|777|778)\d{7}$'
                  example: "+77020000001"
                  description: Номер для добавления (шаг 1)
                verification_code:
                  type: integer
                  minimum: 10000
                  maximum: 99999
                  description: Код подтверждения из последних 5 цифр входящего звонка (шаг 2)
            examples:
              Шаг 1 — добавить номер:
                value:
                  number: "+77020000001"
              Шаг 2 — подтвердить:
                value:
                  verification_code: 30756
      responses:
        '201':
          description: Номер добавлен (шаг 1) или подтверждён (шаг 2).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ConfirmedNumber' }
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # ПЛАТЕЖИ
  # ─────────────────────────────────────────

  /balance:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить баланс
      operationId: get-balance
      tags: [Платежи]
      description: Возвращает текущий баланс аккаунта.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект с балансом.
          content:
            application/json:
              schema:
                type: object
                required: [balance, currency]
                properties:
                  balance:
                    type: number
                    description: Остаток на балансе
                  currency:
                    type: string
                    description: Символ валюты
              example:
                balance: 1450.75
                currency: "₸"
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

  /payments:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить транзакции
      operationId: get-payments
      tags: [Платежи]
      description: Возвращает полную информацию о балансе и историю транзакций с постраничным выводом.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Объект с данными баланса и транзакциями.
          content:
            application/json:
              schema:
                type: object
                required: [balance, currency, reserved, incoming, outgoing, paid, used, transactions]
                properties:
                  balance: { type: number }
                  currency: { type: string }
                  reserved: { type: number, description: Замороженная сумма }
                  incoming: { type: number, description: Обороты по приходу }
                  outgoing: { type: number, description: Обороты по расходу }
                  paid: { type: number, description: Всего пополнено }
                  used: { type: number, description: Всего израсходовано }
                  transactions:
                    allOf:
                      - $ref: '#/components/schemas/Pagination'
                      - type: object
                        properties:
                          data:
                            type: array
                            items:
                              $ref: '#/components/schemas/Transaction'
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

    post:
      summary: Пополнить баланс
      operationId: post-payments
      tags: [Платежи]
      description: Создаёт платёжную сессию и возвращает ссылку на оплату через VISA/MASTERCARD. Минимальная сумма — 200 тенге.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount]
              properties:
                amount:
                  type: integer
                  minimum: 200
                  description: Сумма пополнения в тенге
            example:
              amount: 1000
      responses:
        '201':
          description: Ссылка на страницу оплаты.
          content:
            application/json:
              schema:
                type: object
                required: [payment_url]
                properties:
                  payment_url:
                    type: string
                    description: URL страницы оплаты
              example:
                payment_url: "https://3ds.kassanova.kz/payment/merchants/kassanova/payment.html?mdOrder=b5f7a05f&language=ru"
        '401': { $ref: '#/components/responses/401' }
        '422': { $ref: '#/components/responses/422' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # ПАНЕЛЬ МОНИТОРИНГА
  # ─────────────────────────────────────────

  /dashboard:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить данные панели мониторинга
      operationId: get-dashboard
      tags: [Панель мониторинга]
      description: Возвращает сводные данные аккаунта — активные обзвоны, баланс, статистику за неделю и за всё время.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект с данными панели мониторинга.
          content:
            application/json:
              schema:
                type: object
                required: ["running&awaiting", in_queue, reserved, balance, currency, week_activity, full_activity]
                properties:
                  "running&awaiting":
                    type: integer
                    description: Количество активных обзвонов (статусы running + awaiting)
                  in_queue:
                    type: integer
                    description: Количество номеров в очереди на звонок
                  reserved:
                    type: number
                    description: Замороженная сумма
                  balance:
                    type: number
                    description: Текущий баланс
                  currency:
                    type: string
                    description: Символ валюты
                  week_activity:
                    type: object
                    description: Статистика по дням за последнюю неделю (ключ — дата Y-m-d)
                    additionalProperties:
                      $ref: '#/components/schemas/DayActivity'
                  full_activity:
                    $ref: '#/components/schemas/DayActivity'
              example:
                "running&awaiting": 3
                in_queue: 145
                reserved: 408.83
                balance: 1450.75
                currency: "₸"
                week_activity:
                  "2024-11-01":
                    all: 120
                    success: 85
                    recalled: 5
                    no_answer: 22
                    busy: 5
                    error: 2
                    spam: 1
                full_activity:
                  all: 5420
                  success: 3850
                  recalled: 210
                  no_answer: 980
                  busy: 280
                  error: 85
                  spam: 15
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

  # ─────────────────────────────────────────
  # УВЕДОМЛЕНИЯ
  # ─────────────────────────────────────────

  /notifications:
    servers:
      - url: https://autocall.kz/api/v1
    get:
      summary: Получить уведомления
      operationId: get-notifications
      tags: [Уведомления]
      description: |
        Возвращает все уведомления аккаунта.

        #### Типы уведомлений
        | Тип | Описание |
        |-----|----------|
        | `SoundConfirmed` | Аудиозапись прошла/не прошла модерацию |
        | `SoundCommented` | Комментарий к аудиозаписи |
        | `SMSBulkConfirmed` | Рассылка прошла/не прошла модерацию |
        | `SMSBulkCommented` | Комментарий к рассылке |
        | `SMSBulkFinished` | Рассылка завершена |
        | `ScenarioConfirmed` | Сценарий прошёл модерацию |
        | `ScenarioCommented` | Комментарий к сценарию |
        | `AutocallConfirmed` | Обзвон прошёл/не прошёл модерацию |
        | `AutocallCommented` | Комментарий к обзвону |
        | `AutocallFinished` | Обзвон завершён |
        | `ImportComplete` | Импорт списка завершён |
        | `ImportFailure` | Ошибка импорта |
        | `NonexistentContactsDetected` | В списке обнаружены несуществующие номера |
        | `ExportIsReady` | Экспорт готов к скачиванию |
        | `ExportFailure` | Ошибка экспорта |
        | `RefreshBalance` | Изменение баланса |
        | `LowBalance` | Баланс опустился ниже установленного порога |
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: Объект с уведомлениями и счётчиком непрочитанных.
          content:
            application/json:
              schema:
                type: object
                required: [data, unread]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Notification'
                  unread:
                    type: integer
                    description: Количество непрочитанных уведомлений
              example:
                data:
                  - id: "4f227359-cbc5-4b4d-811f-e8846abe6f35"
                    type: "AutocallFinished"
                    data:
                      id: 42
                      name: "Акция ноябрь"
                    read_at: null
                    created_at: "2024-11-01 10:35:00"
                    updated_at: "2024-11-01 10:35:00"
                  - id: "2c2f0bac-3e80-4504-82e5-6b41d17cfbd3"
                    type: "LowBalance"
                    data:
                      balance: 45.20
                      threshold: 100.00
                    read_at: null
                    created_at: "2024-11-01 11:00:00"
                    updated_at: "2024-11-01 11:00:00"
                unread: 2
        '401': { $ref: '#/components/responses/401' }
        '429': { $ref: '#/components/responses/429' }

  /notifications/{id}:
    servers:
      - url: https://autocall.kz/api/v1
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: string }
        description: ID уведомления (UUID)
    put:
      summary: Отметить уведомление прочитанным
      operationId: put-notifications-id
      tags: [Уведомления]
      description: Помечает уведомление как прочитанное.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '200':
          description: ID и дата прочтения уведомления.
          content:
            application/json:
              schema:
                type: object
                required: [id, read_at]
                properties:
                  id: { type: string }
                  read_at: { type: string }
              example:
                id: "2c2f0bac-3e80-4504-82e5-6b41d17cfbd3"
                read_at: "2024-11-01 12:00:00"
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
    delete:
      summary: Удалить уведомление
      operationId: delete-notifications-id
      tags: [Уведомления]
      description: Удаляет уведомление.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      responses:
        '204':
          description: Уведомление удалено.
        '401': { $ref: '#/components/responses/401' }
        '404': { $ref: '#/components/responses/404' }
        '429': { $ref: '#/components/responses/429' }
        '500': { $ref: '#/components/responses/500' }

  # ─────────────────────────────────────────
  # FLASH CALLS
  # ─────────────────────────────────────────

  /flash-calls:
    servers:
      - url: https://autocall.kz/api/v1
    post:
      summary: Отправить Flash Call
      operationId: post-flash-calls
      tags: [Flash Calls]
      description: |
        Совершает Flash Call на указанный номер. Последние N цифр номера звонящего — это код подтверждения.
        Звонок автоматически сбрасывается, биллинг на стороне абонента не возникает.

        **Авто-код** (`digits`): сервис сам генерирует случайный код. Работает всегда.
        **Свой код** (`code`): вы передаёте нужный код. Может вернуть 422 если номер с нужными последними цифрами недоступен.

        > Рекомендуется использовать Авто-код для гарантированной доставки.

        **Rate limit:** не более 3 попыток на один номер в течение 5 минут.
      parameters:
        - $ref: '#/components/parameters/ContentType'
        - $ref: '#/components/parameters/Accept'
      requestBody:
        required: true
        description: |
          Для **Авто-кода** отправьте `number` + `digits`.
          Для **Своего кода** отправьте `number` + `code`.
        content:
          application/json:
            schema:
              type: object
              required: [number]
              properties:
                number:
                  type: string
                  minLength: 12
                  maxLength: 12
                  pattern: '^\+7(700|701|702|705|706|707|708|747|771|775|776|777|778|710|711|712|713|714|715|716|717|718|721|722|723|724|725|726|727|728|729|736)\d{7}$'
                  example: "+77000000000"
                  description: Номер получателя в формате +7XXXXXXXXXX
                digits:
                  type: integer
                  minimum: 3
                  maximum: 6
                  description: |
                    **Авто-код**: количество цифр кода (3–6).
                    Обязательно если `code` не передан.
                code:
                  type: string
                  pattern: '^\d{3,6}$'
                  minLength: 3
                  maxLength: 6
                  example: "1234"
                  description: |
                    **Свой код**: числовой код от 3 до 6 цифр.
                    Обязательно если `digits` не передан.
            examples:
              Авто-код из 4 цифр:
                value:
                  number: "+77010000001"
                  digits: 4
              Свой код:
                value:
                  number: "+77010000001"
                  code: "7823"
      responses:
        '201':
          description: Flash Call отправлен. Код подтверждения — последние N цифр поля `code`.
          content:
            application/json:
              schema:
                type: object
                required: [id, code, number]
                properties:
                  id:
                    type: integer
                    description: ID Flash Call записи
                  code:
                    type: string
                    description: Код подтверждения (последние N цифр номера звонящего)
                  number:
                    type: string
                    description: Номер получателя
              example:
                id: 1023
                code: "7482"
                number: "+77010000001"
        '401': { $ref: '#/components/responses/401' }
        '402':
          description: Недостаточно средств на балансе.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorMessage' }
        '422':
          description: |
            Ошибка валидации: неверно заполнены поля, либо (для режима Свой код) номер с нужными цифрами недоступен.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ValidationError' }
        '429':
          description: Превышен rate limit — не более 3 попыток на один номер в течение 5 минут.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorMessage' }

# ─────────────────────────────────────────
# КОМПОНЕНТЫ
# ─────────────────────────────────────────

components:

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Заголовок: Authorization: Bearer ВАШ_API_КЛЮЧ'

  parameters:
    ContentType:
      name: Content-Type
      in: header
      required: true
      schema:
        type: string
        default: application/json
      description: Тип содержимого запроса
    Accept:
      name: Accept
      in: header
      required: true
      schema:
        type: string
        default: application/json
      description: Тип содержимого ответа
    DateFrom:
      name: date_from
      in: query
      schema:
        type: string
        example: "2024-01-01"
      description: "Фильтр: с какой даты (формат Y-m-d)"
    DateTo:
      name: date_to
      in: query
      schema:
        type: string
        example: "2024-12-31"
      description: "Фильтр: по какую дату (формат Y-m-d)"
    Order:
      name: order
      in: query
      schema:
        type: string
        enum: [asc, desc]
      description: Сортировка по дате создания
    ClientIdFilter:
      name: client_id
      in: query
      schema:
        type: string
      description: Фильтр по client_id из Вашей системы
    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
      description: Номер страницы

  responses:
    '401':
      description: Не передан или недействителен API ключ.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorMessage' }
          example:
            message: "Unauthenticated."
    '404':
      description: Ресурс не найден по указанному ID.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorMessage' }
          example:
            message: "Not found."
    '422':
      description: Ошибка валидации — данные запроса не прошли проверку.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ValidationError' }
          example:
            message: "The given data was invalid."
            errors:
              name: ["Поле name обязательно."]
    '429':
      description: Превышен лимит запросов. По умолчанию — 300 запросов в минуту.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorMessage' }
          example:
            message: "Too Many Attempts."
    '500':
      description: Внутренняя ошибка сервера.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorMessage' }
          example:
            message: "Server Error."

  schemas:

    ErrorMessage:
      type: object
      required: [message]
      properties:
        message:
          type: string
          description: Описание ошибки

    ValidationError:
      type: object
      required: [message, errors]
      properties:
        message:
          type: string
          description: Общее описание ошибки
        errors:
          type: object
          description: Объект с ошибками по каждому полю
          additionalProperties:
            type: array
            items:
              type: string

    Tariff:
      type: object
      properties:
        name:
          type: string
          enum: [BRONZE, SILVER, GOLD]
          description: Название тарифного плана
        threshold:
          type: integer
          description: Минимальный баланс для активации тарифа (тенге)
        call_price:
          type: number
          description: Цена за секунду исходящего звонка (тенге)
        sms_price:
          type: number
          description: Цена за одно SMS-сообщение (тенге)
        synthesis_price:
          type: number
          description: Цена за секунду синтеза речи (тенге)
        speech_price:
          type: number
          description: Цена за секунду распознавания речи (тенге)
        recording_price:
          type: number
          description: Цена за секунду записи звонка (тенге)

    Pagination:
      type: object
      description: Стандартный объект постраничного вывода Laravel
      required: [current_page, data, first_page_url, from, last_page, last_page_url, next_page_url, path, per_page, prev_page_url, to, total]
      properties:
        current_page: { type: integer }
        data:
          type: array
          items: {}
        first_page_url: { type: string }
        from: { type: integer }
        last_page: { type: integer }
        last_page_url: { type: string }
        next_page_url: { type: [string, "null"] }
        path: { type: string }
        per_page: { type: integer }
        prev_page_url: { type: [string, "null"] }
        to: { type: integer }
        total: { type: integer }

    DayActivity:
      type: object
      properties:
        all: { type: integer, description: Всего звонков }
        success: { type: integer, description: Успешно дозвонились }
        recalled: { type: integer, description: Перезвонили сами }
        no_answer: { type: integer, description: Нет ответа }
        busy: { type: integer, description: Занято }
        error: { type: integer, description: Не существует }
        spam: { type: integer, description: Пожаловались на спам }

    Contact:
      type: object
      required: [id, number, extra, created_at, updated_at]
      properties:
        id: { type: integer }
        list_id: { type: integer }
        number: { type: string }
        extra: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    ContactList:
      type: object
      required: [id, client_id, name, importing, nonexistent, created_at, updated_at, contacts_count, used]
      properties:
        id: { type: integer }
        client_id: { type: [string, "null"] }
        name: { type: string }
        importing: { type: boolean, description: Идёт ли импорт прямо сейчас }
        nonexistent:
          type: [array, "null"]
          description: Массив ID контактов с несуществующими номерами
          items: { type: integer }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        contacts_count: { type: integer }
        used: { type: integer, description: Сколько раз список использовался }

    Sound:
      type: object
      required: [id, client_id, name, duration, confirmed, comment, confirmed_at, created_at, updated_at, playback_url, used]
      properties:
        id: { type: integer }
        client_id: { type: [string, "null"] }
        name: { type: string }
        duration: { type: integer, description: Длительность в секундах }
        confirmed: { type: boolean, description: Одобрена ли модераторами }
        comment: { type: [string, "null"], description: Комментарий модератора }
        confirmed_at: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        playback_url: { type: string, description: URL для прослушивания/скачивания }
        used: { type: integer, description: Количество использований в обзвонах }

    Scenario:
      type: object
      required: [id, client_id, name, max_cost, comment, created_at, updated_at, used]
      properties:
        id: { type: integer }
        client_id: { type: [string, "null"] }
        name: { type: string }
        max_cost: { type: [string, number], description: Максимальная стоимость на 1 звонок }
        comment: { type: [string, "null"] }
        confirmed_at: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        used: { type: integer }

    ScenarioDetail:
      allOf:
        - $ref: '#/components/schemas/Scenario'
        - type: object
          properties:
            data:
              type: object
              description: Структура сценария

    AutocallSummary:
      type: object
      properties:
        id: { type: integer }
        client_id: { type: [string, "null"] }
        name: { type: string }
        date_from: { type: string }
        time_from: { type: string }
        time_to: { type: string }
        max_tries: { type: integer }
        tries_interval: { type: integer }
        type:
          type: string
          enum: [regular, interactive]
        status:
          type: string
          enum: [running, completed, awaiting, moderation, denied]
        max_cost: { type: [number, string] }
        final_cost: { type: [number, string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    AutocallCreate:
      type: object
      required: [name, type, list_id]
      properties:
        name: { type: string, minLength: 1, maxLength: 40 }
        time_from: { type: string, default: "12:00:00", minLength: 8, maxLength: 8 }
        time_to: { type: string, default: "18:00:00", minLength: 8, maxLength: 8 }
        type:
          type: string
          enum: [regular, interactive]
          default: regular
        audio_id:
          type: array
          description: "Обязательно если type=regular. ID аудиозаписей или объекты для создания новых."
          items:
            oneOf:
              - type: integer
              - type: object
                required: [type, data]
                properties:
                  type: { type: string, enum: [synthesis, upload] }
                  data:
                    type: object
                    properties:
                      text: { type: string, minLength: 1, maxLength: 400 }
                      voice:
                        type: string
                        enum: [ru-RU-Wavenet-A, ru-RU-Wavenet-B, ru-RU-Wavenet-C, ru-RU-Wavenet-D, alyss, jane, oksana, omazh, zahar, ermil, filipp, alena]
                      speed: { type: number, default: 1, minimum: 0.25, maximum: 4 }
                      pitch: { type: number, default: 0, minimum: -20, maximum: 20 }
                      file: { type: string, description: Base64 строка файла mp3/wav/ogg }
        scenario_id: { type: integer, description: "Обязательно если type=interactive." }
        list_id:
          type: array
          minItems: 1
          maxItems: 5000
          items:
            oneOf:
              - type: integer
              - type: object
                required: [number]
                properties:
                  number: { type: string, minLength: 12, maxLength: 12 }
                  extra: { type: string, maxLength: 30 }
        max_tries: { type: integer, default: 1, enum: [1, 2, 3, 4] }
        tries_interval: { type: integer, default: 60, enum: [15, 30, 60, 120] }
        date_from: { type: string, minLength: 10, maxLength: 10 }
        sms: { type: boolean, default: false, description: Отправить SMS при успешном дозвоне? }
        text: { type: string, description: "Обязательно если sms=true." }
        sms_unanswered: { type: boolean, default: false, description: Отправить SMS если не дозвонились? }
        text_unanswered: { type: string, description: "Обязательно если sms_unanswered=true." }
        lifetime: { type: boolean, default: false, description: Ждать перезвонивших абонентов }
        record: { type: boolean, default: false, description: Записывать разговоры? }
        client_id: { type: string, maxLength: 60 }
        callback_url: { type: string, maxLength: 256 }

    AutocallDetail:
      allOf:
        - $ref: '#/components/schemas/AutocallSummary'
        - type: object
          properties:
            sms: { type: boolean }
            sms_unanswered: { type: boolean }
            text: { type: [string, "null"] }
            text_unanswered: { type: [string, "null"] }
            audio_id: { type: [array, "null"], items: { type: [number, string] } }
            scenario_id: { type: [integer, "null"] }
            processed: { type: integer }
            speeches: { type: integer }
            lifetime: { type: boolean }
            record: { type: boolean }
            comment: { type: [string, "null"] }
            callback_url: { type: [string, "null"] }
            finished_at: { type: [string, "null"] }
            calls_count: { type: integer }
            processed_percentage: { type: number }
            counters:
              type: object
              properties:
                success_percentage: { type: number }
                success: { type: integer }
                recalled: { type: integer }
                no_answer: { type: integer }
                busy: { type: integer }
                error: { type: integer }
                spam: { type: integer }
                tries: { type: integer }
                duration: { type: integer, description: Всего секунд разговора (биллинг) }
                total: { type: integer }
            calls:
              allOf:
                - $ref: '#/components/schemas/Pagination'
                - type: object
                  properties:
                    data:
                      type: array
                      items: { $ref: '#/components/schemas/Call' }

    Call:
      type: object
      required: [id, autocall_id, number, extra, lock, tries, date_tries, status_tries, last_try_at, duration, r_duration, status, created_at, updated_at]
      properties:
        id: { type: integer }
        autocall_id: { type: integer }
        number: { type: string }
        extra: { type: [string, "null"] }
        lock: { type: boolean, description: Идёт ли звонок прямо сейчас }
        tries: { type: integer }
        date_tries: { type: [array, "null"], items: { type: string } }
        status_tries: { type: [array, "null"], items: { type: string } }
        last_try_at: { type: [string, "null"] }
        duration: { type: integer, description: Длительность исходящего разговора в секундах (биллинг) }
        r_duration: { type: integer, description: Длительность входящего разговора (бесплатно) }
        status:
          type: string
          enum: [in_queue, success, recalled, no_answer, busy, error, spam]
        data: { type: [object, "null"], description: Результаты прохождения сценария }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    BulkSummary:
      type: object
      properties:
        id: { type: integer }
        client_id: { type: [string, "null"] }
        name: { type: string }
        status:
          type: string
          enum: [running, completed, awaiting, moderation, denied]
        date_from: { type: string }
        time_from: { type: string }
        text: { type: string }
        segments: { type: integer, description: Количество SMS сегментов }
        recipients: { type: integer }
        list_id: { type: array, items: { type: integer } }
        max_cost: { type: [string, number] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    BulkDetail:
      allOf:
        - $ref: '#/components/schemas/BulkSummary'
        - type: object
          properties:
            final_cost: { type: [string, number, "null"] }
            comment: { type: [string, "null"] }
            callback_url: { type: [string, "null"] }
            messages:
              allOf:
                - $ref: '#/components/schemas/Pagination'
                - type: object
                  properties:
                    data:
                      type: array
                      items: { $ref: '#/components/schemas/Message' }

    Message:
      type: object
      required: [id, bulk_id, number, extra, status, provider, init_time, status_time, created_at, updated_at]
      properties:
        id: { type: integer }
        bulk_id: { type: integer }
        number: { type: string }
        extra: { type: [string, "null"] }
        status:
          type: string
          enum: [queued, disabled, new, sent, delivered, pending, failed, expired, undeliverable, rejected]
          description: |
            | Статус | Значение | Окончательный |
            |--------|----------|---------------|
            | `queued` | в очереди | нет |
            | `new` | создано | нет |
            | `sent` | отправлено | нет |
            | `pending` | ждём отчёта | нет |
            | `delivered` | доставлено | да |
            | `failed` | ошибка | да |
            | `expired` | истекло время ожидания | да |
            | `undeliverable` | не доставлено | да |
            | `rejected` | отклонено оператором | да |
            | `disabled` | удалено | да |
        provider: { type: string, description: Мобильный оператор получателя }
        init_time: { type: [string, "null"], description: Дата отправки }
        status_time: { type: [string, "null"], description: Дата последнего изменения статуса }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    ConfirmedNumber:
      type: object
      required: [id, number, verified_at, created_at, updated_at]
      properties:
        id: { type: integer }
        number: { type: string, description: Номер в формате +7XXXXXXXXXX }
        verified_at:
          type: [string, "null"]
          description: Дата подтверждения. Неподтверждённые удаляются через 10 минут.
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    AccountSettings:
      type: object
      properties:
        name: { type: string }
        phone: { type: string, description: Номер телефона без +7 }
        sms_verified_at: { type: string }
        moderation: { type: boolean, description: Включена ли модерация }
        time_from: { type: string, description: С какого времени разрешены рассылки/обзвоны }
        time_to: { type: string, description: До какого времени разрешены рассылки/обзвоны }
        price: { type: string, description: Тариф — стоимость за секунду обзвона (₸) }
        sms_price: { type: string, description: Тариф — стоимость одного SMS (₸) }
        sip_price: { type: string, description: Тариф — стоимость за секунду через АТС (₸) }
        speech_price: { type: string, description: Тариф — стоимость распознавания речи за сеанс (₸) }
        synthesis_price: { type: string, description: Тариф — стоимость синтеза речи за сеанс (₸) }
        recording_price: { type: string, description: Тариф — стоимость записи разговора (₸) }
        recalled: { type: integer, description: Часов отслеживания перезвонивших абонентов }
        subscribe: { type: boolean, description: Подписка на новости через звонки }
        notifications_subscribe: { type: boolean, description: Дублирование уведомлений через SMS }
        visit_card_audio: { type: [integer, "null"] }
        visit_card_text: { type: [string, "null"] }
        confirmed_numbers:
          type: array
          items: { $ref: '#/components/schemas/ConfirmedNumber' }
        statistics:
          type: object
          properties:
            duration: { type: integer, description: Всего секунд разговоров }
            calls: { type: [number, string], description: Всего звонков }
            confirmed_numbers: { type: integer }
            spam_reports: { type: integer }
            lists: { type: integer }
            contacts: { type: integer }
            autocalls: { type: integer }
            bulks: { type: integer }
            sms: { type: integer }

    Transaction:
      type: object
      properties:
        id: { type: integer }
        type:
          type: string
          enum: [withdraw, deposit]
          description: withdraw — расход, deposit — приход
        confirmed: { type: boolean }
        meta:
          type: object
          properties:
            description: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        total:
          type: number
          description: Сумма (отрицательная для withdraw, положительная для deposit)

    Notification:
      type: object
      required: [id, type, read_at, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        type:
          type: string
          enum:
            - SoundConfirmed
            - SoundCommented
            - SMSBulkConfirmed
            - SMSBulkCommented
            - SMSBulkFinished
            - ScenarioConfirmed
            - ScenarioCommented
            - AutocallConfirmed
            - AutocallCommented
            - AutocallFinished
            - ImportComplete
            - ImportFailure
            - NonexistentContactsDetected
            - ExportIsReady
            - ExportFailure
            - RefreshBalance
            - LowBalance
        data:
          type: object
          description: Содержимое уведомления (структура зависит от типа)
          properties:
            id: { type: integer }
            name: { type: string }
            type:
              type: string
              enum: [autocall, bulk]
            action:
              type: string
              enum: [deposit, withdraw]
            amount: { type: string }
            balance: { type: number, description: Текущий баланс (для LowBalance) }
            threshold: { type: number, description: Порог баланса (для LowBalance) }
        read_at: { type: [string, "null"], description: Дата прочтения (null если не прочитано) }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
