openapi: "3.1.0"
info:
  title: MiniApp — Outgoing Hooks
  description: >
    Исходящие хуки от нашего приложения во внешнее (Acloud → MiniApp).
    Отправляются POST-запросом на webhook_url, настроенный для бота.

    Поле `webhook_url` бота редактируется только через generic API
    из внешнего приложения — UI Contactcenter поле не показывает,
    чтобы исключить ошибочную правку оператором.
  version: "2.0.0"

x-retry-policy:
  max_retries: 3
  strategy: exponential backoff
  delays_ms: [500, 1000, 2000]

x-expected-response:
  success_codes: [200, 201, 202]
  description: Любой 2xx-ответ считается успешным. При не-2xx коде фиксируется ошибка отправки.

webhooks:

  message.new.personal:
    post:
      summary: Отправка сообщения менеджера
      description: >
        URL задаётся в настройках бота (поле webhook_url).
        Наша система отправляет POST с JSON-телом при возникновении события.
      x-headers:
        Content-Type: application/json
        Accept: application/json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - message.new.personal
                payload:
                  $ref: '#/components/schemas/MessageNewPayload'
            examples:
              message_new:
                summary: Отправка сообщения менеджера
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9001"
                    external_chat_id: chat_789
                    text: Добрый день! Ваш заказ №42 отправлен.
                    timestamp: 1710752400
                    event_id: '550e8400-e29b-41d4-a716-446655440000:79000'
                    attachments: []
              message_new_with_attachments:
                summary: Отправка сообщения с приложенными файлами
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9002"
                    external_chat_id: chat_789
                    text: Прилагаю счёт
                    timestamp: 1710752500
                    event_id: '6ba7b810-9dad-11d1-80b4-00c04fd430c8:79000'
                    attachments:
                      - id: "555"
                        type: file
                        url: https://crm.example.com/files/555/download
                        filename: invoice.pdf
                        size: 102400
      responses:
        "200":
          description: Сообщение успешно принято

  chat.init.personal:
    post:
      summary: Инициализация нового чата
      description: >
        Отправляется при первом обращении менеджера к пользователю, когда чат ещё не существует.
        После получения этого хука внешнее приложение должно прислать входящее событие
        message.new.personal с сообщением, подтверждающим создание чата.
      x-headers:
        Content-Type: application/json
        Accept: application/json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - chat.init.personal
                payload:
                  $ref: '#/components/schemas/ChatInitPayload'
            examples:
              chat_init:
                summary: Инициализация чата с пользователем
                value:
                  method: chat.init.personal
                  payload:
                    channel_id: bot_token_abc123
                    to:
                      email: ivan@example.com
                      name: Иван Иванов
                      phone: "+79001234567"
                      other: null
                    message:
                      text: Добрый день! Чем могу помочь?
                    event_id: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'
      responses:
        "200":
          description: Запрос на инициализацию чата принят

  bot.deactivated:
    post:
      summary: Бот деактивирован
      description: >
        Отправляется когда бот MiniApp деактивирован на стороне Acloud.
        Причина указывается в поле reason: manual (ручная), auto_unreachable
        (автоматическая из-за недоступности), subscription_expired (истечение подписки).
        Fire-and-forget: действие уже совершено на стороне Acloud.
      x-headers:
        Content-Type: application/json
        Accept: application/json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - bot.deactivated
                payload:
                  $ref: '#/components/schemas/BotDeactivatedPayload'
            examples:
              bot_deactivated_manual:
                summary: Бот деактивирован вручную
                value:
                  method: bot.deactivated
                  payload:
                    channel_id: bot_token_abc123
                    reason: manual
      responses:
        "200":
          description: Уведомление принято

  bot.deleted:
    post:
      summary: Бот удалён
      description: >
        Отправляется когда бот MiniApp удалён на стороне Acloud.
        Fire-and-forget: действие уже совершено на стороне Acloud.
      x-headers:
        Content-Type: application/json
        Accept: application/json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - bot.deleted
                payload:
                  $ref: '#/components/schemas/BotDeletedPayload'
            examples:
              bot_deleted:
                summary: Бот удалён
                value:
                  method: bot.deleted
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Уведомление принято

  bot.activated:
    post:
      summary: Бот активирован
      description: >
        Отправляется **только** при переходе бота из неактивного состояния в активное
        (`was_active=false → is_active=true`) для **существующего** бота.
        При создании нового активного бота хук **НЕ отправляется** —
        внешнее приложение ещё не знает о существовании такого бота.
        Fire-and-forget: действие уже совершено на стороне Acloud.
      x-headers:
        Content-Type: application/json
        Accept: application/json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - bot.activated
                payload:
                  $ref: '#/components/schemas/BotActivatedPayload'
            examples:
              bot_activated:
                summary: Бот активирован
                value:
                  method: bot.activated
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Уведомление принято

components:
  schemas:

    OutgoingEvent:
      type: object
      required:
        - method
        - payload
      properties:
        method:
          type: string
          enum:
            - message.new.personal
            - chat.init.personal
            - bot.deactivated
            - bot.deleted
            - bot.activated
          description: Тип события
        payload:
          oneOf:
            - $ref: '#/components/schemas/MessageNewPayload'
            - $ref: '#/components/schemas/ChatInitPayload'
            - $ref: '#/components/schemas/BotDeactivatedPayload'
            - $ref: '#/components/schemas/BotDeletedPayload'
            - $ref: '#/components/schemas/BotActivatedPayload'
          discriminator:
            propertyName: method

    # ─── message.new.personal ─────────────────────────────────────────────────

    MessageNewPayload:
      type: object
      description: Отправка сообщения менеджера пользователю во внешнее приложение
      required:
        - channel_id
        - inner_message_id
        - external_chat_id
        - text
        - timestamp
        - event_id
      properties:
        channel_id:
          type: string
          description: Токен бота (bot_token) — ID интеграции на стороне внешнего приложения
          example: bot_token_abc123
        inner_message_id:
          type: string
          description: >
            ID сообщения в нашей системе.
            Внешнее приложение должно вернуть его обратно в событии message.completed.personal
          example: "9001"
        external_chat_id:
          type: string
          description: ID чата во внешней системе, в который отправляется сообщение
          example: chat_789
        text:
          type: string
          description: Текст сообщения
          example: Добрый день! Ваш заказ №42 отправлен.
        timestamp:
          type: integer
          description: Unix timestamp времени отправки
          example: 1710752400
        event_id:
          type: string
          description: >
            Непрозрачный идентификатор события (до 60 символов).
            Внешнее приложение должно сохранить это значение как есть и вернуть его
            без изменений в поле event_id события error, если не смогло обработать хук.
            Структура поля — внутренняя деталь Acloud и может меняться без предупреждения;
            не парсите и не валидируйте содержимое.
          example: '550e8400-e29b-41d4-a716-446655440000:79000'
        attachments:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/OutgoingAttachmentObject'
          description: >
            Файлы, приложенные к сообщению. Внешнее приложение должно скачать
            каждый файл по url и доставить его получателю.

    # ─── chat.init.personal ───────────────────────────────────────────────────

    ChatInitPayload:
      type: object
      description: >
        Инициализация нового персонального чата с пользователем.
        Используется при первом обращении, когда external_chat_id ещё неизвестен.
      required:
        - channel_id
        - to
        - message
        - event_id
      properties:
        channel_id:
          type: string
          description: Токен бота (bot_token) — ID интеграции на стороне внешнего приложения
          example: bot_token_abc123
        to:
          $ref: '#/components/schemas/To'
        message:
          type: object
          description: Первое сообщение в создаваемом чате
          required:
            - text
          properties:
            text:
              type: string
              description: Текст первого сообщения
              example: Добрый день! Чем могу помочь?
        event_id:
          type: string
          description: >
            Непрозрачный идентификатор события (до 60 символов).
            Внешнее приложение должно сохранить это значение как есть и вернуть его
            без изменений в поле event_id события error, если не смогло обработать хук.
            Структура поля — внутренняя деталь Acloud и может меняться без предупреждения;
            не парсите и не валидируйте содержимое.
          example: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'

    # ─── bot.deactivated ───────────────────────────────────────────────────

    BotDeactivatedPayload:
      type: object
      description: >
        Уведомление о деактивации бота. Fire-and-forget — корреляция с error не предусмотрена.
      required:
        - channel_id
        - reason
      properties:
        channel_id:
          type: string
          description: Токен бота (bot_token) — ID интеграции на стороне внешнего приложения
          example: bot_token_abc123
        reason:
          type: string
          description: >
            Причина деактивации: manual — ручная деактивация пользователем,
            auto_unreachable — автоматическая из-за недоступности внешнего приложения,
            subscription_expired — истечение подписки аккаунта
          enum:
            - manual
            - auto_unreachable
            - subscription_expired
          example: manual

    # ─── bot.deleted ─────────────────────────────────────────────────────

    BotDeletedPayload:
      type: object
      description: >
        Уведомление об удалении бота. Fire-and-forget — корреляция с error не предусмотрена.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Токен бота (bot_token) — ID интеграции на стороне внешнего приложения
          example: bot_token_abc123

    # ─── bot.activated ───────────────────────────────────────────────────

    BotActivatedPayload:
      type: object
      description: >
        Уведомление об активации ранее деактивированного бота. Fire-and-forget —
        корреляция с error не предусмотрена.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Токен бота (bot_token) — ID интеграции на стороне внешнего приложения
          example: bot_token_abc123

    # ─── Shared types ─────────────────────────────────────────────────────────

    To:
      type: object
      description: Данные получателя для инициализации чата
      properties:
        email:
          type: ["string", "null"]
          description: Email пользователя
        name:
          type: ["string", "null"]
          description: Имя пользователя
        phone:
          type: ["string", "null"]
          description: Номер телефона пользователя
        other:
          type: ["string", "null"]
          description: Дополнительный идентификатор пользователя во внешней системе

    OutgoingAttachmentObject:
      type: object
      description: >
        Метаданные исходящего вложения. Внешнее приложение должно скачать файл
        по переданному url и доставить его получателю.
      required:
        - id
        - type
        - url
        - filename
      properties:
        id:
          type: string
          description: ID файла в системе Acloud.
        type:
          type: string
          enum: [photo, video, audio, file]
          description: Тип вложения, определённый по mime файла.
        url:
          type: string
          description: >
            Одноразовая публичная ссылка для скачивания файла из Acloud.
            Срок жизни до 7 дней (для iOS-получателей) или без ограничения (по UUID).
        filename:
          type: string
          description: Имя файла с расширением.
        size:
          type: integer
          description: Размер файла в байтах.
        metadata:
          type: ["object", "null"]
          description: Произвольные метаданные (опционально).
