# Copyright (c) 2026 Sentinex Risk Technologies. All rights reserved.
# Licensed under the Sentinex Risk Commercial License.

openapi: "3.0.3"
info:
  title: Sentinex Risk Crypto — API
  description: |
    Plataforma SaaS multi-tenant de risco operacional, compliance e KYC/KYT
    para criptoativos no Brasil. Todos os endpoints protegidos operam no
    escopo do tenant extraído do token de autenticação.

    ## Casos de uso principais

    - **KYT em tempo real**: pontue cada transação on/off-chain em <100ms
      e receba decisão `APPROVE` / `REVIEW` / `REJECT` com breakdown de score
      em 5 dimensões e regras acionadas explicáveis.
    - **KYC/KYB completo**: onboarding de pessoa física e jurídica com
      validação documental, screening em listas de sanções (OFAC, ONU, COAF,
      União Europeia, FinCEN) e bureaus brasileiros.
    - **Análise de carteiras on-chain**: clustering proprietário, atribuição
      a entidades conhecidas, screening de exposição a endereços sancionados.
    - **Travel Rule (IVMS101)**: troca de informações entre VASPs conforme
      Art. 44 e Art. 89 da BCB Res. 520.
    - **Reportes regulatórios**: SAR COAF automatizado (>R$100k / >R$500k
      em 7 dias), DeCripto (RFB IN 2291/2025), reporte mensal de câmbio com
      stablecoins ao BCB.
    - **Mesa de trabalho integrada**: alertas, casos, escalonamento e audit
      trail imutável para times de compliance.

    ## Camadas de segurança (em ordem de aplicação)

    1. **Recovery middleware** — captura panics, retorna 500 padronizado
    2. **Security headers** — HSTS, CSP, X-Frame-Options, X-Content-Type-Options
    3. **API Shield** — abuse score, blocklist de IPs, detecção SQLi/XSS
    4. **Rate limiting global** — por IP e por tenant, *fail-closed*
    5. **CORS** — allowlist restritiva de origens
    6. **CSRF double-submit cookie** — em rotas autenticadas via cookie
    7. **Autenticação** — JWT RS256 (dashboard) ou API Key (B2B futuro V1)
    8. **Resolução de tenant + gating de módulo** — entitlements por plano

    ## Compliance regulatório embarcado

    | Norma | Cobertura |
    |-------|-----------|
    | Lei 14.478/2022 (Marco Legal Cripto) | Toda |
    | Decreto 11.563/2023 | Toda |
    | BCB Res. 519, 520, 521, 549, 550, 553 | KYC, custódia, Travel Rule |
    | BCB IN 693, 701, 704 | Reportes operacionais |
    | RFB IN 2291/2025 (DeCripto) | Reporte mensal/anual |
    | COAF Lei 9.613/1998 + Carta-Circular 4001 | SAR automatizado |
    | CMN Res. 5280, 5281 | Câmbio com stablecoins |
    | LGPD (Lei 13.709/2018) | Hash PII, soft-delete, DSAR |

    ## Suporte técnico

    - 📧 dev-support@sentinexrisk.com
    - 📖 Quickstart: https://docs.crypto.sentinexrisk.com/quickstart
    - 💬 Slack Connect (clientes Pro+)
    - 🎫 Status page: https://status.crypto.sentinexrisk.com
  version: "1.0.0"
  termsOfService: https://sentinexrisk.com/termos
  contact:
    name: Sentinex Risk Technologies
    url: https://sentinexrisk.com
    email: dev-support@sentinexrisk.com
  license:
    name: Licença Comercial Sentinex Risk
    url: https://sentinexrisk.com/licenca

servers:
  - url: https://api.crypto.sentinexrisk.com/v1
    description: Produção (V1) — em provisionamento (Onda 1 do roadmap)
  - url: https://sandbox.api.crypto.sentinexrisk.com/v1
    description: Sandbox público — dados fictícios, sem custo, rate limit 10 rpm
  - url: https://crypto.sentinexrisk.com/api/v1
    description: Produção (dashboard humano — JWT) — endpoint atual
  - url: http://localhost:3000/api/v1
    description: Desenvolvimento local

# ──────────────────────────────────────────────────────────────────────
# Tags organizadas hierarquicamente por DOMÍNIO (modelo Exato.digital).
# Prefixos:
#   • Sentinex —     produto interno (motor, regras, decisão)
#   • Identidade —   KYC/KYB pessoa física e jurídica
#   • Carteiras —    análise on-chain, screening, blocklist
#   • Transações —   KYT, scoring, review
#   • Compliance —   reportes regulatórios obrigatórios (COAF, RFB, BCB)
#   • Operações —    alertas, casos, mesa de trabalho
#   • Plataforma —   infra do produto (auth, billing, módulos, admin)
# ──────────────────────────────────────────────────────────────────────
tags:
  # ── Sentinex — núcleo do produto ──
  - name: Sentinex — Saúde
    description: |
      Health checks e endpoints públicos de infraestrutura (chaves de
      criptografia, desafios proof-of-work, status do sistema).
  - name: Sentinex — Decisão
    description: |
      Motor de decisão antifraude. Recebe contexto de transação ou cliente,
      retorna score 0-100 com breakdown em 5 dimensões e regras acionadas.
      Latência alvo &lt;100ms p95.
  - name: Sentinex — Explicabilidade
    description: |
      Quebra explicável da decisão antifraude (Explainable AI). Retorna
      contribuição de cada feature e cada regra ao score final. Suporta
      auditoria regulatória e contestação operacional.
  - name: Sentinex — Radar
    description: |
      Mapa em tempo real de hotspots geográficos de fraude. Agrega alertas
      por região, tipologia e janela temporal para resposta proativa.
  - name: Sentinex — Fingerprint
    description: |
      Coleta e consulta do SentinexPrint™ — fingerprint de dispositivo
      em 7 camadas (canvas, audio, fonts, WebGL, comportamento, network,
      sinais avançados). Substituto proprietário de FingerprintJS.

  # ── Identidade — KYC/KYB ──
  - name: Identidade — Clientes
    description: |
      Cadastro, atualização, consulta e remoção (soft delete LGPD) de
      clientes. Suporta pessoa física e jurídica brasileira (CPF/CNPJ).
  - name: Identidade — Screening
    description: |
      Triagem de cliente contra listas de sanções (OFAC, ONU, União
      Europeia, FinCEN), bases brasileiras (COAF, IBAMA, PGFN, CEIS,
      CEPIM, CNEP, TSE) e PEPs.

  # ── Transações — KYT ──
  - name: Transações — KYT
    description: |
      Pontuação e revisão de transações cripto em tempo real. Cada
      transação retorna decisão `APPROVE` / `REVIEW` / `REJECT` com
      score 0-100, breakdown em 5 dimensões e regras acionadas.
  - name: Transações — Histórico
    description: |
      Consulta paginada do histórico de transações com filtros (status,
      score, severidade, blockchain, janela temporal).

  # ── Carteiras — On-chain ──
  - name: Carteiras — Análise
    description: |
      Análise on-chain de endereços: clustering, atribuição (exchange,
      mixer, sanctions, VASP conhecido), exposição a fontes ilícitas,
      caminhos para endereços sancionados.
  - name: Carteiras — Blocklist
    description: |
      Inclusão e remoção de endereços em blocklist do tenant.
      Aplicação automática em scoring futuro.

  # ── Operações ──
  - name: Operações — Alertas
    description: |
      Triagem, atribuição, resolução e escalonamento de alertas gerados
      pelo motor antifraude. Integra com Mesa de Trabalho.
  - name: Operações — Casos
    description: |
      Gestão de casos de investigação. Agrega alertas, evidências, notas,
      timeline e audit trail imutável. Escalável a SAR COAF.
  - name: Operações — Mesa de Trabalho
    description: |
      Visão consolidada do operador: fila de alertas pendentes, casos
      atribuídos, KYB em análise, escalonamentos urgentes.

  # ── Compliance ──
  - name: Compliance — COAF (SAR)
    description: |
      Geração e submissão de Relatório de Operação Suspeita (SAR / RIF)
      ao COAF. Disparo automático em transações &gt;R$100k ou agregadas
      &gt;R$500k em 7 dias. Conforme Lei 9.613/1998 e Carta-Circular 4001.
  - name: Compliance — RFB (DeCripto)
    description: |
      Reporte mensal e anual à Receita Federal conforme IN 2291/2025
      (Declaração de Operações com Criptoativos).
  - name: Compliance — BCB (Câmbio)
    description: |
      Reporte mensal de operações de câmbio com stablecoins ao Banco
      Central, conforme CMN Res. 5280/5281 (vigência 04/05/2026).
  - name: Compliance — Travel Rule
    description: |
      Troca de mensagens IVMS101 entre VASPs (originador e beneficiário)
      conforme Art. 44 e Art. 89 da BCB Res. 520.
  - name: Compliance — LGPD
    description: |
      Atendimento ao titular (DSAR), soft delete, anonimização. Cobertura
      Lei 13.709/2018 e regulamentações ANPD.

  # ── Plataforma ──
  - name: Plataforma — Autenticação
    description: |
      Login, MFA (TOTP), refresh de sessão, logout, gestão de senha.
      JWT RS256 com cookie HttpOnly + Secure + SameSite=Strict.
  - name: Plataforma — Painel
    description: |
      Métricas agregadas e KPIs do dashboard (volume, scores médios,
      alertas abertos, casos pendentes, conformidade regulatória).
  - name: Plataforma — Regras
    description: |
      Listagem de regras do motor antifraude (37 base + 12 crypto +
      24 avançadas = 73 regras totais), toggle shadow-mode, versionamento.
  - name: Plataforma — Relatórios
    description: |
      Geração de relatórios consolidados para auditoria interna e externa
      (PDF, CSV, XLSX). Suporta export assíncrono para volumes grandes.
  - name: Plataforma — Webhooks
    description: |
      Recebimento de webhooks de provedores externos (chargeback,
      blockchain events, billing providers). HMAC SHA-256 + replay protect.
  - name: Plataforma — Cobrança
    description: |
      Faturamento BR-nativo: boleto, PIX, cartão (via Asaas) e emissão
      de NFS-e (via eNotas). Faturamento recorrente por plano.
  - name: Plataforma — Módulos
    description: |
      Licenciamento modular. Catálogo de módulos, entitlements por tenant,
      controle de quota, trial sob demanda. Mapeia obrigações regulatórias
      brasileiras (BCB Res. 520/521, RFB IN 2291/2025, COAF Lei 9.613)
      a módulos comercializáveis individualmente ou em pacotes.
  - name: Administração
    description: |
      Endpoints exclusivos para super-administradores Sentinex. Gestão
      de tenants, usuários cross-tenant, auditoria global, configurações
      do produto.

paths:
  # ──────────────────── Health ────────────────────
  /health:
    get:
      operationId: healthCheck
      summary: Verifica saúde do sistema
      description: |
        Retorna status agregado da plataforma e dos componentes críticos
        (PostgreSQL, Redis, integrações externas). Use este endpoint em
        probes de load balancer, monitoramento externo e dashboards de
        status. Não requer autenticação para permitir verificação de
        saúde mesmo quando o serviço de auth está degradado.
      tags: ["Sentinex — Saúde"]
      security: []
      responses:
        "200":
          description: Sistema saudável
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "503":
          description: Sistema degradado ou indisponível

  # ──────────────────── Security ────────────────────
  /security/pubkey:
    get:
      operationId: getPublicKey
      summary: Obtém chave pública para criptografia de transporte
      description: |
        Retorna a chave pública RSA usada para cifrar payloads sensíveis
        no transporte (camada adicional sobre TLS). Clientes que enviam
        secrets em request bodies (tokens, senhas em fluxo de signup)
        devem cifrar com esta chave. O par de chaves é rotacionado em
        prod a cada 24h.
      tags: ["Sentinex — Saúde"]
      security: []
      responses:
        "200":
          description: Chave pública RSA para criptografia de transporte
          content:
            application/json:
              schema:
                type: object
                properties:
                  public_key:
                    type: string
                    description: Chave pública RSA codificada em PEM

  /security/challenge:
    get:
      operationId: getPoWChallenge
      summary: Obtém challenge proof-of-work para anti-abuso
      description: |
        Retorna um challenge de proof-of-work que o cliente deve resolver
        antes de chamar `/auth/login` ou endpoints sensíveis. Defesa
        contra credential stuffing e brute force distribuído: aumenta o
        custo computacional do atacante sem onerar usuários legítimos.
        O dificuldade é ajustada dinamicamente conforme tenant_id, IP
        de origem e janela temporal de tentativas falhas.
      tags: ["Sentinex — Saúde"]
      security: []
      responses:
        "200":
          description: Challenge proof-of-work para o cliente resolver antes da autenticação
          content:
            application/json:
              schema:
                type: object
                properties:
                  challenge:
                    type: string
                  difficulty:
                    type: integer
                  expires_at:
                    type: string
                    format: date-time

  # ──────────────────── Auth ────────────────────
  /auth/login:
    post:
      operationId: authLogin
      summary: Autentica com email e senha
      description: |
        Inicia o fluxo de autenticação interativo. Em sucesso retorna um
        token JWT de curta duração quando MFA não está habilitado para
        o usuário; quando MFA está habilitado, retorna um `mfa_challenge`
        que deve ser completado em `/auth/mfa/verify`. Rate limit por
        IP + por email; falhas consecutivas incrementam a dificuldade do
        próximo proof-of-work exigido. Senhas inválidas e usuários
        inexistentes retornam a mesma resposta para evitar enumeração.
      tags: ["Plataforma — Autenticação"]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          description: Login bem-sucedido (retorna token JWT ou challenge MFA pendente)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoginResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /auth/mfa/verify:
    post:
      operationId: authVerifyMFA
      summary: Verifica código TOTP do MFA
      description: |
        Completa o segundo fator de autenticação. Recebe `mfa_challenge`
        retornado por `/auth/login` e o código TOTP de 6 dígitos do
        autenticador do usuário. Em sucesso emite o JWT final + refresh
        token. Para evitar fadiga do usuário, sessões do mesmo
        device+IP dentro de janela de 3h não exigem nova verificação MFA.
      tags: ["Plataforma — Autenticação"]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MFAVerifyRequest"
      responses:
        "200":
          description: MFA verificado, JWT emitido
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /auth/refresh:
    post:
      operationId: authRefresh
      summary: Renova access token usando refresh token
      description: |
        Troca um refresh token válido por um novo par (access + refresh).
        O refresh token original é invalidado (rotação obrigatória) para
        impedir reuso. Refresh tokens têm duração de 7 dias por default;
        tenants com perfil de risco maior podem ter janelas menores
        configuradas. Falha se o refresh token foi rotacionado
        anteriormente — caracteriza tentativa de replay.
      tags: ["Plataforma — Autenticação"]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [refresh_token]
              properties:
                refresh_token:
                  type: string
      responses:
        "200":
          description: Novo par de tokens emitido (refresh anterior invalidado)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /auth/logout:
    post:
      operationId: authLogout
      summary: Encerra sessão e invalida tokens
      description: |
        Adiciona o JWT atual e o refresh token correspondente a uma
        blocklist com TTL igual ao tempo de vida remanescente do token.
        Após este endpoint, todas as requests subsequentes com esses
        tokens retornam 401. Logout é idempotente — chamadas repetidas
        após sucesso continuam retornando 200.
      tags: ["Plataforma — Autenticação"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Sessão encerrada com sucesso
        "401":
          $ref: "#/components/responses/Unauthorized"

  /auth/me:
    get:
      operationId: authMe
      summary: Retorna perfil do usuário autenticado
      description: |
        Retorna informações do usuário atual extraídas do JWT + dados
        complementares persistidos (papel RBAC, tenant, status do MFA,
        timestamps de criação e último login). Não inclui dados
        sensíveis como hash de senha. Use este endpoint para popular
        a tela de perfil e checar permissões antes de renderizar UI
        sensível.
      tags: ["Plataforma — Autenticação"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Perfil do usuário atual
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfile"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /auth/password:
    put:
      operationId: authChangePassword
      summary: Altera senha do usuário autenticado
      description: |
        Requer senha atual como prova de posse (defesa contra session
        hijacking que tentaria trocar a senha). A nova senha precisa ter
        no mínimo 12 caracteres e passa por validação de força (entropia
        + dicionário de senhas comuns + dados pessoais do usuário). Em
        sucesso, todas as outras sessões ativas do usuário são
        invalidadas; o JWT atual permanece válido até expiração natural.
      tags: ["Plataforma — Autenticação"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, new_password]
              properties:
                current_password:
                  type: string
                  format: password
                new_password:
                  type: string
                  format: password
                  minLength: 12
      responses:
        "200":
          description: Senha alterada com sucesso
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /auth/mfa/setup:
    post:
      operationId: authSetupMFA
      summary: Inicia configuração do MFA (retorna secret TOTP e QR code)
      description: |
        Gera um secret TOTP novo para o usuário e retorna em três
        formatos: secret cru (base32), QR code pronto para escaneamento
        em apps autenticadores (Google Authenticator, Authy, 1Password)
        e URL otpauth para deep-link. O secret só é persistido como
        ativo após confirmação em `/auth/mfa/confirm` — até lá fica em
        estado pending e expira em 10 minutos. Se o usuário já tem MFA
        habilitado, este endpoint requer reautenticação recente.
      tags: ["Plataforma — Autenticação"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Dados de provisionamento TOTP (secret + QR code + URL)
          content:
            application/json:
              schema:
                type: object
                properties:
                  secret:
                    type: string
                  qr_code:
                    type: string
                    description: Imagem do QR code codificada em Base64
                  otpauth_url:
                    type: string
        "401":
          $ref: "#/components/responses/Unauthorized"

  /auth/mfa/confirm:
    post:
      operationId: authConfirmMFA
      summary: Confirma configuração do MFA com código TOTP
      description: |
        Valida o primeiro código TOTP gerado pelo app autenticador do
        usuário. Em sucesso o secret pending vira ativo e retorna 10
        recovery codes single-use que o usuário deve salvar em local
        seguro (recovery codes permitem login se o device com TOTP for
        perdido). Cada recovery code é hashed com bcrypt antes da
        persistência. Falha aborta o setup e o secret pending é
        descartado — usuário precisa chamar `/auth/mfa/setup` novamente.
      tags: ["Plataforma — Autenticação"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [code]
              properties:
                code:
                  type: string
                  pattern: "^[0-9]{6}$"
      responses:
        "200":
          description: MFA habilitado com sucesso (retorna 10 recovery codes single-use)
          content:
            application/json:
              schema:
                type: object
                properties:
                  recovery_codes:
                    type: array
                    items:
                      type: string
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────── Dashboard ────────────────────
  /dashboard/summary:
    get:
      operationId: getDashboardSummary
      summary: Retorna resumo executivo do painel para o tenant atual
      description: |
        Agrega os indicadores principais do tenant: volume de transações
        no período (com comparativo mês-anterior e ano-anterior), taxa
        de aprovação/revisão/bloqueio, total de alertas abertos por
        severidade, casos em mesa de trabalho, status agregado de
        compliance (SAR pendentes, COAF deadline próximo, DeCripto
        mensal). Calculado em real-time via materialized views; cache
        de 60s no Redis. Use este endpoint para o card principal do
        dashboard — métricas detalhadas estão em `/dashboard/metrics`.
      tags: ["Plataforma — Painel"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/DateFrom"
        - $ref: "#/components/parameters/DateTo"
      responses:
        "200":
          description: Resumo agregado do painel para o período solicitado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardSummary"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /dashboard/metrics:
    get:
      operationId: getDashboardMetrics
      summary: Retorna métricas detalhadas do painel (papel analyst ou superior)
      description: |
        Versão detalhada das métricas do dashboard, com breakdown por:
        regra acionada (top 20), faixa de score (histograma), canal
        de origem (web/mobile/API), corredor geográfico (estado, país),
        hora do dia (heatmap 24×7). Inclui métricas operacionais para
        analistas: SLA da fila de revisão, tempo médio entre alerta e
        decisão, taxa de override de regra, false positive rate
        estimado. Requer papel `ANALYST`, `COMPLIANCE` ou superior;
        viewers não enxergam (`403`).
      tags: ["Plataforma — Painel"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/DateFrom"
        - $ref: "#/components/parameters/DateTo"
      responses:
        "200":
          description: Métricas detalhadas com breakdown por regra, faixa, canal, geografia e janela temporal
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardMetrics"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  # ──────────────────── Transactions ────────────────────
  /transactions:
    get:
      operationId: listTransactions
      summary: Lista transações com filtros e paginação
      description: |
        Retorna transações do tenant atual, paginadas, com filtros por
        período (`from`/`to`), status (`pending` / `approved` /
        `blocked` / `review`) e nível de risco (`low` / `medium` /
        `high` / `critical`). Os resultados vêm ordenados por timestamp
        decrescente. Páginas grandes (>100) são cap-limitadas pelo
        servidor (`page_size` máximo 200). Use os links de paginação
        retornados no header `Link` para iteração estável.
      tags: ["Transações — Histórico"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - $ref: "#/components/parameters/DateFrom"
        - $ref: "#/components/parameters/DateTo"
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, approved, blocked, review]
        - name: risk_level
          in: query
          schema:
            type: string
            enum: [low, medium, high, critical]
      responses:
        "200":
          description: Lista paginada de transações filtradas
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedTransactions"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /transactions/{id}:
    get:
      operationId: getTransaction
      summary: Retorna transação por ID
      description: |
        Detalhe completo de uma transação: payload original, decisão
        final, score 5-D breakdown (sentinexprint, payload, geo,
        comportamental, contextual), regras acionadas com peso, alertas
        gerados, links para alertas/casos correlacionados, audit trail
        (quem aprovou/revisou/escalonou). Retorna `404` se ID não
        pertence ao tenant atual (defesa contra enumeração cross-tenant).
      tags: ["Transações — Histórico"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Detalhe completo da transação (payload + score + regras + audit)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Transaction"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /transactions/analyze:
    post:
      operationId: analyzeTransaction
      summary: Submete transação para análise de risco em tempo real
      description: |
        Endpoint principal de KYT: recebe o payload da transação e
        retorna decisão (`APPROVE` / `REVIEW` / `REJECT`) em <100ms p99
        (SLA contratual). Pipeline interno: SentinexPrint
        (fingerprint), enrichment (geo + bureau + watchlist), scoring
        em 5 dimensões, Grule rules engine (hot-reloadable), persistência.
        A transação é registrada com `transaction_id` retornado, que
        pode ser usado para consultar em `/transactions/{id}` e
        `/transactions/{id}/explain`. Rate limit: por tenant, default
        100 RPS para tier Starter, escala em planos superiores.
      tags: ["Transações — Histórico"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnalyzeRequest"
      responses:
        "200":
          description: Resultado da análise de risco com decisão e breakdown
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnalysisResult"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /transactions/{id}/review:
    post:
      operationId: reviewTransaction
      summary: Registra decisão manual de revisão (papel analyst ou superior)
      description: |
        Encerra um caso em revisão manual com decisão do analista:
        `APPROVE` (override de regra que bloqueou), `BLOCK` (escalona
        para suspeição manual) ou `ESCALATE` (envia para mesa de
        compliance/SAR). Obrigatório incluir `justification` (audit
        trail). A decisão é imutável após persistida; retificações
        exigem caso novo. Trigger automático: se decisão = `BLOCK` e
        valor > R$100k OU acumulado 7 dias > R$500k, dispara workflow
        SAR COAF.
      tags: ["Transações — Histórico"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReviewRequest"
      responses:
        "200":
          description: Decisão de revisão registrada (imutável a partir deste ponto)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReviewResponse"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /transactions/{id}/explain:
    get:
      operationId: explainTransaction
      summary: Retorna breakdown explicável do score de risco da transação
      description: |
        Diferencial Sentinex (vs Sardine, Feedzai): explicabilidade
        completa. Retorna decomposição do score final em 5 dimensões
        (cada uma com sub-fatores), lista de regras acionadas com
        peso individual, contrafactuais (`o que mudaria a decisão se X
        fosse diferente`), evidências on-chain (se transação crypto),
        e fatores contextuais (horário, geolocalização, padrão histórico
        do cliente). Use para tela de detalhe + para responder
        questionamentos LGPD do titular (decisão automatizada).
      tags: ["Sentinex — Explicabilidade"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Breakdown explicável do score com dimensões, regras e contrafactuais
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExplainResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Clients ────────────────────
  /clients:
    get:
      operationId: listClients
      summary: Lista clientes com filtros e paginação
      description: |
        Retorna clientes do tenant atual com filtro por risk tier
        BCB Res. 520 (`GREEN` baixo / `AMBER` médio / `RED` alto) e
        busca textual (nome ou identifier hashed). Identifiers (CPF,
        CNPJ, e-mail) nunca aparecem em claro nas listagens — somente
        o hash SHA-256. Use search por hash quando o cliente já provou
        conhecer o identifier original.
      tags: ["Identidade — Clientes"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - name: risk_tier
          in: query
          schema:
            type: string
            enum: [GREEN, AMBER, RED]
        - name: search
          in: query
          schema:
            type: string
          description: Busca por nome ou identifier hashed (SHA-256)
      responses:
        "200":
          description: Lista paginada de clientes (identifiers sempre hashed)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedClients"
        "401":
          $ref: "#/components/responses/Unauthorized"

    post:
      operationId: createClient
      summary: Cria novo cliente (papel compliance)
      description: |
        Onboarding manual de cliente PF/PJ. Dispara pipeline KYC
        completo: validação documental + screening em listas de
        sanções (OFAC, ONU, COAF, UE, FinCEN) + bureau brasileiro +
        risk tiering automático (GREEN/AMBER/RED conforme BCB Res.
        520 Art. 58-59). Identifiers (CPF/CNPJ/e-mail) são
        persistidos sempre como SHA-256 — payload original é
        sanitizado antes do log. Em caso de match em sanção,
        cliente é criado com status `BLOCKED` e gera alerta crítico
        automático.
      tags: ["Identidade — Clientes"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateClientRequest"
      responses:
        "201":
          description: Cliente criado; pipeline KYC iniciado em background
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Client"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /clients/{id}:
    get:
      operationId: getClient
      summary: Retorna cliente por ID
      description: |
        Detalhe completo do cliente: dados KYC (sempre hashed),
        histórico de transações (count + volume agregado), alertas
        ativos, status de screening em listas de sanções, audit
        trail completo (LGPD Art. 18). Os identifiers em claro NUNCA
        são retornados por este endpoint — para acessar use
        `/dpo/data-requests` (LGPD-compliant com motivo registrado).
      tags: ["Identidade — Clientes"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Detalhe do cliente (identifiers hashed)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Client"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

    put:
      operationId: updateClient
      summary: Atualiza dados do cliente (papel compliance)
      description: |
        Atualiza campos não-imutáveis do cliente (nome social,
        endereço atualizado, segmento, observações de compliance).
        Identifiers core (CPF, CNPJ) NÃO podem ser alterados via
        este endpoint — só por fluxo DPO. Cada update incrementa
        versão e registra no audit trail (who/when/which fields).
        Triggers automáticos: alteração de endereço dispara
        recálculo de risk tier; alteração de segmento dispara
        re-screening em watchlists relevantes.
      tags: ["Identidade — Clientes"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateClientRequest"
      responses:
        "200":
          description: Cliente atualizado; audit trail registrado; triggers de re-screening disparados
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Client"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /clients/{id}/block:
    post:
      operationId: blockClient
      summary: Bloqueia cliente (papel tenant admin)
      description: |
        Ação operacional: marca cliente como `BLOCKED` e impede
        novas transações. Não remove dados (LGPD Art. 16: retenção
        obrigatória 5 anos para compliance financeiro mesmo após
        bloqueio). `reason` é obrigatório e registrado no audit
        trail — usado em response a auditoria BCB/COAF. Bloqueio
        dispara workflow: alertas abertos do cliente viram
        `BLOCKED_PENDING_REVIEW`, casos em andamento são congelados
        aguardando decisão final.
      tags: ["Identidade — Clientes"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
      responses:
        "200":
          description: Cliente bloqueado; novas transações rejeitadas a partir deste ponto
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Alerts ────────────────────
  /alerts:
    get:
      operationId: listAlerts
      summary: Lista alertas com filtros e paginação
      description: |
        Retorna alertas do tenant atual com filtro por status
        (`open` / `assigned` / `resolved` / `escalated`) e severidade
        (`low` / `medium` / `high` / `critical`). Alertas têm SLA de
        triagem configurável por severidade (default: critical=15min,
        high=1h, medium=4h, low=24h). Use este endpoint para popular
        a fila de mesa de trabalho do analista. Para detalhe + ação,
        use `/alerts/{id}` e endpoints derivados.
      tags: ["Operações — Alertas"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - name: status
          in: query
          schema:
            type: string
            enum: [open, assigned, resolved, escalated]
        - name: severity
          in: query
          schema:
            type: string
            enum: [low, medium, high, critical]
      responses:
        "200":
          description: Lista paginada de alertas filtrados por status e severidade
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedAlerts"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /alerts/{id}:
    get:
      operationId: getAlert
      summary: Retorna alerta por ID
      description: |
        Detalhe completo do alerta: contexto disparador (regra
        acionada + transação ou cliente origem), severity, SLA
        countdown, assignee atual, audit trail de ações (assign,
        unassign, comment, escalate), evidências anexadas, casos
        correlacionados. Em alertas decorrentes de match em sanção,
        inclui referência ao item da lista (OFAC SDN list + data da
        sanção + jurisdição).
      tags: ["Operações — Alertas"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Detalhe completo do alerta com contexto, evidências e audit trail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Alert"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /alerts/{id}/assign:
    post:
      operationId: assignAlert
      summary: Atribui alerta a um analista (papel analyst ou superior)
      description: |
        Atribui alerta para um usuário específico do tenant (campo
        `assignee_id`). Pode auto-atribuir (`assignee_id` = próprio
        user). Defesa contra ping-pong: 3 reatribuições consecutivas
        sem ação automaticamente escalonam para compliance. SLA
        countdown não reseta com reatribuição — começa a contar do
        primeiro disparo. Atribuir já-atribuído sobrescreve o
        assignee anterior e registra no audit trail.
      tags: ["Operações — Alertas"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assignee_id]
              properties:
                assignee_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Alerta atribuído; assignee_id atualizado no audit trail
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /alerts/{id}/resolve:
    post:
      operationId: resolveAlert
      summary: Resolve alerta com decisão (papel analyst ou superior)
      description: |
        Encerra alerta com classificação: `true_positive` (alerta
        legítimo, fraude/lavagem confirmada), `false_positive`
        (falso alarme, alimenta tuning das regras Grule),
        `inconclusive` (sem evidência suficiente, cliente fica em
        watchlist interna). `notes` obrigatório para audit trail.
        Trigger automático: 10+ false_positive em 30 dias na mesma
        regra dispara ticket de tuning para o time de risco.
        Resolução é imutável após persistida.
      tags: ["Operações — Alertas"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [resolution, notes]
              properties:
                resolution:
                  type: string
                  enum: [true_positive, false_positive, inconclusive]
                notes:
                  type: string
      responses:
        "200":
          description: Alerta resolvido; classificação imutável; feedback alimenta tuning de regras
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /alerts/{id}/escalate:
    post:
      operationId: escalateAlert
      summary: Escalona alerta para compliance (papel compliance)
      description: |
        Escala alerta da mesa operacional para compliance/SAR.
        Dispara workflow específico: cria caso compliance,
        bloqueia transações do cliente correlacionado por default
        até decisão, registra timer COAF (24h para SAR se
        confirmar suspeição). `priority=critical` notifica DPO
        imediatamente via webhook Slack + email. Geralmente usado
        quando analista identifica padrão de lavagem (smurfing,
        layering, mixer pattern) que excede competência da mesa.
      tags: ["Operações — Alertas"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                priority:
                  type: string
                  enum: [normal, urgent, critical]
      responses:
        "200":
          description: Alerta escalonado para compliance; workflow SAR iniciado se aplicável
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Wallets ────────────────────
  /wallets:
    get:
      operationId: listWallets
      summary: Lista carteiras conhecidas com filtros
      description: |
        Retorna carteiras on-chain catalogadas pelo tenant atual:
        clustering proprietário Sentinex (não compra Chainalysis/TRM),
        atribuição a entidades conhecidas (exchanges, mixers, gambling,
        sanctioned), histórico de exposição agregado. Filtros por
        blockchain (Bitcoin, Ethereum, Tron, Solana, Polygon) e nível
        de risco. Endereços completos sempre retornados (são públicos
        on-chain) mas qualquer link com cliente PF/PJ é hashed.
      tags: ["Carteiras — Análise"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - name: blockchain
          in: query
          schema:
            type: string
            enum: [bitcoin, ethereum, tron, solana, polygon]
        - name: risk_level
          in: query
          schema:
            type: string
            enum: [low, medium, high, critical]
      responses:
        "200":
          description: Lista paginada de carteiras filtradas por blockchain e nível de risco
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedWallets"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /wallets/{id}:
    get:
      operationId: getWallet
      summary: Retorna carteira por ID
      description: |
        Detalhe completo: endereço, blockchain, primeira/última
        atividade observada, saldo atual, score de risco com
        breakdown (exposição direta a sanção, exposição indireta a
        N hops, idade do cluster, padrões típicos detectados),
        labels conhecidos (Binance hot wallet, Tornado Cash, etc),
        contraparts mais frequentes (top 20), proveniência inferida
        (faucet/mining/exchange/p2p). Use para investigação manual
        de carteira específica.
      tags: ["Carteiras — Análise"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Detalhe completo da carteira com clustering + labels + score breakdown
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Wallet"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /wallets/screen:
    post:
      operationId: screenWallet
      summary: Triagem de endereço de carteira contra bases de risco
      description: |
        Endpoint sob demanda: recebe endereço + blockchain e
        retorna decisão de risco em <500ms. Pipeline interno:
        normalização de endereço (checksum EIP-55 para Ethereum
        etc), busca em watchlists (OFAC SDN, ONU, COAF, listas
        internas), screening em clustering proprietário (mixers,
        sanctioned, gambling, darknet markets), exposição a N hops
        configurável. Decisão final + breakdown explicável. Use
        antes de aceitar depósito ou autorizar saque.
      tags: ["Carteiras — Análise"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WalletScreenRequest"
      responses:
        "200":
          description: Resultado de screening com decisão e breakdown de exposição
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletScreenResponse"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /wallets/{id}/blocklist:
    post:
      operationId: blocklistWallet
      summary: Adiciona carteira à blocklist (papel compliance)
      description: |
        Adiciona endereço à blocklist interna do tenant. Origem
        pode ser inferida de match em sanção (OFAC/COAF) ou
        decisão manual de compliance (`internal`). `reason`
        obrigatório registra contexto para audit BCB. Após
        blocklist: (a) novas transações envolvendo o endereço são
        bloqueadas em tempo real, (b) transações históricas geram
        alertas retroativos para revisão, (c) clientes com
        exposição ao endereço passam por re-screening.
      tags: ["Carteiras — Análise"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                source:
                  type: string
                  description: "Origem do bloqueio (e.g. OFAC, COAF, internal)"
      responses:
        "200":
          description: Carteira em blocklist; bloqueio retroativo + re-screening de clientes expostos disparados
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /wallets/{id}/transactions:
    get:
      operationId: getWalletTransactions
      summary: Lista transações de uma carteira específica
      description: |
        Retorna transações on-chain envolvendo a carteira (in
        + out), enriquecidas com atribuição da contraparte (se
        conhecida) e decisão Sentinex se a transação passou pelo
        tenant atual. Útil para investigação de padrão (smurfing,
        peeling chain, mixer in/out, sweep). Para análise de
        cluster completo (todos os endereços relacionados), use
        endpoint `/wallets/{id}` que retorna o cluster_id.
      tags: ["Carteiras — Análise"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Lista paginada de transações on-chain da carteira (in + out + atribuição)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedTransactions"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Rules ────────────────────
  /rules:
    get:
      operationId: listRules
      summary: Lista todas as regras de risco do tenant
      description: |
        Retorna regras ativas no Grule engine para o tenant atual:
        regras base (R-001..R-037 antifraude tradicional), regras
        crypto-específicas (R-CRYPTO-01..R-CRYPTO-12 — pig
        butchering, drainer, SIM swap, etc), regras avançadas
        (R-ADV-01..R-ADV-24 — typology pattern matching).
        Cada regra inclui: descrição, peso no score final, status
        (active/shadow/disabled), versão, autor, métricas
        agregadas (FP rate, TP count nos últimos 30d).
      tags: ["Plataforma — Regras"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de regras com versão, peso, status e métricas agregadas
          content:
            application/json:
              schema:
                type: object
                properties:
                  rules:
                    type: array
                    items:
                      $ref: "#/components/schemas/Rule"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rules/{id}:
    get:
      operationId: getRule
      summary: Retorna regra por ID
      description: |
        Detalhe completo: DRL source (Grule rule definition
        language), histórico de versões (each commit), métricas
        de execução (trigger rate, FP rate, TP count, average
        latency contribution), exemplos de transações que
        recentemente acionaram a regra (top 20 anonimizados),
        regras correlatas (que tendem a acionar junto).
      tags: ["Plataforma — Regras"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Detalhe da regra com DRL source, métricas e exemplos
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rule"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /rules/{id}/shadow:
    post:
      operationId: toggleRuleShadow
      summary: Alterna modo shadow de uma regra (papel tenant admin)
      description: |
        Toggle entre modo `shadow` (regra dispara mas não afeta
        decisão final — apenas log para análise) e `enforce`
        (regra impacta decisão normalmente). Use shadow para
        testar regras novas em produção sem risco operacional —
        coleta FP/TP rate por N dias, valida que threshold está
        bem calibrado, depois promove para enforce. Toggle é
        hot-reloadable (não precisa restart). Mudança fica
        registrada no audit trail com timestamp + user_id.
      tags: ["Plataforma — Regras"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [shadow]
              properties:
                shadow:
                  type: boolean
                  description: "true = shadow mode (log only), false = enforce (impacta decisão)"
      responses:
        "200":
          description: Modo shadow alternado; mudança hot-reload, sem restart
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Fingerprint ────────────────────
  /fingerprint/collect:
    post:
      operationId: collectFingerprint
      summary: Coleta dados de fingerprint do dispositivo (SentinexPrint 7-layer)
      description: |
        Endpoint chamado pelo client (web/mobile/API) para registrar
        fingerprint do dispositivo. SentinexPrint é proprietário (não
        compra FingerprintJS) e coleta 7 camadas: browser (UA, canvas,
        WebGL, audio context), hardware (CPU cores, screen, timezone),
        network (IP geo, ASN, VPN/Tor heuristics), behavioral (digital
        pattern do form fill, click cadence), TLS fingerprint (JA3/JA4),
        font fingerprint, plugin fingerprint. Resultado: device_id
        UUID + risk_score + flag is_known_device.
      tags: ["Sentinex — Fingerprint"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FingerprintPayload"
      responses:
        "200":
          description: Fingerprint collected and device ID returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id:
                    type: string
                    format: uuid
                  risk_score:
                    type: number
                    format: float
                  is_known_device:
                    type: boolean
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /fingerprint/device/{id}:
    get:
      operationId: getDevice
      summary: Retorna detalhes do fingerprint de um device
      description: |
        Retorna histórico do device_id: primeira/última observação,
        contagem de transações associadas, lista de IPs de origem (com
        ASN), flags suspeitos (rotação rápida de cookies, mismatch
        timezone vs IP geo, UA forjada), score atual e tendência.
        Use para investigação manual de cliente — device com 100+
        transações em 24h ou rotando 10+ IPs/dia é sinal forte.
      tags: ["Sentinex — Fingerprint"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Device fingerprint details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceInfo"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Cases ────────────────────
  /cases:
    get:
      operationId: listCases
      summary: Lista casos de investigação
      description: |
        Casos são unidades de trabalho de compliance: agrupam alertas
        relacionados (mesmo cliente, mesma typology, mesma janela
        temporal) + evidências coletadas + audit trail completo. Use
        este endpoint para popular a mesa de trabalho de compliance.
        Casos sempre auditáveis para responses BCB/COAF (audit chain
        imutável via hash encadeado).
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - name: status
          in: query
          schema:
            type: string
            enum: [open, in_progress, resolved, escalated, closed]
      responses:
        "200":
          description: Paginated case list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedCases"
        "401":
          $ref: "#/components/responses/Unauthorized"

    post:
      operationId: createCase
      summary: Cria caso de investigação (papel analyst ou superior)
      description: |
        Caso típico criado manualmente pelo analista quando padrão
        observado em alertas excede competência da mesa operacional.
        Auto-attach: alertas correlacionados (mesma typology + janela)
        são puxados automaticamente para o caso. Caso é versionado
        (cada modificação cria revisão). Em escalada para COAF, o
        caso vira anexo do SAR. Não-imutável até resolve().
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCaseRequest"
      responses:
        "201":
          description: Case created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Case"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /cases/{id}:
    get:
      operationId: getCase
      summary: Retorna caso por ID
      description: |
        Detalhe completo: dados do caso, lista de alertas correlatos,
        evidências coletadas (anexos S3 keys + hash SHA-256), histórico
        de comentários, audit trail de status, assignee atual,
        deadlines (SAR COAF se aplicável). Inclui flag de
        chain-of-custody integrity (cada evidência tem hash assinado;
        violação invalida o caso para uso em SAR).
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Case details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Case"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /cases/{id}/comment:
    post:
      operationId: addCaseComment
      summary: Adiciona comentário ao caso (papel analyst ou superior)
      description: |
        Comentários são imutáveis após criados (registros de audit
        trail). Permitem anexos via reference IDs (storage S3 com
        hash SHA-256 validado). Markdown limitado suportado (sem
        HTML/scripts). Mentions @user disparam notificação. Usado
        para registrar progresso de investigação, achados parciais,
        decisões intermediárias.
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
                attachments:
                  type: array
                  items:
                    type: string
                    description: Attachment reference IDs
      responses:
        "201":
          description: Comment added
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /cases/{id}/assign:
    post:
      operationId: assignCase
      summary: Atribui caso ao analista (papel compliance)
      description: |
        Atribuição é registrada no audit trail (who/when/why).
        Reatribuições são permitidas mas não resetam SLA timers
        (deadline COAF de 24h é absoluto, conta do trigger inicial).
        Auto-notify: assignee novo recebe email + entry no inbox.
        Devolução é permitida (assignee pode unassign solicitando
        revisão por outro com `reason`).
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assignee_id]
              properties:
                assignee_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Case assigned
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /cases/{id}/resolve:
    post:
      operationId: resolveCase
      summary: Resolve caso com findings (papel compliance)
      description: |
        Encerra o caso com classificação final: `confirmed_fraud`
        (suspeição confirmada — dispara SAR COAF se ainda dentro de
        janela 24h), `false_positive` (não-suspeito, alimenta tuning
        de regras), `inconclusive` (sem evidência suficiente — cliente
        permanece em watchlist por 90d), `referred_to_coaf` (SAR já
        em fila externa). `findings` (texto longo) é obrigatório
        e vira parte do SAR/audit. Resolução é imutável.
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [resolution, findings]
              properties:
                resolution:
                  type: string
                  enum: [confirmed_fraud, false_positive, inconclusive, referred_to_coaf]
                findings:
                  type: string
      responses:
        "200":
          description: Case resolved
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /cases/{id}/escalate:
    post:
      operationId: escalateCase
      summary: Escalona caso (papel compliance)
      description: |
        Target define destino: `compliance_lead` (encaminhamento
        interno para liderança quando excede competência do
        compliance officer), `coaf` (submissão SAR COAF — dispara
        workflow de geração de XML + assinatura ICP-Brasil), ou
        `law_enforcement` (ordens judiciais já comunicadas — atualiza
        status e prepara pacote forense). `reason` obrigatório vira
        parte do audit. Em escalada para COAF, prazo 24h conta dos
        gatilhos automáticos (>R$100k transação OU >R$500k/7d).
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                target:
                  type: string
                  enum: [compliance_lead, coaf, law_enforcement]
      responses:
        "200":
          description: Case escalated
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /cases/{id}/timeline:
    get:
      operationId: getCaseTimeline
      summary: Retorna timeline auditável do caso
      description: |
        Lista cronológica completa de todos os eventos do caso:
        criação, atribuições, comentários, anexos, mudanças de status,
        escaladas, resolução. Cada evento tem hash encadeado com o
        anterior (audit chain integrity LGPD-compatível). Use para
        produzir relatório forense quando solicitado por BCB/COAF/
        autoridade judicial — o JSON retornado pode ser anexado
        diretamente ao SAR ou packaging.
      tags: ["Operações — Casos"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Timeline events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      $ref: "#/components/schemas/TimelineEvent"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Reports ────────────────────
  /reports:
    get:
      operationId: listReports
      summary: Lista relatórios de compliance

      description: |

        Lista relatórios regulatórios do tenant: SAR (COAF), DeCripto (RFB IN 2291/2025), BCB FX (stablecoins mensal), reportes customizados. Cada relatório tem status (draft/generated/submitted/acknowledged) e link para o XML/PDF gerado. Filtros por tipo e período.
      tags: ["Plataforma — Relatórios"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - name: type
          in: query
          schema:
            type: string
            enum: [sar, decripto, bcb_fx, coaf, custom]
      responses:
        "200":
          description: Paginated report list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedReports"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /reports/{id}:
    get:
      operationId: getReport
      summary: Retorna relatório por ID

      description: |

        Detalhe completo do relatório: payload, status, audit trail, hash de integridade, assinatura ICP-Brasil (se signed), referência externa (protocolo COAF, ID DeCripto), histórico de envios e respostas regulatórias.
      tags: ["Plataforma — Relatórios"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Report details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Report"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /reports/generate:
    post:
      operationId: generateReport
      summary: Gera relatório de compliance (papel compliance)

      description: |

        Pipeline async: agregação de dados do período + geração de XML/PDF conforme schema regulatório + assinatura digital ICP-Brasil (cert A1/A3) + persistência com hash. Tipos suportados: SAR (XML COAF Manual VII), DeCripto (XSD RFB), BCB FX (XML BCB). Geração demora 30-180s; retorno imediato com job_id para polling.
      tags: ["Plataforma — Relatórios"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GenerateReportRequest"
      responses:
        "202":
          description: Report generation started
          content:
            application/json:
              schema:
                type: object
                properties:
                  report_id:
                    type: string
                    format: uuid
                  status:
                    type: string
                    enum: [generating]
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /reports/{id}/submit:
    post:
      operationId: submitReport
      summary: Submete relatório ao órgão regulador (papel compliance)

      description: |

        Envia relatório assinado ao endpoint do regulador (COAF SISCOAF, RFB DeCripto API, BCB ESTRA). Idempotente: submissões duplicadas retornam mesmo protocol_id. Em sucesso, status muda para `submitted` e timer de SLA de acknowledgement inicia. Falhas de transporte são retentadas com backoff exponencial.
      tags: ["Plataforma — Relatórios"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                target:
                  type: string
                  enum: [coaf, receita_federal, bcb]
                notes:
                  type: string
      responses:
        "200":
          description: Report submitted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  # ──────────────────── Radar ────────────────────
  /radar/live:
    get:
      operationId: getRadarLive
      summary: Retorna feed de fraude em tempo real (Fraud Radar)

      description: |

        Diferencial Sentinex: feed live de transações sendo analisadas, decisões em tempo real, alertas disparados nos últimos 60s. WebSocket-friendly (também REST polling-friendly via cursor). Use para construir dashboards operacionais em tempo real e identificar bursts coordenados (e.g. ataque distribuído).
      tags: ["Sentinex — Radar"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Live radar data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RadarLive"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /radar/hotspots:
    get:
      operationId: getRadarHotspots
      summary: Retorna hotspots geográficos de fraude

      description: |

        Agregação geo de transações suspeitas/bloqueadas no período: por país, estado BR, ASN de origem. Inclui breakdown por typology (pig butchering, drainer, SIM swap, etc) e evolução temporal. Use para identificar corredores quentes em planos de tuning regional.
      tags: ["Sentinex — Radar"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Hotspot data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RadarHotspots"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────── Billing ────────────────────
  /billing/usage:
    get:
      operationId: getBillingUsage
      summary: Retorna uso de billing atual do tenant

      description: |

        Métricas do ciclo atual: transações analisadas, alertas gerados, KYT screens, KYC pulls externos, casos abertos, storage usado. Comparado com limites do plano contratado. Inclui projeção de overage (se trend atual continuar) e dias restantes até reset. Use para alertar Founder antes de hard-limit dispar.
      tags: ["Plataforma — Cobrança"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Usage data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingUsage"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /billing/plans:
    get:
      operationId: getBillingPlans
      summary: Lista planos de billing disponíveis

      description: |

        Catálogo dos 5 tiers Sentinex Risk: Starter (R$4.9k/mês — VASP pequeno), Pro (R$18.9k — médio), Scale (R$64.9k — grande), Enterprise (R$189k — multi-VASP), RegTech (R$350k+ — white-label). Cada plano lista limites de uso (TPS, alertas, KYT/KYC, storage) e features inclusas (Ficha 360°, Travel Rule, ML scoring, etc).
      tags: ["Plataforma — Cobrança"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Available plans
          content:
            application/json:
              schema:
                type: object
                properties:
                  plans:
                    type: array
                    items:
                      $ref: "#/components/schemas/BillingPlan"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /billing/change-plan:
    post:
      operationId: changeBillingPlan
      summary: Altera plano de billing (papel tenant admin)

      description: |

        Upgrade é instantâneo (novos limites aplicam imediato + cobrança proporcional do ciclo atual). Downgrade tem grace period 24h (admin pode reverter) e só aplica no próximo ciclo. Integração Stripe: dispara webhook customer.subscription.updated. Auditado em snx_billing_audit + email confirmation enviado.
      tags: ["Plataforma — Cobrança"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan_id]
              properties:
                plan_id:
                  type: string
      responses:
        "200":
          description: Plan changed
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /billing/portal:
    post:
      operationId: getBillingPortal
      summary: Retorna URL do Stripe Customer Portal (papel tenant admin)

      description: |

        URL signed com TTL 5min para o tenant gerenciar pagamento, downloads de fatura, atualizar cartão, ver histórico. Stripe Customer Portal é hosted pelo Stripe — não temos UI própria de billing. Após session no portal, mudanças vêm via webhook.
      tags: ["Plataforma — Cobrança"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Portal session URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
                    format: uri
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /billing/webhook:
    post:
      operationId: handleStripeWebhook
      summary: Endpoint de webhook do Stripe
      description: |
        Recebe eventos do Stripe: customer.subscription.{created,updated,deleted}, invoice.{paid,failed}, payment_intent.{succeeded,failed}. Valida assinatura HMAC com STRIPE_WEBHOOK_SECRET (header Stripe-Signature — não JWT). Idempotente via event_id (Redis SETNX 7d). Atualiza snx_tenants.plan_status + dispara workflows (e.g. invoice.failed → grace 7d → suspend).
      tags: ["Plataforma — Cobrança"]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Webhook processed
        "400":
          description: Invalid signature or payload

  # ──────────────────── Admin ────────────────────
  /admin/tenants:
    get:
      operationId: listTenants
      summary: Lista todos os tenants (papel super admin)

      description: |

        Visão SUPER_ADMIN agregada da plataforma: todos os tenants ativos, suspended, dunning, KYB-pending. Filtros por status, plano, região. Cada entrada: nome legal, CNPJ hashed, plano, MRR, status, último signin, alert count agregado. Use para dashboards executivos. SUPER_ADMIN NUNCA vê operação interna de tenant — para isso, impersonar.
      tags: ["Administração"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Tenant list
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenants:
                    type: array
                    items:
                      $ref: "#/components/schemas/Tenant"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

    post:
      operationId: createTenant
      summary: Cria novo tenant (papel super admin)

      description: |

        Cria tenant manualmente (caminho não-self-service — para sales-led onboarding). Recebe legal_name, CNPJ, plano inicial, contact_email. Diferente de /public/signup que faz wizard completo. Use SUPER_ADMIN para parceiros estratégicos sem signup público.
      tags: ["Administração"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateTenantRequest"
      responses:
        "201":
          description: Tenant created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Tenant"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /admin/tenants/{id}:
    get:
      operationId: getTenant
      summary: Retorna tenant por ID (papel super admin)

      description: |

        Detalhe admin do tenant: metadados, status, plano, billing health, audit trail. Equivale a /admin/tenants/{id} simplified — para SUPER_ADMIN consultar info administrativa sem entrar em operação.
      tags: ["Administração"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      responses:
        "200":
          description: Tenant details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Tenant"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /admin/tenants/{id}/modules:
    put:
      operationId: updateTenantModules
      summary: Atualiza em lote os módulos habilitados do tenant (super_admin)
      tags: ["Administração"]
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ResourceID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [modules]
              properties:
                modules:
                  type: array
                  items:
                    type: string
                    enum: [kyc, kyt, sanctions, travel_rule, coaf, decripto, bcb_fx, cases, radar, billing]
      responses:
        "200":
          description: Modules updated
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /admin/users:
    get:
      operationId: listAllUsers
      summary: Lista todos usuários cross-tenant (papel super admin)

      description: |

        Visão SUPER_ADMIN dos usuários: filtros por tenant, papel RBAC, status (ativo/suspenso/locked), MFA enabled. Não retorna password hash nem TOTP secret (LGPD + segurança). Use para auditoria cross-tenant de quem tem acesso a quê. Ações disponíveis: bloquear/desbloquear (lock investigação), forçar reset MFA (em caso de device perdido confirmado).
      tags: ["Administração"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: User list
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      $ref: "#/components/schemas/UserProfile"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

    post:
      operationId: createUser
      summary: Cria usuário em um tenant (papel super admin)

      description: |

        Criação manual de usuário em tenant existente — SUPER_ADMIN-only (geralmente para suporte/troubleshooting). Recebe email + papel RBAC + tenant_id. Dispara magic link de primeira senha. Audit trail registra quem do Sentinex criou + reason.
      tags: ["Administração"]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUserRequest"
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfile"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /admin/stats:
    get:
      operationId: getSystemStats
      summary: Retorna estatísticas do sistema (papel super admin)

      description: |

        Métricas técnicas globais: uptime backend/frontend, p99 latency, error rate cross-tenant, DB metrics (connections, slow queries), Redis cache hit ratio, Kafka outbox depth, jobs queue size, etc. Use para health monitoring SUPER_ADMIN e SRE.
      tags: ["Administração"]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: System stats
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SystemStats"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  # ──────────────────── Webhooks ────────────────────
  /webhooks/chargeback:
    post:
      operationId: handleChargeback
      summary: Receive chargeback notification
      tags: ["Plataforma — Webhooks"]
      security:
        - hmacAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChargebackEvent"
      responses:
        "200":
          description: Chargeback processed
        "401":
          description: Invalid HMAC signature

  /webhooks/blockchain:
    post:
      operationId: handleBlockchainEvent
      summary: Receive blockchain event notification
      tags: ["Plataforma — Webhooks"]
      security:
        - hmacAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BlockchainEvent"
      responses:
        "200":
          description: Event processed
        "401":
          description: Invalid HMAC signature

  # ──────────────────── Public — Signup self-service ────────────────────
  /public/signup:
    post:
      operationId: publicSignup
      tags: ["Plataforma — Autenticação"]
      summary: Cria tenant + first admin user (self-service onboarding)
      description: |
        Cadastro público sem autenticação. Cria atomicamente:

        - Tenant em estado TRIAL (14 dias, sem cartão)
        - Primeiro user com role TENANT_ADMIN
        - Módulos required ativados (mod_crypto_core, audit, rules, scoring, helpdesk)
        - API key sandbox (snxc_sand_*)

        Validações:
        - CNPJ formato válido (Mod11 checksum)
        - Email corporativo (provedores gratuitos opcionalmente bloqueados via SIGNUP_BLOCK_FREE_EMAILS)
        - Senha NIST 800-63B (mín 12 chars, letra+dígito, sem padrões comuns)
        - Aceites obrigatórios (terms + LGPD)
        - PSAVCategory enum (INTERMEDIARIA/CUSTODIANTE/CORRETORA/FINTECH/IGAMING/PSP/OUTRA)

        Response inclui API key sandbox em plaintext APENAS uma vez. Cliente
        deve copiar para uso imediato; rotação posterior via UI requer login.

        Email welcome assíncrono enviado via Resend após sucesso (não bloqueia).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [company_name, cnpj, psav_category, admin_name, admin_email, admin_password, accept_terms, accept_lgpd]
              properties:
                company_name:
                  type: string
                  minLength: 3
                  maxLength: 200
                  example: "Acme Tecnologia LTDA"
                cnpj:
                  type: string
                  pattern: "^[0-9]{2}\\.?[0-9]{3}\\.?[0-9]{3}/?[0-9]{4}-?[0-9]{2}$"
                  example: "11.222.333/0001-81"
                psav_category:
                  type: string
                  enum: [INTERMEDIARIA, CUSTODIANTE, CORRETORA, FINTECH, IGAMING, PSP, OUTRA]
                volume_band:
                  type: string
                  enum: [UNDER_1M, 1M_TO_10M, 10M_TO_100M, OVER_100M]
                  example: "1M_TO_10M"
                admin_name:
                  type: string
                  minLength: 3
                  example: "Maria Silva"
                admin_email:
                  type: string
                  format: email
                  example: "fin@acme.com.br"
                admin_phone:
                  type: string
                  example: "+55 11 99999-9999"
                admin_password:
                  type: string
                  minLength: 12
                  maxLength: 128
                  example: "MinhaS3nhaForte!Acme"
                accept_terms:
                  type: boolean
                  description: Obrigatório
                accept_lgpd:
                  type: boolean
                  description: Obrigatório
                opt_in_marketing:
                  type: boolean
                  default: false
      responses:
        "201":
          description: Tenant criado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  tenant_id: { type: string, format: uuid }
                  user_id: { type: string, format: uuid }
                  email: { type: string, format: email }
                  trial_ends_at: { type: string, format: date-time }
                  message: { type: string }
                  sandbox_api_key:
                    type: string
                    description: API key plaintext — copie e armazene; não é recuperável
                    example: "snxc_sand_a1b2c3d4e5f6..."
                  sandbox_api_key_prefix:
                    type: string
                    example: "snxc_sand_"
        "400":
          description: Validação falhou (invalid_cnpj, weak_password, terms_required, lgpd_required, etc)
        "409":
          description: Conta já existe (anti-enumeration — não diferencia email vs slug)

  # ──────────────────── Public — Module catalog ────────────────────
  /public/catalog:
    get:
      operationId: publicCatalogList
      tags: ["Plataforma — Módulos"]
      summary: Catálogo público de módulos vendáveis
      description: |
        Retorna módulos com is_public_catalog=true e is_active=true.
        Inclui pricing, billing model, included quota, dependencies,
        regulatory mapping (qual norma BR cada módulo atende).

        Endpoint sem autenticação — usado em página de pricing pré-signup.
      parameters:
        - name: category
          in: query
          schema:
            type: string
            enum: [core, identity, onchain, regulatory, compliance, fraud, forensics, intelligence, analytics, operations, commercial, support]
      responses:
        "200":
          description: Lista de módulos
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/CatalogEntry' }

  # ──────────────────── Modules — Tenant ────────────────────
  /admin/my/modules:
    get:
      operationId: myModulesList
      tags: ["Plataforma — Módulos"]
      summary: Módulos do tenant logado

      description: |

        Lista módulos visíveis para o tenant atual: ativados, em request pending, disponíveis para upgrade. Cada entrada inclui quotas atuais vs limites e status do request (se aplicável).
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Lista de TenantModule do tenant
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/TenantModule' }
        "401": { description: Token inválido ou tenant ausente }

  /admin/my/modules/usage:
    get:
      operationId: myModulesUsage
      tags: ["Plataforma — Módulos"]
      summary: Uso por módulo nos últimos 30 dias

      description: |

        Histórico de consumo dos últimos 30 dias por módulo, com daily breakdown. Use para mostrar trends de uso ao tenant + alertar antes de atingir hard-limit. Inclui projeção de overage no ciclo atual.
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Agregação 30d com overage
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        code: { type: string }
                        transactions_30d: { type: integer }
                        overage_qty: { type: integer }
                        overage_cents: { type: integer }

  /admin/my/modules/{code}/request-upgrade:
    post:
      operationId: myModulesRequestUpgrade
      tags: ["Plataforma — Módulos"]
      summary: Solicita ativação de módulo (workflow super_admin approval)

      description: |

        Cliente solicita ativação ou upgrade de quota. Cria entrada em snx_module_requests com status `pending`. Fica visível para SUPER_ADMIN em /admin/modules/requests/pending. Email + notificação Slack disparados ao Founder. Aprovação faz cobrança proporcional + ativação imediata.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: code
          in: path
          required: true
          schema: { type: string }
          example: "mod_crypto_kyt"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [justification]
              properties:
                justification:
                  type: string
                  minLength: 20
      responses:
        "200": { description: Pedido criado em status pending }
        "400": { description: Validação falhou (justification_min_20_chars) }
        "403": { description: "Módulo requer ativação super_admin (ex módulos internos)" }
        "404": { description: Módulo não existe no catálogo }

  # ──────────────────── Modules — Super Admin ────────────────────
  /admin/modules/catalog:
    get:
      operationId: adminModulesCatalog
      tags: ["Plataforma — Módulos"]
      summary: Catálogo completo (super_admin Sentinex)

      description: |

        Versão SUPER_ADMIN do catálogo: inclui módulos draft/internal não-públicos, métricas de adoption (tenants ativos por módulo), MRR contribuído por módulo, churn rate. Use para decisões de pricing/packaging e identificar módulos a depreciar.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: active
          in: query
          schema: { type: boolean, default: true }
        - name: addons_only
          in: query
          schema: { type: boolean }
        - name: category
          in: query
          schema: { type: string }
        - name: exclude_internal
          in: query
          schema: { type: boolean }
      responses:
        "200":
          description: Lista filtrada do catálogo
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/CatalogEntry' }

  /admin/modules/requests/pending:
    get:
      operationId: adminModulesRequestsPending
      tags: ["Plataforma — Módulos"]
      summary: Pedidos pendentes de aprovação (super_admin)

      description: |

        Fila FIFO de requests de módulos aguardando decisão SUPER_ADMIN. Cada entry: tenant solicitante, módulo, quota pedida vs atual, justification, MRR potencial. Ordenado por created_at ASC. Cada request tem SLA de 24h para resposta (escalada automática para Founder se exceder).
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Pedidos em status pending
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/TenantModule' }

  /admin/tenants/{tenantId}/modules:
    get:
      operationId: adminTenantModulesList
      tags: ["Plataforma — Módulos"]
      summary: Lista módulos de um tenant (super_admin)

      description: |

        Visão SUPER_ADMIN dos módulos de um tenant específico: ativos, suspended, history de mudanças. Use antes de processar request de upgrade para entender contexto do tenant. NÃO entra em operação interna (alertas/casos/transações).
      security: [{ bearerAuth: [] }]
      parameters:
        - name: tenantId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Vínculos do tenant com module catalog
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/TenantModule' }

  /admin/tenants/{tenantId}/modules/{code}/activate:
    post:
      operationId: adminTenantModuleActivate
      tags: ["Plataforma — Módulos"]
      summary: Ativa módulo para tenant (super_admin + freshMFA)

      description: |

        Operação sensitiva: ativa módulo para tenant em janela específica. Requer fresh MFA (TOTP nos últimos 5min) — defesa contra session hijacking. Audit trail completo (who/when/which_module/which_tenant/reason). Billing ajustado proporcional ao ciclo. Email + Slack disparam para Founder e tenant admin.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: tenantId
          in: path
          required: true
          schema: { type: string, format: uuid }
        - name: code
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string, minLength: 10 }
                trial_days:
                  type: integer
                  minimum: 0
                  maximum: 90
                  description: Se > 0, cria trial; senão ativação definitiva
                quota_override:
                  type: integer
                  description: Sobrescreve included_quota do catalog
                notes: { type: string }
      responses:
        "200": { description: "Módulo ativado (trial iniciado se trial_days maior que 0)" }
        "404": { description: "Módulo não existe no catálogo" }
        "422": { description: "Dependências não satisfeitas (missing_dependencies)" }

  /admin/tenants/{tenantId}/modules/{code}/deactivate:
    post:
      operationId: adminTenantModuleDeactivate
      tags: ["Plataforma — Módulos"]
      summary: Desativa módulo (corte imediato, super_admin + freshMFA)

      description: |

        Desativação imediata do módulo para o tenant. Requer fresh MFA. Se módulo tem dados ativos (e.g. casos abertos em /cases), valida que não há lock antes de aplicar. Billing cessa imediato — não há proporcional para downgrade (créditos só no próximo ciclo). Use com cautela: ação operacionalmente disruptiva.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: tenantId
          in: path
          required: true
          schema: { type: string, format: uuid }
        - name: code
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string, minLength: 10 }
      responses:
        "200": { description: Módulo desativado }
        "404": { description: Vínculo não encontrado ou já inativo }

  /admin/tenants/{tenantId}/modules/{code}/quota:
    patch:
      operationId: adminTenantModuleQuotaPatch
      tags: ["Plataforma — Módulos"]
      summary: Atualiza quota override (super_admin + freshMFA)

      description: |

        Aumenta/diminui limite específico de um módulo para um tenant (override do plano base). Use para clientes enterprise com necessidades especiais (e.g. burst legítimo de 10x TPS durante Black Friday). Requer fresh MFA. Override é por janela temporal (start/end) — após expira, volta ao limite do plano.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: tenantId
          in: path
          required: true
          schema: { type: string, format: uuid }
        - name: code
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                quota_override:
                  type: integer
                  nullable: true
                  description: null para voltar ao included_quota do catalog
                quota_reset_at:
                  type: string
                  format: date-time
      responses:
        "200": { description: Quota atualizada }
        "404": { description: Vínculo não encontrado }

  /admin/tenants/{tenantId}/modules/{code}/reject:
    post:
      operationId: adminTenantModuleReject
      tags: ["Plataforma — Módulos"]
      summary: Rejeita pedido pending (super_admin + freshMFA)

      description: |

        Rejeita request de módulo. Requer fresh MFA + reason (texto). Tenant recebe email com motivo. Não cria charge. Audit trail registra rejeição. Diferente de aprovar+cancel (que entrega temporariamente). Use quando upgrade não é compatível com perfil de risco do tenant.
      security: [{ bearerAuth: [] }]
      parameters:
        - name: tenantId
          in: path
          required: true
          schema: { type: string, format: uuid }
        - name: code
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string, minLength: 10 }
      responses:
        "200": { description: Pedido rejeitado }
        "404": { description: Pedido não está em status pending }

  # ─── Escalations chain (Sprint 2.5 P0 Açoriana V2 / B9 Wave 3) ─────────────
  /escalations/to-me:
    get:
      tags: [Escalations]
      summary: Lista escalations pendentes para o user logado
      description: |
        Chain imutável append-only sobre alerts+cases (mig 172). Filtra
        event_type='escalation' + tenant RLS. RBAC AnalystOrAbove.
      security: [{ bearerAuth: [] }]
      parameters:
        - in: query
          name: limit
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
      responses:
        "200":
          description: Lista de escalations
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  items:
                    type: array
                    items: { $ref: "#/components/schemas/EscalationEvent" }

  /escalations/by-me:
    get:
      tags: [Escalations]
      summary: Lista escalations criadas pelo user logado
      security: [{ bearerAuth: [] }]
      parameters:
        - in: query
          name: limit
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
      responses:
        "200": { description: Lista }

  /escalations/timeline/{resource_type}/{id}:
    get:
      tags: [Escalations]
      summary: Timeline completa de escalations sobre um recurso
      security: [{ bearerAuth: [] }]
      parameters:
        - in: path
          name: resource_type
          required: true
          schema: { type: string, enum: [alert, case] }
        - in: path
          name: id
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200": { description: Timeline chronological }

  /escalations:
    post:
      tags: [Escalations]
      summary: Cria escalation (RequireCompliance + freshMFA + PII detect)
      security: [{ bearerAuth: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [resource_type, resource_id, escalated_to_user_id, reason]
              properties:
                resource_type: { type: string, enum: [alert, case] }
                resource_id: { type: string, format: uuid }
                escalated_to_user_id: { type: string, format: uuid }
                reason: { type: string, minLength: 10 }
                notes: { type: string }
                severity_after: { type: string, enum: [low, medium, high, critical] }
      responses:
        "200": { description: Escalation criada com id }
        "400": { description: Validação falhou (PII, self-escalation, target inválido) }
        "403": { description: Cross-tenant target ou inactive target }

  /escalations/{id}/acknowledge:
    post:
      tags: [Escalations]
      summary: Destinatário acknowledge escalation
      security: [{ bearerAuth: [] }]
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                notes: { type: string }
      responses:
        "200": { description: Acknowledge persistido }
        "403": { description: Apenas destinatário pode acknowledge }
        "404": { description: Escalation not found (inclui cross-tenant IDOR) }

  /escalations/{id}/reject:
    post:
      tags: [Escalations]
      summary: Reverse-escalate (B9 — Sofia journey CX gap, Wave 3 2026-05-19)
      description: |
        Destinatário Y devolve escalation para originante X com justificativa.
        Gera nova row event_type='rejected' com from/to invertidos e
        parent_escalation_id preservado (chain integrity). PII bloqueado em
        reason e notes (DPIA-001 R-001 defense-in-depth).
      security: [{ bearerAuth: [] }]
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string, minLength: 10, description: "Justificativa devolução (sem PII)" }
                notes: { type: string }
      responses:
        "200": { description: Reverse-escalate persistido }
        "400": { description: Reason curto OU PII detectada }
        "403": { description: Caller não é destinatário do parent }
        "404": { description: Parent escalation not found (inclui cross-tenant IDOR) }

  # ─── LGPD (Lei 13.709/2018 Art. 18) — corrigido B8 Wave 4 ──────────────────
  /lgpd/me:
    get:
      tags: [LGPD]
      summary: Confirmação de tratamento (Art. 18 II)
      security: [{ bearerAuth: [] }]
      parameters:
        - in: query
          name: id
          description: UUID do client (operator-only, AnalystOrAbove)
          schema: { type: string, format: uuid }
      responses:
        "200": { description: Confirmação + data_categories }
        "404": { description: No record on file }

  /lgpd/me/export:
    get:
      tags: [LGPD]
      summary: Portabilidade dos dados (Art. 18 V) — requer freshMFA
      description: |
        Retorna JSON allowlist (DTO B4 Wave 1) com dados do titular. Campos
        sensíveis em SHA-256. format_version=1.2. Pode incluir bloco external_kyc
        ou flag `external_kyc.available=false` quando query falhar.
      security: [{ bearerAuth: [] }]
      parameters:
        - in: query
          name: id
          description: UUID do client (operator-only)
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: JSON export attachment
          content:
            application/json:
              schema: { type: object }

  /lgpd/me/erase:
    post:
      tags: [LGPD]
      summary: Solicitar exclusão (Art. 18 IV) — requer freshMFA
      description: |
        NÃO apaga imediatamente. Marca pending_erasure respeitando retenção
        legal (BCB Res 520: 5 anos PJ regulada). DPO + cliente notificados.
        Audit log registra user_id do operador (B5 audit trail).
      security: [{ bearerAuth: [] }]
      parameters:
        - in: query
          name: id
          description: UUID do client (operator-only)
          schema: { type: string, format: uuid }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                justification: { type: string }
      responses:
        "200": { description: Exclusão registrada em fila }

# ════════════════════════════════════════════════════════
# Components
# ════════════════════════════════════════════════════════
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: RS256 JWT token obtained via /auth/login + /auth/mfa/verify
    hmacAuth:
      type: apiKey
      in: header
      name: X-Signature-256
      description: HMAC-SHA256 signature of the request body

  parameters:
    ResourceID:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid

    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1

    PageSize:
      name: page_size
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

    DateFrom:
      name: from
      in: query
      schema:
        type: string
        format: date-time

    DateTo:
      name: to
      in: query
      schema:
        type: string
        format: date-time

  responses:
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    Unauthorized:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

  schemas:
    # ──── Escalations (mig 172 + B9 Wave 3) ────
    EscalationEvent:
      type: object
      required: [id, tenant_id, resource_type, resource_id, escalated_by_user_id, escalated_to_user_id, reason, event_type, created_at]
      properties:
        id: { type: string, format: uuid }
        tenant_id: { type: string, format: uuid }
        resource_type: { type: string, enum: [alert, case] }
        resource_id: { type: string, format: uuid }
        escalated_by_user_id: { type: string, format: uuid }
        escalated_to_user_id: { type: string, format: uuid }
        reason: { type: string }
        notes: { type: string }
        event_type:
          type: string
          enum: [escalation, acceptance, resolution, reassignment, rejected]
          description: "rejected = reverse-escalate (B9 Wave 3 2026-05-19)"
        severity_before: { type: string }
        severity_after: { type: string }
        parent_escalation_id: { type: string, format: uuid, nullable: true }
        created_at: { type: string, format: date-time }

    # ──── Common ────
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
        code:
          type: string
        details:
          type: object

    # ──── Modules architecture (mig 096-098) ────
    CatalogEntry:
      type: object
      description: |
        Linha de snx_module_catalog. Fonte de verdade comercial para
        preços, quota inclusa, trial default, dependências e mapeamento
        regulatório (qual norma BR cada módulo atende).
      properties:
        code: { type: string, example: "mod_crypto_kyt" }
        display_name: { type: string }
        short_description: { type: string }
        long_description: { type: string }
        category:
          type: string
          enum: [core, identity, onchain, regulatory, compliance, fraud, forensics, intelligence, analytics, operations, commercial, support]
        base_price_cents: { type: integer, format: int64, description: Preço base mensal em centavos BRL }
        billing_model:
          type: string
          enum: [MONTHLY_FIXED, PER_USAGE, HYBRID, FREE_WITH_PLAN]
        included_quota: { type: integer, description: Quantidade inclusa no base_price (0 = ilimitado) }
        overage_unit: { type: string, nullable: true, example: "transacao" }
        overage_price_cents: { type: integer, format: int64 }
        trial_days_default: { type: integer, minimum: 0, maximum: 90 }
        dependencies:
          type: array
          items: { type: string }
          example: ["mod_crypto_core", "mod_crypto_kyt"]
        plan_tier_minimum:
          type: string
          nullable: true
          enum: [starter, pro, scale, enterprise, regtech]
        is_addon: { type: boolean }
        is_active: { type: boolean }
        is_public_catalog: { type: boolean }
        requires_super_admin_activation: { type: boolean }
        feature_bullets:
          type: array
          items: { type: string }
        regulatory_mapping:
          type: array
          items: { type: string }
          example: ["BCB_RES_521_KYT", "COAF_LEI_9613_ART_10A"]
          description: Códigos das normas BR que este módulo atende
        icon_hint: { type: string, nullable: true, description: lucide-react icon name }
        color_hint: { type: string, nullable: true }
        sort_order: { type: integer }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    TenantModule:
      type: object
      description: |
        Linha de snx_tenant_modules — entitlement de um módulo para um tenant.
        Inclui trial sob demanda, quota override e workflow de request.
      properties:
        id: { type: string, format: uuid }
        tenant_id: { type: string, format: uuid }
        module_code: { type: string }
        is_active: { type: boolean }
        activated_at: { type: string, format: date-time }
        deactivated_at: { type: string, format: date-time, nullable: true }
        enabled_by_user_id: { type: string, format: uuid, nullable: true }
        disabled_by_user_id: { type: string, format: uuid, nullable: true }
        enabled_reason: { type: string, nullable: true }
        disabled_reason: { type: string, nullable: true }
        trial_started_at: { type: string, format: date-time, nullable: true }
        trial_expires_at: { type: string, format: date-time, nullable: true }
        trial_converted_at:
          type: string
          format: date-time
          nullable: true
          description: Quando tenant converteu trial em assinatura paga
        quota_override:
          type: integer
          nullable: true
          description: Sobrescreve included_quota do catalog quando preenchido
        quota_reset_at: { type: string, format: date-time, nullable: true }
        requested_at: { type: string, format: date-time, nullable: true }
        requested_by_user_id: { type: string, format: uuid, nullable: true }
        request_status:
          type: string
          nullable: true
          enum: [pending, approved, rejected]
        request_rejection_reason: { type: string, nullable: true }
        notes: { type: string, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    Pagination:
      type: object
      properties:
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer

    # ──── Health ────
    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [healthy, degraded, unhealthy]
        version:
          type: string
        uptime:
          type: string
        checks:
          type: object
          properties:
            database:
              type: string
            redis:
              type: string
            kafka:
              type: string

    # ──── Auth ────
    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        device_fingerprint:
          type: string

    LoginResponse:
      type: object
      properties:
        mfa_required:
          type: boolean
        mfa_token:
          type: string
          description: Temporary token for MFA verification (when mfa_required=true)
        access_token:
          type: string
          description: JWT access token (when mfa_required=false)
        refresh_token:
          type: string

    MFAVerifyRequest:
      type: object
      required: [mfa_token, code]
      properties:
        mfa_token:
          type: string
        code:
          type: string
          pattern: "^[0-9]{6}$"

    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
        refresh_token:
          type: string
        expires_in:
          type: integer
          description: Token TTL in seconds

    UserProfile:
      type: object
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        role:
          type: string
          enum: [SUPER_ADMIN, TENANT_ADMIN, COMPLIANCE, ANALYST, VIEWER, API_SERVICE]
        tenant_id:
          type: string
          format: uuid
        mfa_enabled:
          type: boolean
        created_at:
          type: string
          format: date-time
        last_login:
          type: string
          format: date-time

    # ──── Dashboard ────
    DashboardSummary:
      type: object
      properties:
        total_transactions:
          type: integer
        transactions_blocked:
          type: integer
        transactions_review:
          type: integer
        total_alerts:
          type: integer
        open_alerts:
          type: integer
        avg_risk_score:
          type: number
          format: float
        period:
          type: object
          properties:
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time

    DashboardMetrics:
      type: object
      properties:
        risk_distribution:
          type: object
          properties:
            low:
              type: integer
            medium:
              type: integer
            high:
              type: integer
            critical:
              type: integer
        daily_volume:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                format: date
              count:
                type: integer
              amount:
                type: number
        top_rules_triggered:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              rule_name:
                type: string
              count:
                type: integer

    # ──── Transactions ────
    Transaction:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        client_id:
          type: string
          format: uuid
        type:
          type: string
          enum: [deposit, withdrawal, transfer, swap, staking]
        amount:
          type: number
        currency:
          type: string
        blockchain:
          type: string
        from_address:
          type: string
        to_address:
          type: string
        tx_hash:
          type: string
        risk_score:
          type: number
          format: float
        risk_level:
          type: string
          enum: [low, medium, high, critical]
        decision:
          type: string
          enum: [approved, blocked, review]
        rules_triggered:
          type: array
          items:
            type: string
        status:
          type: string
        reviewed_by:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        analyzed_at:
          type: string
          format: date-time

    PaginatedTransactions:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Transaction"
        pagination:
          $ref: "#/components/schemas/Pagination"

    AnalyzeRequest:
      type: object
      required: [client_id, type, amount, currency]
      properties:
        client_id:
          type: string
          format: uuid
        type:
          type: string
          enum: [deposit, withdrawal, transfer, swap, staking]
        amount:
          type: number
        currency:
          type: string
        blockchain:
          type: string
        from_address:
          type: string
        to_address:
          type: string
        tx_hash:
          type: string
        metadata:
          type: object

    AnalysisResult:
      type: object
      properties:
        transaction_id:
          type: string
          format: uuid
        risk_score:
          type: number
          format: float
        risk_level:
          type: string
          enum: [low, medium, high, critical]
        decision:
          type: string
          enum: [approved, blocked, review]
        rules_triggered:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              rule_name:
                type: string
              score_impact:
                type: number
        dimensions:
          type: object
          description: "5-dimension score breakdown"
          properties:
            behavioral:
              type: number
            transactional:
              type: number
            device:
              type: number
            network:
              type: number
            compliance:
              type: number
        latency_ms:
          type: integer

    ReviewRequest:
      type: object
      required: [decision, reason]
      properties:
        decision:
          type: string
          enum: [approve, block]
        reason:
          type: string

    ReviewResponse:
      type: object
      properties:
        transaction_id:
          type: string
          format: uuid
        decision:
          type: string
        reviewed_by:
          type: string
          format: uuid
        reviewed_at:
          type: string
          format: date-time

    ExplainResponse:
      type: object
      properties:
        transaction_id:
          type: string
          format: uuid
        overall_score:
          type: number
          format: float
        decision:
          type: string
        dimensions:
          type: object
          properties:
            behavioral:
              type: object
              properties:
                score:
                  type: number
                factors:
                  type: array
                  items:
                    $ref: "#/components/schemas/ExplainFactor"
            transactional:
              type: object
              properties:
                score:
                  type: number
                factors:
                  type: array
                  items:
                    $ref: "#/components/schemas/ExplainFactor"
            device:
              type: object
              properties:
                score:
                  type: number
                factors:
                  type: array
                  items:
                    $ref: "#/components/schemas/ExplainFactor"
            network:
              type: object
              properties:
                score:
                  type: number
                factors:
                  type: array
                  items:
                    $ref: "#/components/schemas/ExplainFactor"
            compliance:
              type: object
              properties:
                score:
                  type: number
                factors:
                  type: array
                  items:
                    $ref: "#/components/schemas/ExplainFactor"
        rules_triggered:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              rule_name:
                type: string
              description:
                type: string
              score_impact:
                type: number
              evidence:
                type: string

    ExplainFactor:
      type: object
      properties:
        name:
          type: string
        value:
          type: string
        impact:
          type: number
        description:
          type: string

    # ──── Clients ────
    Client:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        name:
          type: string
        type:
          type: string
          enum: [individual, corporate]
        document_hash:
          type: string
          description: SHA-256 hash of CPF/CNPJ (never plain text)
        email_hash:
          type: string
          description: SHA-256 hash of email (never plain text)
        risk_tier:
          type: string
          enum: [GREEN, AMBER, RED]
        risk_score:
          type: number
          format: float
        kyc_status:
          type: string
          enum: [pending, verified, rejected, expired]
        pep:
          type: boolean
        blocked:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    PaginatedClients:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Client"
        pagination:
          $ref: "#/components/schemas/Pagination"

    CreateClientRequest:
      type: object
      required: [name, type, document, email]
      properties:
        name:
          type: string
        type:
          type: string
          enum: [individual, corporate]
        document:
          type: string
          description: CPF or CNPJ (will be hashed server-side)
        email:
          type: string
          format: email
        phone:
          type: string
        risk_tier:
          type: string
          enum: [GREEN, AMBER, RED]

    UpdateClientRequest:
      type: object
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
        risk_tier:
          type: string
          enum: [GREEN, AMBER, RED]
        kyc_status:
          type: string
          enum: [pending, verified, rejected, expired]
        pep:
          type: boolean

    # ──── Alerts ────
    Alert:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        transaction_id:
          type: string
          format: uuid
        client_id:
          type: string
          format: uuid
        type:
          type: string
        severity:
          type: string
          enum: [low, medium, high, critical]
        status:
          type: string
          enum: [open, assigned, resolved, escalated]
        title:
          type: string
        description:
          type: string
        assigned_to:
          type: string
          format: uuid
        resolved_at:
          type: string
          format: date-time
        resolution:
          type: string
        created_at:
          type: string
          format: date-time

    PaginatedAlerts:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Alert"
        pagination:
          $ref: "#/components/schemas/Pagination"

    # ──── Wallets ────
    Wallet:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        address:
          type: string
        blockchain:
          type: string
        label:
          type: string
        risk_score:
          type: number
          format: float
        risk_level:
          type: string
          enum: [low, medium, high, critical]
        blocklisted:
          type: boolean
        tags:
          type: array
          items:
            type: string
        first_seen:
          type: string
          format: date-time
        last_seen:
          type: string
          format: date-time

    PaginatedWallets:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Wallet"
        pagination:
          $ref: "#/components/schemas/Pagination"

    WalletScreenRequest:
      type: object
      required: [address, blockchain]
      properties:
        address:
          type: string
        blockchain:
          type: string
          enum: [bitcoin, ethereum, tron, solana, polygon]

    WalletScreenResponse:
      type: object
      properties:
        address:
          type: string
        blockchain:
          type: string
        risk_score:
          type: number
          format: float
        risk_level:
          type: string
          enum: [low, medium, high, critical]
        sanctions_match:
          type: boolean
        labels:
          type: array
          items:
            type: string
            description: "Known labels (e.g., exchange, mixer, darknet, scam)"
        sources:
          type: array
          items:
            type: string
            description: "Data sources checked (e.g., chainalysis, trm, ofac, internal)"

    # ──── Rules ────
    Rule:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        category:
          type: string
          enum: [base, crypto, advanced]
        severity:
          type: string
          enum: [low, medium, high, critical]
        score_impact:
          type: number
        shadow:
          type: boolean
          description: "Shadow mode: logs but does not enforce"
        enabled:
          type: boolean
        conditions:
          type: string
          description: Grule DSL expression
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    # ──── Fingerprint ────
    FingerprintPayload:
      type: object
      properties:
        canvas_hash:
          type: string
        webgl_hash:
          type: string
        audio_hash:
          type: string
        font_hash:
          type: string
        screen:
          type: object
          properties:
            width:
              type: integer
            height:
              type: integer
            color_depth:
              type: integer
        timezone:
          type: string
        language:
          type: string
        platform:
          type: string
        user_agent:
          type: string
        plugins:
          type: array
          items:
            type: string
        touch_support:
          type: boolean

    DeviceInfo:
      type: object
      properties:
        device_id:
          type: string
          format: uuid
        fingerprint_hash:
          type: string
        risk_score:
          type: number
          format: float
        first_seen:
          type: string
          format: date-time
        last_seen:
          type: string
          format: date-time
        user_ids:
          type: array
          items:
            type: string
            format: uuid
        ip_history:
          type: array
          items:
            type: object
            properties:
              ip:
                type: string
              geo:
                type: string
              seen_at:
                type: string
                format: date-time
        anomalies:
          type: array
          items:
            type: string

    # ──── Cases ────
    Case:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        title:
          type: string
        description:
          type: string
        status:
          type: string
          enum: [open, in_progress, resolved, escalated, closed]
        priority:
          type: string
          enum: [low, medium, high, critical]
        assigned_to:
          type: string
          format: uuid
        related_transactions:
          type: array
          items:
            type: string
            format: uuid
        related_alerts:
          type: array
          items:
            type: string
            format: uuid
        related_clients:
          type: array
          items:
            type: string
            format: uuid
        resolution:
          type: string
        findings:
          type: string
        created_by:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        resolved_at:
          type: string
          format: date-time

    PaginatedCases:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Case"
        pagination:
          $ref: "#/components/schemas/Pagination"

    CreateCaseRequest:
      type: object
      required: [title, description]
      properties:
        title:
          type: string
        description:
          type: string
        priority:
          type: string
          enum: [low, medium, high, critical]
        related_transactions:
          type: array
          items:
            type: string
            format: uuid
        related_alerts:
          type: array
          items:
            type: string
            format: uuid
        related_clients:
          type: array
          items:
            type: string
            format: uuid

    TimelineEvent:
      type: object
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum: [created, assigned, commented, escalated, resolved, closed, status_changed]
        actor_id:
          type: string
          format: uuid
        actor_name:
          type: string
        content:
          type: string
        metadata:
          type: object
        created_at:
          type: string
          format: date-time

    # ──── Reports ────
    Report:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        type:
          type: string
          enum: [sar, decripto, bcb_fx, coaf, custom]
        status:
          type: string
          enum: [generating, ready, submitted, accepted, rejected]
        title:
          type: string
        period_from:
          type: string
          format: date
        period_to:
          type: string
          format: date
        submitted_to:
          type: string
        submitted_at:
          type: string
          format: date-time
        created_by:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time

    PaginatedReports:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Report"
        pagination:
          $ref: "#/components/schemas/Pagination"

    GenerateReportRequest:
      type: object
      required: [type, period_from, period_to]
      properties:
        type:
          type: string
          enum: [sar, decripto, bcb_fx, coaf, custom]
        period_from:
          type: string
          format: date
        period_to:
          type: string
          format: date
        filters:
          type: object
          description: Optional filters for report content

    # ──── Radar ────
    RadarLive:
      type: object
      properties:
        events:
          type: array
          items:
            type: object
            properties:
              transaction_id:
                type: string
                format: uuid
              risk_score:
                type: number
                format: float
              risk_level:
                type: string
              type:
                type: string
              amount:
                type: number
              currency:
                type: string
              geo:
                type: object
                properties:
                  lat:
                    type: number
                  lng:
                    type: number
                  country:
                    type: string
              timestamp:
                type: string
                format: date-time

    RadarHotspots:
      type: object
      properties:
        hotspots:
          type: array
          items:
            type: object
            properties:
              geo:
                type: object
                properties:
                  lat:
                    type: number
                  lng:
                    type: number
                  country:
                    type: string
                  city:
                    type: string
              event_count:
                type: integer
              avg_risk_score:
                type: number
                format: float
              top_risk_types:
                type: array
                items:
                  type: string

    # ──── Billing ────
    BillingUsage:
      type: object
      properties:
        tenant_id:
          type: string
          format: uuid
        plan:
          type: string
        period:
          type: object
          properties:
            from:
              type: string
              format: date
            to:
              type: string
              format: date
        transactions_analyzed:
          type: integer
        transactions_limit:
          type: integer
        api_calls:
          type: integer
        api_calls_limit:
          type: integer
        wallets_screened:
          type: integer

    BillingPlan:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        price_brl:
          type: number
        price_usd:
          type: number
        transactions_limit:
          type: integer
        api_calls_limit:
          type: integer
        modules:
          type: array
          items:
            type: string
        features:
          type: array
          items:
            type: string

    # ──── Admin ────
    Tenant:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        plan:
          type: string
        modules:
          type: array
          items:
            type: string
        active:
          type: boolean
        created_at:
          type: string
          format: date-time

    CreateTenantRequest:
      type: object
      required: [name, slug, plan]
      properties:
        name:
          type: string
        slug:
          type: string
          pattern: "^[a-z0-9-]+$"
        plan:
          type: string
        modules:
          type: array
          items:
            type: string
        admin_email:
          type: string
          format: email
        admin_name:
          type: string

    CreateUserRequest:
      type: object
      required: [email, name, role, tenant_id]
      properties:
        email:
          type: string
          format: email
        name:
          type: string
        role:
          type: string
          enum: [TENANT_ADMIN, COMPLIANCE, ANALYST, VIEWER, API_SERVICE]
        tenant_id:
          type: string
          format: uuid

    SystemStats:
      type: object
      properties:
        total_tenants:
          type: integer
        active_tenants:
          type: integer
        total_users:
          type: integer
        total_transactions:
          type: integer
        total_alerts:
          type: integer
        total_cases:
          type: integer
        uptime:
          type: string
        version:
          type: string

    # ──── Webhooks ────
    ChargebackEvent:
      type: object
      properties:
        event_type:
          type: string
        transaction_ref:
          type: string
        amount:
          type: number
        currency:
          type: string
        reason_code:
          type: string
        timestamp:
          type: string
          format: date-time

    BlockchainEvent:
      type: object
      properties:
        event_type:
          type: string
          enum: [large_transfer, sanctions_hit, mixer_detected, bridge_transfer]
        blockchain:
          type: string
        tx_hash:
          type: string
        from_address:
          type: string
        to_address:
          type: string
        amount:
          type: number
        currency:
          type: string
        risk_indicators:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
