openapi: 3.1.0
info:
  title: IndicaAi API
  version: 0.1.0-rascunho
  summary: API pública do portal de indicações IndicaAi (multi-empresa).
  description: |
    Contrato **rascunho** da API pública. Cada chave de API pertence a uma empresa e nunca enxerga outra.
    As regras de negócio vivem no banco; esta API é uma porta para as mesmas funções usadas pelo web/app.
    Veja `docs/09-api.md`.
servers:
  - url: https://api.indicaai.softhubsolucoes.com/v1
    description: Produção (planejado)
security:
  - ApiKey: []
tags:
  - name: Indicações
  - name: Base de contatos
  - name: Vendedores
  - name: Comissões e Pix
  - name: Gamificação
  - name: Webhooks
paths:
  /indicacoes:
    get:
      tags: [Indicações]
      summary: Lista indicações
      description: Escopo `indicacoes:read`.
      parameters:
        - { name: status, in: query, schema: { $ref: '#/components/schemas/StatusIndicacao' } }
        - { name: desde, in: query, schema: { type: string, format: date-time } }
        - { name: vendedor_id, in: query, schema: { type: string, format: uuid } }
        - { $ref: '#/components/parameters/Limite' }
        - { $ref: '#/components/parameters/Cursor' }
      responses:
        '200':
          description: Página de indicações
          content:
            application/json:
              schema:
                type: object
                properties:
                  itens: { type: array, items: { $ref: '#/components/schemas/Indicacao' } }
                  proximo_cursor: { type: [string, 'null'] }
        '401': { $ref: '#/components/responses/NaoAutorizado' }
        '429': { $ref: '#/components/responses/LimiteExcedido' }
  /indicacoes/{id}:
    get:
      tags: [Indicações]
      summary: Detalha uma indicação com a linha do tempo
      parameters:
        - { $ref: '#/components/parameters/IdPath' }
      responses:
        '200':
          description: Indicação
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Indicacao'
                  - type: object
                    properties:
                      eventos: { type: array, items: { $ref: '#/components/schemas/Evento' } }
        '404': { $ref: '#/components/responses/NaoEncontrado' }
  /indicacoes/{id}/acoes:
    post:
      tags: [Indicações]
      summary: Aplica uma ação no funil
      description: |
        Escopo `indicacoes:write`. Executa `atualizar_indicacao`: valida a transição, pontua o indicador e,
        em `registrar_venda`, gera a comissão pela regra ativa.
      parameters:
        - { $ref: '#/components/parameters/IdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AcaoIndicacao' }
            examples:
              confirmar:
                value: { acao: confirmar_interesse, dados: { nota_interna: 'Falei por telefone' } }
              venda:
                value: { acao: registrar_venda, dados: { valor_venda: 62000 } }
              perda:
                value: { acao: registrar_perda, dados: { motivo: sem_interesse, motivo_detalhe: 'Já comprou outro carro' } }
      responses:
        '200':
          description: Resultado da ação
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string, format: uuid }
                  status: { $ref: '#/components/schemas/StatusIndicacao' }
                  pontos: { type: integer, description: Pontos concedidos ao indicador nesta ação }
                  comissao_id: { type: [string, 'null'], format: uuid }
        '409': { $ref: '#/components/responses/Regra' }
        '422': { $ref: '#/components/responses/Regra' }
  /contatos/importar:
    post:
      tags: [Base de contatos]
      summary: Importa clientes para barrar indicações de quem já é cliente
      description: Escopo `contatos:write`. Deduplica por telefone, CPF e `externo_id`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [contatos]
              properties:
                sistema: { type: string, example: autoconf }
                contatos:
                  type: array
                  maxItems: 1000
                  items: { $ref: '#/components/schemas/Contato' }
      responses:
        '200':
          description: Resumo
          content:
            application/json:
              schema:
                type: object
                properties:
                  inseridos: { type: integer }
                  atualizados: { type: integer }
                  ignorados: { type: integer }
  /vendedores:
    get:
      tags: [Vendedores]
      summary: Lista vendedores
      responses:
        '200':
          description: Vendedores
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/Vendedor' } }
    put:
      tags: [Vendedores]
      summary: Sincroniza vendedores (upsert por `externo_id`)
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: array, items: { $ref: '#/components/schemas/Vendedor' } }
      responses:
        '200': { description: Sincronizado }
  /comissoes:
    get:
      tags: [Comissões e Pix]
      summary: Lista comissões
      parameters:
        - { name: status, in: query, schema: { type: string, enum: [pendente, aprovada, paga, cancelada] } }
      responses:
        '200':
          description: Comissões
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/Comissao' } }
  /comissoes/{id}/aprovar:
    post:
      tags: [Comissões e Pix]
      summary: Aprova a comissão e abre o pedido de Pix
      parameters:
        - { $ref: '#/components/parameters/IdPath' }
      responses:
        '200': { description: Aprovada }
        '422': { $ref: '#/components/responses/Regra' }
  /pagamentos-pix:
    get:
      tags: [Comissões e Pix]
      summary: Lista pedidos de Pix
      parameters:
        - { name: status, in: query, schema: { type: string, enum: [aguardando_chave, aberto, pago, confirmado, cancelado] } }
      responses:
        '200':
          description: Pedidos
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/PagamentoPix' } }
  /pagamentos-pix/{id}/pagar:
    post:
      tags: [Comissões e Pix]
      summary: Registra o pagamento com o comprovante
      description: |
        O comprovante deve ser enviado antes ao armazenamento privado, na pasta `<empresa_id>/<pagamento_id>/`.
        Sem comprovante válido a operação é recusada (`comprovante_obrigatorio`).
      parameters:
        - { $ref: '#/components/parameters/IdPath' }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [comprovante_path]
              properties:
                comprovante_path: { type: string, example: '0b8f…/9a1c…/comprovante.pdf' }
                observacao: { type: string }
      responses:
        '200': { description: Pago }
        '422': { $ref: '#/components/responses/Regra' }
  /ranking:
    get:
      tags: [Gamificação]
      summary: Ranking da empresa
      parameters:
        - { name: periodo, in: query, schema: { type: string, enum: [semana, mes, ano, geral], default: mes } }
        - { $ref: '#/components/parameters/Limite' }
      responses:
        '200':
          description: Posições (apenas apelido e avatar)
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/PosicaoRanking' } }
  /webhooks:
    get:
      tags: [Webhooks]
      summary: Lista webhooks de saída
      responses:
        '200':
          description: Webhooks
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/Webhook' } }
    post:
      tags: [Webhooks]
      summary: Cria webhook de saída
      description: Escopo `webhooks:manage`. O segredo de assinatura é devolvido uma única vez.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WebhookNovo' }
      responses:
        '201':
          description: Criado
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Webhook'
                  - type: object
                    properties:
                      segredo: { type: string, description: Mostrado apenas na criação }
  /integracoes/autoconf/webhook:
    post:
      tags: [Webhooks]
      summary: Entrada de eventos do Autoconf
      description: |
        Receptor dos webhooks do Autoconf (lead criado, sucesso, insucesso). **Formato do payload a confirmar**
        com a documentação do Autoconf; aqui fica o contrato interno normalizado após o adaptador.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                evento_externo_id: { type: string, description: Para idempotência }
                lead_externo_id: { type: string }
                tipo: { type: string, enum: [lead_criado, sucesso, insucesso] }
                valor_venda: { type: string }
                motivo: { type: string }
      responses:
        '202': { description: Aceito }
webhooks:
  eventoIndicacao:
    post:
      summary: Evento enviado pelo IndicaAi ao seu endpoint
      description: Cabeçalhos `X-IndicaAi-Event`, `X-IndicaAi-Delivery`, `X-IndicaAi-Timestamp`, `X-IndicaAi-Signature` (HMAC SHA-256).
      requestBody:
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EventoWebhook' }
      responses:
        '2XX': { description: Recebido }
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-API-Key
  parameters:
    IdPath: { name: id, in: path, required: true, schema: { type: string, format: uuid } }
    Limite: { name: limite, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } }
    Cursor: { name: cursor, in: query, schema: { type: string } }
    IdempotencyKey: { name: Idempotency-Key, in: header, schema: { type: string, maxLength: 80 } }
  responses:
    NaoAutorizado:
      description: Chave ausente, inválida ou sem escopo
      content: { application/json: { schema: { $ref: '#/components/schemas/Erro' } } }
    NaoEncontrado:
      description: Não encontrado nesta empresa
      content: { application/json: { schema: { $ref: '#/components/schemas/Erro' } } }
    LimiteExcedido:
      description: Muitas requisições
      headers: { Retry-After: { schema: { type: integer } } }
      content: { application/json: { schema: { $ref: '#/components/schemas/Erro' } } }
    Regra:
      description: Violação de regra de negócio (ver códigos em docs/09-api.md)
      content: { application/json: { schema: { $ref: '#/components/schemas/Erro' } } }
  schemas:
    Erro:
      type: object
      required: [erro]
      properties:
        erro:
          type: object
          required: [codigo, mensagem]
          properties:
            codigo: { type: string, example: transicao_invalida }
            mensagem: { type: string }
            detalhes: { type: object }
    StatusIndicacao:
      type: string
      enum: [lead_validado, em_contato, interesse_confirmado, distribuida, em_negociacao, venda_concluida, perdida, rejeitada, cancelada, expirada]
    Indicacao:
      type: object
      properties:
        id: { type: string, format: uuid }
        numero: { type: integer }
        status: { $ref: '#/components/schemas/StatusIndicacao' }
        motivo: { type: [string, 'null'] }
        interesse: { type: string }
        lead: { $ref: '#/components/schemas/Lead' }
        indicador: { type: object, properties: { id: { type: string, format: uuid }, apelido: { type: string }, nivel: { type: string } } }
        vendedor_id: { type: [string, 'null'], format: uuid }
        valor_venda: { type: [string, 'null'] }
        sla_feedback_ate: { type: [string, 'null'], format: date-time }
        criada_em: { type: string, format: date-time }
    Lead:
      type: object
      description: Dados pessoais do indicado; só retornam com o escopo e a habilitação da empresa (minimização).
      properties:
        nome: { type: string }
        telefone: { type: string, example: '+5534999448067' }
        email: { type: [string, 'null'] }
        cpf: { type: [string, 'null'] }
    Evento:
      type: object
      properties:
        tipo: { type: string }
        de_status: { type: [string, 'null'] }
        para_status: { type: [string, 'null'] }
        mensagem: { type: [string, 'null'] }
        visivel_indicador: { type: boolean }
        em: { type: string, format: date-time }
    AcaoIndicacao:
      type: object
      required: [acao]
      properties:
        acao:
          type: string
          enum: [iniciar_contato, confirmar_interesse, distribuir, iniciar_negociacao, registrar_venda, registrar_perda, cancelar]
        dados:
          type: object
          properties:
            vendedor_id: { type: string, format: uuid }
            valor_venda: { type: number }
            motivo: { type: string }
            motivo_detalhe: { type: string }
            mensagem: { type: string, description: Visível ao indicador }
            nota_interna: { type: string, description: Não visível ao indicador }
    Contato:
      type: object
      properties:
        externo_id: { type: string }
        nome: { type: string }
        telefone: { type: string }
        cpf: { type: string }
        email: { type: string }
    Vendedor:
      type: object
      required: [nome]
      properties:
        id: { type: string, format: uuid }
        externo_id: { type: string }
        nome: { type: string }
        email: { type: string }
        telefone: { type: string }
        ativo: { type: boolean }
    Comissao:
      type: object
      properties:
        id: { type: string, format: uuid }
        indicacao_id: { type: string, format: uuid }
        tipo: { type: string, enum: [fixo, percentual] }
        valor: { type: string, example: '300.00' }
        status: { type: string, enum: [pendente, aprovada, paga, cancelada] }
    PagamentoPix:
      type: object
      properties:
        id: { type: string, format: uuid }
        comissao_id: { type: string, format: uuid }
        valor: { type: string }
        status: { type: string, enum: [aguardando_chave, aberto, pago, confirmado, cancelado] }
        comprovante_path: { type: [string, 'null'] }
    PosicaoRanking:
      type: object
      properties:
        posicao: { type: integer }
        apelido: { type: string }
        avatar_url: { type: [string, 'null'] }
        nivel: { type: string }
        pontos: { type: integer }
    Webhook:
      type: object
      properties:
        id: { type: string, format: uuid }
        url: { type: string, format: uri }
        eventos: { type: array, items: { type: string } }
        ativo: { type: boolean }
    WebhookNovo:
      type: object
      required: [url, eventos]
      properties:
        url: { type: string, format: uri, pattern: '^https://' }
        eventos: { type: array, items: { type: string, example: indicacao.venda_concluida } }
    EventoWebhook:
      type: object
      properties:
        id: { type: string, format: uuid }
        evento: { type: string }
        empresa: { type: string }
        ocorrido_em: { type: string, format: date-time }
        dados: { type: object }
