openapi: "3.1.0"
info:
  title: MiniApp — Outgoing Hooks
  description: >
    Outgoing hooks from our application to the external one (Acloud → MiniApp).
    Sent as POST requests to the webhook_url configured for the bot.

    The bot's `webhook_url` field can only be edited via the generic API from the external
    application — the Contactcenter UI does not show this field to prevent accidental
    editing by an operator.
  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: Any 2xx response is considered successful. Non-2xx status codes are recorded as a send error.

webhooks:

  message.new.personal:
    post:
      summary: Manager message send
      description: >
        The URL is set in the bot settings (webhook_url field).
        Our system sends a POST with a JSON body when an event occurs.
      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: Manager message send
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9001"
                    external_chat_id: chat_789
                    text: Good day! Your order #42 has been shipped.
                    timestamp: 1710752400
                    event_id: '550e8400-e29b-41d4-a716-446655440000:79000'
                    attachments: []
              message_new_with_attachments:
                summary: Message send with attachments
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9002"
                    external_chat_id: chat_789
                    text: Attaching the invoice
                    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: Message accepted successfully

  chat.init.personal:
    post:
      summary: New chat initialization
      description: >
        Sent when a manager first contacts a user and the chat does not yet exist.
        After receiving this hook, the external application must send the incoming event
        message.new.personal with a message confirming the chat was created.
      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: Chat initialization with user
                value:
                  method: chat.init.personal
                  payload:
                    channel_id: bot_token_abc123
                    to:
                      email: john@example.com
                      name: John Smith
                      phone: "+79001234567"
                      other: null
                    message:
                      text: Good day! How can I help?
                    event_id: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'
      responses:
        "200":
          description: Chat initialization request accepted

  bot.deactivated:
    post:
      summary: Bot deactivated
      description: >
        Sent when the MiniApp bot is deactivated on the Acloud side. The reason is specified
        in the reason field: manual (manual deactivation), auto_unreachable (automatic due to
        unreachability), subscription_expired (subscription expired).
        Fire-and-forget: the action has already been performed on the Acloud side.
      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: Bot deactivated manually
                value:
                  method: bot.deactivated
                  payload:
                    channel_id: bot_token_abc123
                    reason: manual
      responses:
        "200":
          description: Notification accepted

  bot.deleted:
    post:
      summary: Bot deleted
      description: >
        Sent when the MiniApp bot is deleted on the Acloud side.
        Fire-and-forget: the action has already been performed on the Acloud side.
      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: Bot deleted
                value:
                  method: bot.deleted
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Notification accepted

  bot.activated:
    post:
      summary: Bot activated
      description: >
        Sent **only** when the bot transitions from inactive to active state
        (`was_active=false → is_active=true`) for an **existing** bot.
        When a new active bot is created, the hook is **NOT sent** —
        the external application does not yet know about such a bot.
        Fire-and-forget: the action has already been performed on the Acloud side.
      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: Bot activated
                value:
                  method: bot.activated
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Notification accepted

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: Event type
        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: Sending a manager message to a user via the external application
      required:
        - channel_id
        - inner_message_id
        - external_chat_id
        - text
        - timestamp
        - event_id
      properties:
        channel_id:
          type: string
          description: Bot token (bot_token) — integration ID on the external application side
          example: bot_token_abc123
        inner_message_id:
          type: string
          description: >
            Message ID in our system.
            The external application must return it in the message.completed.personal event.
          example: "9001"
        external_chat_id:
          type: string
          description: Chat ID in the external system to which the message is sent
          example: chat_789
        text:
          type: string
          description: Message text
          example: Good day! Your order #42 has been shipped.
        timestamp:
          type: integer
          description: Unix timestamp of send time
          example: 1710752400
        event_id:
          type: string
          description: >
            Opaque event identifier (up to 60 characters). The external application must store
            this value as-is and return it unchanged in the event_id field of the error event if
            it could not process the hook. The field structure is an internal Acloud detail and
            may change without notice; do not parse or validate the content.
          example: '550e8400-e29b-41d4-a716-446655440000:79000'
        attachments:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/OutgoingAttachmentObject'
          description: >
            Files attached to the message. The external application must download each file
            by url and deliver it to the recipient.

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

    ChatInitPayload:
      type: object
      description: >
        Initialization of a new personal chat with a user.
        Used on first contact when external_chat_id is not yet known.
      required:
        - channel_id
        - to
        - message
        - event_id
      properties:
        channel_id:
          type: string
          description: Bot token (bot_token) — integration ID on the external application side
          example: bot_token_abc123
        to:
          $ref: '#/components/schemas/To'
        message:
          type: object
          description: First message in the chat being created
          required:
            - text
          properties:
            text:
              type: string
              description: Text of the first message
              example: Good day! How can I help?
        event_id:
          type: string
          description: >
            Opaque event identifier (up to 60 characters). The external application must store
            this value as-is and return it unchanged in the event_id field of the error event if
            it could not process the hook. The field structure is an internal Acloud detail and
            may change without notice; do not parse or validate the content.
          example: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'

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

    BotDeactivatedPayload:
      type: object
      description: >
        Bot deactivation notification. Fire-and-forget — no correlation with error is provided.
      required:
        - channel_id
        - reason
      properties:
        channel_id:
          type: string
          description: Bot token (bot_token) — integration ID on the external application side
          example: bot_token_abc123
        reason:
          type: string
          description: >
            Deactivation reason: manual — manual deactivation by user,
            auto_unreachable — automatic due to external application unreachability,
            subscription_expired — account subscription expired
          enum:
            - manual
            - auto_unreachable
            - subscription_expired
          example: manual

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

    BotDeletedPayload:
      type: object
      description: >
        Bot deletion notification. Fire-and-forget — no correlation with error is provided.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Bot token (bot_token) — integration ID on the external application side
          example: bot_token_abc123

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

    BotActivatedPayload:
      type: object
      description: >
        Notification of reactivation of a previously deactivated bot.
        Fire-and-forget — no correlation with error is provided.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Bot token (bot_token) — integration ID on the external application side
          example: bot_token_abc123

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

    To:
      type: object
      description: Recipient data for chat initialization
      properties:
        email:
          type: ["string", "null"]
          description: User email
        name:
          type: ["string", "null"]
          description: User name
        phone:
          type: ["string", "null"]
          description: User phone number
        other:
          type: ["string", "null"]
          description: Additional user identifier in the external system

    OutgoingAttachmentObject:
      type: object
      description: >
        Outgoing attachment metadata. The external application must download the file
        from the provided url and deliver it to the recipient.
      required:
        - id
        - type
        - url
        - filename
      properties:
        id:
          type: string
          description: File ID in the Acloud system.
        type:
          type: string
          enum: [photo, video, audio, file]
          description: Attachment type determined by the file mime type.
        url:
          type: string
          description: >
            One-time public link for downloading the file from Acloud.
            Valid for up to 7 days (for iOS recipients) or without expiration (by UUID).
        filename:
          type: string
          description: Filename with extension.
        size:
          type: integer
          description: File size in bytes.
        metadata:
          type: ["object", "null"]
          description: Arbitrary metadata (optional).
