openapi: "3.1.0"
info:
  title: Telephony MiniApp — Eventos de Entrada
  description: >
    Eventos de entrada do aplicativo externo (MiniApp → Acloud).
    O aplicativo externo envia uma requisicao POST para o endpoint do hook:
    POST /external/rest/telephony/hook/mini_app/{account_id}/{uuid}
    onde {account_id} e o ID da conta, {uuid} e o identificador unico da integracao
    (retornado na resposta da API telephony/create).
  version: "1.0.0"

webhooks:

  call.init:
    post:
      summary: Inicializacao de chamada
      description: >
        Enviado pelo aplicativo externo quando uma nova chamada aparece (antes da conexao).
        Para chamadas de entrada, o Acloud retorna uma decisao de roteamento — para qual
        funcionario redirecionar a chamada. Para chamadas de saida — confirmacao de recebimento.
      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: Chamada de entrada
                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: Chamada de saida
                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 processado com sucesso. Para chamadas de entrada, contem a decisao de roteamento.
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    $ref: '#/components/schemas/RoutingResult'
              examples:
                redirect:
                  summary: Redirecionar para funcionario
                  value:
                    result:
                      action: redirect
                      target:
                        sip: "101"
                pass:
                  summary: Passar sem redirecionamento
                  value:
                    result:
                      action: pass
        "400":
          description: Erro de validacao (formato invalido ou campos obrigatorios ausentes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracao desativada ou conta expirada.
            Codigos de erro: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Conta ou integracao nao encontradas.
            Codigos de erro: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.start:
    post:
      summary: Chamada conectada
      description: >
        Enviado pelo aplicativo externo quando a chamada foi atendida
        e a conversa comecou.
      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: Chamada 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 processado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Erro de validacao (formato invalido ou campos obrigatorios ausentes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracao desativada ou conta expirada.
            Codigos de erro: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Conta ou integracao nao encontradas.
            Codigos de erro: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.terminate:
    post:
      summary: Chamada encerrada
      description: >
        Enviado pelo aplicativo externo quando uma chamada e encerrada.
        Contem o status final e a duracao da conversa.
      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: Chamada concluida com sucesso
                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: Chamada 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 processado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Erro de validacao (formato invalido ou campos obrigatorios ausentes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracao desativada ou conta expirada.
            Codigos de erro: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Conta ou integracao nao encontradas.
            Codigos de erro: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.record:
    post:
      summary: Informacoes de gravacao
      description: >
        Enviado pelo aplicativo externo apos o encerramento da chamada.
        Informa se uma gravacao da conversa foi criada.
      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: Gravacao criada
                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: Sem gravacao
                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 processado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Erro de validacao (formato invalido ou campos obrigatorios ausentes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracao desativada ou conta expirada.
            Codigos de erro: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Conta ou integracao nao encontradas.
            Codigos de erro: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  call.info:
    post:
      summary: Atualizacao de status da chamada
      description: >
        Enviado pelo aplicativo externo quando o estado da chamada muda.
        Pode chegar em qualquer ordem em relacao a outros eventos —
        o sistema o processa 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: Chamada ativa
                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: Chamada encerrada
                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 processado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "400":
          description: Erro de validacao (formato invalido ou campos obrigatorios ausentes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "409":
          description: >
            Integracao desativada ou conta expirada.
            Codigos de erro: `telephony_deactivated`, `account_expired`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "410":
          description: >
            Conta ou integracao nao encontradas.
            Codigos de erro: `account_not_found`, `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  integration.deactivate:
    post:
      summary: Desativacao de integracao
      description: >
        Enviado pelo aplicativo externo para desativar a integracao de telefonia.
        Apos o processamento, a integracao passa para o estado inativo (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: Desativar integracao
                value:
                  method: integration.deactivate
                  payload: {}
      responses:
        "200":
          description: Integracao desativada com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: object
        "410":
          description: >
            Integracao nao encontrada.
            Codigo de erro: `integration_not_found`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Erro interno do 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 em todos os eventos de chamada
      required:
        - call_id
        - from
        - to
        - direction
        - timestamp
      properties:
        call_id:
          type: string
          description: Identificador unico da chamada no sistema externo
          example: call_abc123
        from:
          type: string
          description: >
            Numero de telefone ou ramal interno (extension) da origem da chamada.
            Para chamadas de entrada — numero do chamador; para saida — ramal do funcionario.
          example: "+79990001234"
        to:
          type: string
          description: >
            Numero de telefone ou ramal do destinatario da chamada.
            Para chamadas de entrada — ramal do funcionario; para saida — numero de telefone.
          example: "101"
        direction:
          type: string
          enum:
            - in
            - out
          description: Direcao da chamada. `in` — entrada, `out` — saida.
        timestamp:
          type: string
          description: Data e hora do evento no formato ISO 8601
          example: "2026-04-09T10:30:00Z"

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

    CallInitPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
      description: Payload do evento de inicializacao de chamada. Os campos correspondem ao conjunto base.

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

    CallStartPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
      description: Payload do evento de chamada conectada. Os campos correspondem ao conjunto base.

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

    CallTerminatePayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
        - type: object
          required:
            - call_duration
            - status
          properties:
            call_duration:
              type: integer
              description: Duracao da conversa em segundos
              example: 295
            status:
              type: string
              enum:
                - terminated
                - missed
              description: >
                Status final da chamada.
                `terminated` — chamada foi atendida e encerrada normalmente;
                `missed` — chamada nao foi atendida (perdida).

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

    CallRecordPayload:
      allOf:
        - $ref: '#/components/schemas/CallBasePayload'
        - type: object
          required:
            - has_record
          properties:
            has_record:
              type: boolean
              description: Indicador de existencia de gravacao da chamada
              example: true
            record_url:
              type: string
              description: Link de download da gravacao da chamada
              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: >
                Status atual da chamada.
                `active` — chamada ativa (conectada);
                `terminated` — chamada encerrada com sucesso;
                `missed` — chamada nao atendida.

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

    IntegrationDeactivatePayload:
      type: object
      description: >
        Payload do evento de desativacao de integracao. Nao contem campos —
        o simples envio do evento indica uma solicitacao de desativacao.

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

    RoutingResult:
      type: object
      required:
        - action
      properties:
        action:
          type: string
          enum:
            - redirect
            - pass
          description: >
            Acao de roteamento para chamada de entrada.
            `redirect` — redirecionar para um funcionario especifico;
            `pass` — passar sem alterar a rota.
        target:
          type: object
          description: "Destino do redirecionamento. Presente apenas quando action: redirect."
          required:
            - sip
          properties:
            sip:
              type: string
              description: Endereco SIP ou ramal do funcionario para redirecionamento
              example: "101"

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

    ErrorResponse:
      type: object
      required:
        - result
        - code
        - message
      properties:
        result:
          type: string
          enum:
            - error
        code:
          type: integer
          description: Codigo de resposta 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 erro legivel por maquina.
            `invalid_json` — o corpo da requisicao nao e um JSON valido;
            `validation_error` — campos obrigatorios ausentes ou formato de dados invalido;
            `event_not_supported` — tipo de evento desconhecido (method);
            `internal_error` — erro interno do servidor;
            `account_not_found` — a conta nao existe;
            `integration_not_found` — integracao de telefonia nao encontrada;
            `telephony_deactivated` — a integracao de telefonia esta desativada;
            `account_expired` — conta expirada, integracao desativada automaticamente.
        message:
          type: string
          description: Descricao do erro legivel por humanos
          example: "Missing required payload field: call_id"
