openapi: "3.1.0"
info:
  title: MiniApp — Hooks Salientes
  description: >
    Hooks salientes de nuestra aplicacion hacia la externa (Acloud → MiniApp).
    Se envian como solicitudes POST al webhook_url configurado para el bot.

    El campo `webhook_url` del bot solo puede editarse a traves del API generico desde el
    aplicativo externo — la UI de Contactcenter no muestra este campo para evitar ediciones
    accidentales por parte del operador.
  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: Cualquier respuesta 2xx se considera exitosa. Los codigos que no sean 2xx se registran como error de envio.

webhooks:

  message.new.personal:
    post:
      summary: Envio de mensaje del gestor
      description: >
        La URL se configura en los ajustes del bot (campo webhook_url).
        Nuestro sistema envia un POST con cuerpo JSON cuando ocurre un evento.
      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: Envio de mensaje del gestor
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9001"
                    external_chat_id: chat_789
                    text: Buenos dias! Su pedido #42 ha sido enviado.
                    timestamp: 1710752400
                    event_id: '550e8400-e29b-41d4-a716-446655440000:79000'
                    attachments: []
              message_new_with_attachments:
                summary: Envio de mensaje con archivos adjuntos
                value:
                  method: message.new.personal
                  payload:
                    channel_id: bot_token_abc123
                    inner_message_id: "9002"
                    external_chat_id: chat_789
                    text: Adjunto la factura
                    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: Mensaje aceptado correctamente

  chat.init.personal:
    post:
      summary: Inicializacion de nuevo chat
      description: >
        Enviado cuando un gestor contacta a un usuario por primera vez y el chat aun no existe.
        Tras recibir este hook, el aplicativo externo debe enviar el evento entrante
        message.new.personal con un mensaje que confirme la creacion del chat.
      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: Inicializacion de chat con usuario
                value:
                  method: chat.init.personal
                  payload:
                    channel_id: bot_token_abc123
                    to:
                      email: juan@example.com
                      name: Juan Garcia
                      phone: "+79001234567"
                      other: null
                    message:
                      text: Buenos dias! En que puedo ayudarle?
                    event_id: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'
      responses:
        "200":
          description: Solicitud de inicializacion de chat aceptada

  bot.deactivated:
    post:
      summary: Bot desactivado
      description: >
        Enviado cuando el bot MiniApp es desactivado en el lado de Acloud. La razon se indica
        en el campo reason: manual (desactivacion manual), auto_unreachable (automatica por
        inaccesibilidad), subscription_expired (vencimiento de suscripcion).
        Fire-and-forget: la accion ya se realizo en el lado de 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: Bot desactivado manualmente
                value:
                  method: bot.deactivated
                  payload:
                    channel_id: bot_token_abc123
                    reason: manual
      responses:
        "200":
          description: Notificacion aceptada

  bot.deleted:
    post:
      summary: Bot eliminado
      description: >
        Enviado cuando el bot MiniApp es eliminado en el lado de Acloud.
        Fire-and-forget: la accion ya se realizo en el lado de 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: Bot eliminado
                value:
                  method: bot.deleted
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Notificacion aceptada

  bot.activated:
    post:
      summary: Bot activado
      description: >
        Enviado **solo** cuando el bot pasa del estado inactivo al activo
        (`was_active=false → is_active=true`) para un bot **existente**.
        Al crear un nuevo bot activo, el hook **NO se envia** —
        el aplicativo externo aun no sabe de la existencia de dicho bot.
        Fire-and-forget: la accion ya se realizo en el lado de 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: Bot activado
                value:
                  method: bot.activated
                  payload:
                    channel_id: bot_token_abc123
      responses:
        "200":
          description: Notificacion aceptada

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: Tipo de evento
        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: Envio de un mensaje del gestor al usuario a traves del aplicativo externo
      required:
        - channel_id
        - inner_message_id
        - external_chat_id
        - text
        - timestamp
        - event_id
      properties:
        channel_id:
          type: string
          description: Token del bot (bot_token) — ID de integracion en el lado del aplicativo externo
          example: bot_token_abc123
        inner_message_id:
          type: string
          description: >
            ID del mensaje en nuestro sistema.
            El aplicativo externo debe devolverlo en el evento message.completed.personal.
          example: "9001"
        external_chat_id:
          type: string
          description: ID del chat en el sistema externo al que se envia el mensaje
          example: chat_789
        text:
          type: string
          description: Texto del mensaje
          example: Buenos dias! Su pedido #42 ha sido enviado.
        timestamp:
          type: integer
          description: Unix timestamp del momento de envio
          example: 1710752400
        event_id:
          type: string
          description: >
            Identificador opaco del evento (hasta 60 caracteres). El aplicativo externo debe
            guardar este valor tal cual y devolverlo sin cambios en el campo event_id del evento
            error si no pudo procesar el hook. La estructura del campo es un detalle interno de
            Acloud y puede cambiar sin previo aviso; no parsee ni valide el contenido.
          example: '550e8400-e29b-41d4-a716-446655440000:79000'
        attachments:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/OutgoingAttachmentObject'
          description: >
            Archivos adjuntos al mensaje. El aplicativo externo debe descargar cada archivo
            por url y entregarlo al destinatario.

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

    ChatInitPayload:
      type: object
      description: >
        Inicializacion de un nuevo chat personal con un usuario.
        Se usa en el primer contacto cuando external_chat_id aun no es conocido.
      required:
        - channel_id
        - to
        - message
        - event_id
      properties:
        channel_id:
          type: string
          description: Token del bot (bot_token) — ID de integracion en el lado del aplicativo externo
          example: bot_token_abc123
        to:
          $ref: '#/components/schemas/To'
        message:
          type: object
          description: Primer mensaje en el chat que se esta creando
          required:
            - text
          properties:
            text:
              type: string
              description: Texto del primer mensaje
              example: Buenos dias! En que puedo ayudarle?
        event_id:
          type: string
          description: >
            Identificador opaco del evento (hasta 60 caracteres). El aplicativo externo debe
            guardar este valor tal cual y devolverlo sin cambios en el campo event_id del evento
            error si no pudo procesar el hook. La estructura del campo es un detalle interno de
            Acloud y puede cambiar sin previo aviso; no parsee ni valide el contenido.
          example: '7c9e6679-7425-40de-944b-e07fc1f90ae7:79000'

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

    BotDeactivatedPayload:
      type: object
      description: >
        Notificacion de desactivacion del bot. Fire-and-forget — no se prevee correlacion con error.
      required:
        - channel_id
        - reason
      properties:
        channel_id:
          type: string
          description: Token del bot (bot_token) — ID de integracion en el lado del aplicativo externo
          example: bot_token_abc123
        reason:
          type: string
          description: >
            Razon de desactivacion: manual — desactivacion manual por el usuario,
            auto_unreachable — automatica por inaccesibilidad del aplicativo externo,
            subscription_expired — vencimiento de la suscripcion de la cuenta
          enum:
            - manual
            - auto_unreachable
            - subscription_expired
          example: manual

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

    BotDeletedPayload:
      type: object
      description: >
        Notificacion de eliminacion del bot. Fire-and-forget — no se prevee correlacion con error.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Token del bot (bot_token) — ID de integracion en el lado del aplicativo externo
          example: bot_token_abc123

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

    BotActivatedPayload:
      type: object
      description: >
        Notificacion de reactivacion de un bot previamente desactivado.
        Fire-and-forget — no se prevee correlacion con error.
      required:
        - channel_id
      properties:
        channel_id:
          type: string
          description: Token del bot (bot_token) — ID de integracion en el lado del aplicativo externo
          example: bot_token_abc123

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

    To:
      type: object
      description: Datos del destinatario para la inicializacion del chat
      properties:
        email:
          type: ["string", "null"]
          description: Email del usuario
        name:
          type: ["string", "null"]
          description: Nombre del usuario
        phone:
          type: ["string", "null"]
          description: Numero de telefono del usuario
        other:
          type: ["string", "null"]
          description: Identificador adicional del usuario en el sistema externo

    OutgoingAttachmentObject:
      type: object
      description: >
        Metadatos del archivo adjunto saliente. El aplicativo externo debe descargar el archivo
        desde la url proporcionada y entregarlo al destinatario.
      required:
        - id
        - type
        - url
        - filename
      properties:
        id:
          type: string
          description: ID del archivo en el sistema Acloud.
        type:
          type: string
          enum: [photo, video, audio, file]
          description: Tipo de archivo adjunto determinado por el mime del archivo.
        url:
          type: string
          description: >
            Enlace publico de un solo uso para descargar el archivo desde Acloud.
            Valido hasta 7 dias (para destinatarios iOS) o sin limite (por UUID).
        filename:
          type: string
          description: Nombre del archivo con extension.
        size:
          type: integer
          description: Tamano del archivo en bytes.
        metadata:
          type: ["object", "null"]
          description: Metadatos arbitrarios (opcional).
