openapi: "3.1.0"
info:
  title: Telephony MiniApp — Eventos Entrantes
  description: >
    Eventos entrantes desde la aplicacion externa (MiniApp → Acloud).
    La aplicacion externa envia una solicitud POST al endpoint del hook:
    POST /external/rest/telephony/hook/mini_app/{account_id}/{uuid}
    donde {account_id} es el ID de la cuenta, {uuid} es el identificador unico de la integracion
    (devuelto en la respuesta de la API telephony/create).
  version: "1.0.0"

webhooks:

  call.init:
    post:
      summary: Inicializacion de llamada
      description: >
        Enviado por la aplicacion externa cuando aparece una nueva llamada (antes de la conexion).
        Para llamadas entrantes, Acloud devuelve una decision de enrutamiento — a que
        empleado redirigir la llamada. Para llamadas salientes — confirmacion de recepcion.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - call.init
                payload:
                  $ref: '#/components/schemas/CallInitPayload'
            examples:
              call_init_inbound:
                summary: Llamada entrante
                value:
                  method: call.init
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:30:00Z"
              call_init_outbound:
                summary: Llamada saliente
                value:
                  method: call.init
                  payload:
                    call_id: call_def456
                    from: "101"
                    to: "+79995556677"
                    direction: out
                    timestamp: "2026-04-09T11:00:00Z"
      responses:
        "200":
          description: >
            Evento procesado correctamente. Para llamadas entrantes, contiene la decision de enrutamiento.
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    $ref: '#/components/schemas/RoutingResult'
              examples:
                redirect:
                  summary: Redirigir al empleado
                  value:
                    result:
                      action: redirect
                      target:
                        sip: "101"
                pass:
                  summary: Pasar sin redireccion
                  value:
                    result:
                      action: pass
        "400":
          description: Error de validacion (formato invalido o campos obligatorios faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracion desactivada o cuenta expirada.
            Codigos de error: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Cuenta o integracion no encontradas.
            Codigos de error: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.start:
    post:
      summary: Llamada conectada
      description: >
        Enviado por la aplicacion externa cuando la llamada fue contestada
        y la conversacion comenzo.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - call.start
                payload:
                  $ref: '#/components/schemas/CallStartPayload'
            examples:
              call_start:
                summary: Llamada conectada
                value:
                  method: call.start
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:30:05Z"
      responses:
        "200":
          description: Evento procesado correctamente
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Error de validacion (formato invalido o campos obligatorios faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracion desactivada o cuenta expirada.
            Codigos de error: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Cuenta o integracion no encontradas.
            Codigos de error: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.terminate:
    post:
      summary: Llamada finalizada
      description: >
        Enviado por la aplicacion externa cuando finaliza una llamada.
        Contiene el estado final y la duracion de la conversacion.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - call.terminate
                payload:
                  $ref: '#/components/schemas/CallTerminatePayload'
            examples:
              call_terminate_success:
                summary: Llamada completada correctamente
                value:
                  method: call.terminate
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:35:00Z"
                    call_duration: 295
                    status: terminated
              call_terminate_missed:
                summary: Llamada perdida
                value:
                  method: call.terminate
                  payload:
                    call_id: call_abc124
                    from: "+79991112233"
                    to: "102"
                    direction: in
                    timestamp: "2026-04-09T10:40:00Z"
                    call_duration: 0
                    status: missed
      responses:
        "200":
          description: Evento procesado correctamente
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Error de validacion (formato invalido o campos obligatorios faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracion desactivada o cuenta expirada.
            Codigos de error: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Cuenta o integracion no encontradas.
            Codigos de error: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.record:
    post:
      summary: Informacion de grabacion
      description: >
        Enviado por la aplicacion externa despues de que finaliza la llamada.
        Informa si se creo una grabacion de la conversacion.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - call.record
                payload:
                  $ref: '#/components/schemas/CallRecordPayload'
            examples:
              call_record_exists:
                summary: Grabacion creada
                value:
                  method: call.record
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:35:05Z"
                    has_record: true
              call_record_none:
                summary: Sin grabacion
                value:
                  method: call.record
                  payload:
                    call_id: call_abc125
                    from: "101"
                    to: "+79997778899"
                    direction: out
                    timestamp: "2026-04-09T12:00:10Z"
                    has_record: false
      responses:
        "200":
          description: Evento procesado correctamente
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Error de validacion (formato invalido o campos obligatorios faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracion desactivada o cuenta expirada.
            Codigos de error: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Cuenta o integracion no encontradas.
            Codigos de error: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.info:
    post:
      summary: Actualizacion de estado de llamada
      description: >
        Enviado por la aplicacion externa cuando cambia el estado de la llamada.
        Puede llegar en cualquier orden relativo a otros eventos —
        el sistema lo procesa de forma idempotente.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - call.info
                payload:
                  $ref: '#/components/schemas/CallInfoPayload'
            examples:
              call_info_active:
                summary: Llamada activa
                value:
                  method: call.info
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:30:10Z"
                    status: active
              call_info_terminated:
                summary: Llamada finalizada
                value:
                  method: call.info
                  payload:
                    call_id: call_abc123
                    from: "+79990001234"
                    to: "101"
                    direction: in
                    timestamp: "2026-04-09T10:35:10Z"
                    status: terminated
      responses:
        "200":
          description: Evento procesado correctamente
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Error de validacion (formato invalido o campos obligatorios faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracion desactivada o cuenta expirada.
            Codigos de error: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Cuenta o integracion no encontradas.
            Codigos de error: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  integration.deactivate:
    post:
      summary: Desactivacion de integracion
      description: >
        Enviado por la aplicacion externa para desactivar la integracion de telefonia.
        Tras el procesamiento, la integracion pasa al estado inactivo (active = false).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - method
                - payload
              properties:
                method:
                  type: string
                  enum:
                    - integration.deactivate
                payload:
                  $ref: '#/components/schemas/IntegrationDeactivatePayload'
            examples:
              deactivate:
                summary: Desactivar integracion
                value:
                  method: integration.deactivate
                  payload: {}
      responses:
        "200":
          description: Integracion desactivada correctamente
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "410":
          description: >
            Integracion no encontrada.
            Codigo de error: `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:

    IncomingEvent:
      type: object
      required:
        - method
        - payload
      properties:
        method:
          type: string
          enum:
            - call.init
            - call.start
            - call.terminate
            - call.record
            - call.info
            - integration.deactivate
          description: Tipo de evento
        payload:
          oneOf:
            - $ref: '#/components/schemas/CallInitPayload'
            - $ref: '#/components/schemas/CallStartPayload'
            - $ref: '#/components/schemas/CallTerminatePayload'
            - $ref: '#/components/schemas/CallRecordPayload'
            - $ref: '#/components/schemas/CallInfoPayload'
            - $ref: '#/components/schemas/IntegrationDeactivatePayload'
          discriminator:
            propertyName: method

    # ─── Shared base ──────────────────────────────────────────────────────────

    CallBasePayload:
      type: object
      description: Campos base presentes en todos los eventos de llamada
      required:
        - call_id
        - from
        - to
        - direction
        - timestamp
      properties:
        call_id:
          type: string
          description: Identificador unico de la llamada en el sistema externo
          example: call_abc123
        from:
          type: string
          description: >
            Numero de telefono o extension interna del origen de la llamada.
            Para llamadas entrantes — numero del llamante; para salientes — extension del empleado.
          example: "+79990001234"
        to:
          type: string
          description: >
            Numero de telefono o extension del destinatario de la llamada.
            Para llamadas entrantes — extension del empleado; para salientes — numero de telefono.
          example: "101"
        direction:
          type: string
          enum:
            - in
            - out
          description: Direccion de la llamada. `in` — entrante, `out` — saliente.
        timestamp:
          type: string
          description: Fecha y hora del evento en formato ISO 8601
          example: "2026-04-09T10:30:00Z"

    # ─── call.init ────────────────────────────────────────────────────────────

    CallInitPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
      description: Payload del evento de inicializacion de llamada. Los campos coinciden con el conjunto base.

    # ─── call.start ───────────────────────────────────────────────────────────

    CallStartPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
      description: Payload del evento de llamada conectada. Los campos coinciden con el conjunto base.

    # ─── call.terminate ───────────────────────────────────────────────────────

    CallTerminatePayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
        - type: object
          required:
            - call_duration
            - status
          properties:
            call_duration:
              type: integer
              description: Duracion de la conversacion en segundos
              example: 295
            status:
              type: string
              enum:
                - terminated
                - missed
              description: >
                Estado final de la llamada.
                `terminated` — la llamada fue contestada y finalizo normalmente;
                `missed` — la llamada no fue contestada (perdida).

    # ─── call.record ──────────────────────────────────────────────────────────

    CallRecordPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
        - type: object
          required:
            - has_record
          properties:
            has_record:
              type: boolean
              description: Indicador de si existe grabacion de la llamada
              example: true
            record_url:
              type: string
              description: Enlace de descarga de la grabacion de la llamada
              example: https://dominio/call_id
              nullable: true

    # ─── call.info ────────────────────────────────────────────────────────────

    CallInfoPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
        - type: object
          required:
            - status
          properties:
            status:
              type: string
              enum:
                - active
                - terminated
                - missed
              description: >
                Estado actual de la llamada.
                `active` — llamada activa (conectada);
                `terminated` — llamada finalizada correctamente;
                `missed` — llamada no contestada.

    # ─── integration.deactivate ─────────────────────────────────────────────

    IntegrationDeactivatePayload:
      type: object
      description: >
        Payload del evento de desactivacion de integracion. No contiene campos —
        el simple envio del evento indica una solicitud de desactivacion.

    # ─── Routing result ───────────────────────────────────────────────────────

    RoutingResult:
      type: object
      required:
        - action
      properties:
        action:
          type: string
          enum:
            - redirect
            - pass
          description: >
            Accion de enrutamiento para una llamada entrante.
            `redirect` — redirigir a un empleado especifico;
            `pass` — pasar sin cambiar la ruta.
        target:
          type: object
          description: "Destino de la redireccion. Presente solo cuando action: redirect."
          required:
            - sip
          properties:
            sip:
              type: string
              description: Direccion SIP o extension del empleado para la redireccion
              example: "101"

    # ─── Error response ───────────────────────────────────────────────────────

    ErrorResponse:
      type: object
      required:
        - result
        - code
        - message
      properties:
        result:
          type: string
          enum:
            - error
        code:
          type: integer
          description: Codigo de respuesta HTTP
          example: 400
        error:
          type: string
          enum:
            - invalid_json
            - validation_error
            - event_not_supported
            - internal_error
            - account_not_found
            - integration_not_found
            - telephony_deactivated
            - account_expired
          description: >
            Codigo de error legible por maquina.
            `invalid_json` — el cuerpo de la solicitud no es JSON valido;
            `validation_error` — campos obligatorios faltantes o formato de datos invalido;
            `event_not_supported` — tipo de evento desconocido (method);
            `internal_error` — error interno del servidor;
            `account_not_found` — la cuenta no existe;
            `integration_not_found` — integracion de telefonia no encontrada;
            `telephony_deactivated` — la integracion de telefonia esta desactivada;
            `account_expired` — cuenta expirada, integracion desactivada automaticamente.
        message:
          type: string
          description: Descripcion del error legible por humanos
          example: "Missing required payload field: call_id"
