openapi: 3.1.0
info:
  title: SignDocs Brasil External API
  version: 1.2.0
  description: |
    API B2B para assinatura eletrônica com biometria facial, OTP, clickwrap e certificado digital ICP-Brasil.

    ## Autenticação

    Todas as rotas protegidas exigem um token JWT Bearer obtido via `POST /oauth2/token`
    usando o fluxo OAuth2 `client_credentials`.

    ## Ambientes

    | Ambiente | URL |
    |----------|-----|
    | Produção | `https://api.signdocs.com.br` |
    | Homologação | `https://api-hml.signdocs.com.br` |
    | Produção (mTLS) | `https://mtls-api.signdocs.com.br` |
    | Homologação (mTLS) | `https://mtls-api-hml.signdocs.com.br` |

    ## Feature Flags

    Alguns comportamentos da API dependem de feature flags habilitadas no tenant:

    | Flag | Efeito |
    |------|--------|
    | `documentExtractionEnabled` | Habilita o source `DOCUMENT_PHOTO` no enrollment biométrico. Permite extrair a face automaticamente de um documento (CNH/RG/passaporte). |
    | `hostedLivenessEnabled` | Habilita o modo de captura `HOSTED_PAGE` para etapas de liveness. |
    | `biometricRequired` | Torna obrigatório o envio de `geolocation` nas etapas biométricas (liveness, match, document photo match). |
    | `serpro.enabled` | Habilita etapas do tipo `SERPRO_IDENTITY_CHECK` para validação de identidade via SERPRO DataValid. |

    Consulte seu gerente de conta para ativar ou desativar essas flags.

    ## Erros

    Todas as respostas de erro seguem o formato RFC 7807 Problem Details (`application/problem+json`).
  license:
    name: Proprietary
    url: https://www.signdocs.com.br/termos-de-uso.html
  contact:
    name: SignDocs Brasil API Support
    email: administrativo@signdocs.com.br

servers:
  - url: https://api.signdocs.com.br
    description: Produção
  - url: https://api-hml.signdocs.com.br
    description: Homologação
  - url: https://mtls-api.signdocs.com.br
    description: Produção (mTLS)
  - url: https://mtls-api-hml.signdocs.com.br
    description: Homologação (mTLS)

tags:
  - name: Health
    description: Verificação de disponibilidade e histórico de saúde da API.
  - name: Authentication
    description: Obtenção de tokens de acesso OAuth2.
  - name: Verification
    description: Verificação pública de evidências de assinatura.
  - name: Transactions
    description: Criação, consulta, cancelamento e finalização de transações de assinatura.
  - name: Documents
    description: Upload, confirmação e download de documentos vinculados a transações.
  - name: Steps
    description: Listagem, início e conclusão de etapas de verificação de identidade.
  - name: Signing
    description: Preparação e conclusão de assinatura digital com certificado ICP-Brasil A1.
  - name: Evidence
    description: Consulta de pacotes de evidência criptograficamente assinados.
  - name: Webhooks
    description: Registro, listagem, remoção e teste de webhooks para notificações de eventos.
  - name: Users
    description: Gerenciamento de enrollment biométrico de usuários.
  - name: DocumentGroups
    description: Operações em grupos de documentos com múltiplos signatários.
  - name: SigningSessions
    description: |
      Sessões de assinatura (Assinatura Expressa). Abstração de alto nível que cria transação,
      etapas, documento e token de embed em uma única chamada. Retorna uma URL e clientSecret
      para integração via redirect, popup checkout ou polling server-side.
  - name: Envelopes
    description: |
      Envelopes para assinatura com múltiplos signatários. Um envelope agrupa sessões de assinatura
      sobre o mesmo documento, suportando fluxos paralelos ou sequenciais.

security:
  - bearerJwt: []

paths:
  # ── Health ──────────────────────────────────────────────────────
  /health:
    get:
      operationId: checkHealth
      tags: [Health]
      summary: Verificar saúde da API
      description: |
        Retorna o status de saúde da API e de seus componentes internos (DynamoDB, S3, KMS, mTLS Truststore).
        Retorna `200` se todos os componentes estão operacionais, `503` se algum está indisponível.
      security: []
      responses:
        '200':
          description: API saudável — todos os componentes operacionais.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /health/history:
    get:
      operationId: getHealthHistory
      tags: [Health]
      summary: Consultar histórico de saúde
      description: |
        Retorna métricas históricas de disponibilidade da API, incluindo uptime por componente,
        incidentes e a última verificação realizada.
      security: []
      parameters:
        - name: period
          in: query
          description: Período de consulta do histórico.
          schema:
            type: string
            enum: ['24h', '7d', '30d']
            default: '24h'
      responses:
        '200':
          description: Histórico de saúde retornado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthHistoryResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Authentication ──────────────────────────────────────────────
  /oauth2/token:
    post:
      operationId: createToken
      tags: [Authentication]
      summary: Obter token de acesso
      description: |
        Emite um token JWT de acesso usando o fluxo OAuth2 `client_credentials`.
        Suporta autenticação via `client_secret` ou via asserção JWT (`private_key_jwt`).

        O token retornado deve ser enviado no header `Authorization: Bearer <token>` nas chamadas protegidas.
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: Token emitido com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  # ── Verification ────────────────────────────────────────────────
  /v1/verify/envelope/{envelopeId}:
    get:
      operationId: verifyEnvelope
      tags: [Verification]
      summary: Verificar envelope multi-signatário
      description: |
        Consulta pública de um envelope multi-signatário. Retorna o status
        do envelope, lista de signatários (com `evidenceId` para drill-down
        em cada evidência), e URLs de download para artefatos consolidados
        a nível de envelope.

        - `downloads.combinedSignedPdf`: presente quando o envelope é um
          PDF e todos os signatários concluíram (PDF combinado com todas as
          assinaturas embutidas).
        - `downloads.consolidatedSignature`: presente quando o envelope é
          um documento não-PDF assinado com certificado digital e todos os
          signatários concluíram. Contém o `.p7s` (PKCS#7 detached) com
          todos os `SignerInfo` consolidados em um único arquivo.
      security: []
      parameters:
        - name: envelopeId
          in: path
          required: true
          description: Identificador único do envelope.
          schema:
            type: string
      responses:
        '200':
          description: Envelope encontrado.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnvelopeVerificationResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/verify/{evidenceId}:
    get:
      operationId: verifyEvidence
      tags: [Verification]
      summary: Verificar evidência de assinatura
      description: |
        Consulta pública de uma evidência de assinatura. Permite que terceiros verifiquem
        a autenticidade de uma transação pelo `evidenceId`, sem necessidade de autenticação.
      security: []
      parameters:
        - name: evidenceId
          in: path
          required: true
          description: Identificador único da evidência.
          schema:
            type: string
      responses:
        '200':
          description: Evidência encontrada e verificada.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerificationResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/verify/{evidenceId}/downloads:
    get:
      operationId: getVerificationDownloads
      tags: [Verification]
      summary: Obter downloads da evidência
      description: |
        Retorna URLs pré-assinadas para download dos artefatos de evidência:
        pacote de evidência (.p7m), PDF assinado e PDF final com carimbo visual.
      security: []
      parameters:
        - name: evidenceId
          in: path
          required: true
          description: Identificador único da evidência.
          schema:
            type: string
      responses:
        '200':
          description: URLs de download geradas com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerificationDownloadsResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  # ── Transactions ────────────────────────────────────────────────
  /v1/transactions:
    post:
      operationId: createTransaction
      tags: [Transactions]
      summary: Criar transação de assinatura
      description: |
        Cria uma nova transação de assinatura eletrônica. Opcionalmente inclui o documento
        em base64 no corpo da requisição. Suporta idempotência via header `X-Idempotency-Key`.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: X-Idempotency-Key
          in: header
          description: Chave de idempotência para evitar criação duplicada.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTransactionRequest'
      responses:
        '201':
          description: Transação criada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            Location:
              description: URL do recurso criado.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateTransactionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      operationId: listTransactions
      tags: [Transactions]
      summary: Listar transações
      description: |
        Retorna uma lista paginada de transações do tenant autenticado.
        Suporta filtros por status, usuário, grupo de documentos e intervalo de datas.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: status
          in: query
          description: Filtrar por status da transação.
          schema:
            $ref: '#/components/schemas/TransactionStatus'
        - name: userExternalId
          in: query
          description: Filtrar por identificador externo do usuário.
          schema:
            type: string
        - name: documentGroupId
          in: query
          description: Filtrar por grupo de documentos.
          schema:
            type: string
        - name: startDate
          in: query
          description: Data inicial do intervalo (ISO 8601).
          schema:
            type: string
            format: date-time
        - name: endDate
          in: query
          description: Data final do intervalo (ISO 8601).
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Número máximo de resultados por página (máximo 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: nextToken
          in: query
          description: Token de paginação retornado pela consulta anterior.
          schema:
            type: string
      responses:
        '200':
          description: Lista de transações retornada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}:
    get:
      operationId: getTransaction
      tags: [Transactions]
      summary: Consultar transação
      description: |
        Retorna os detalhes completos de uma transação, incluindo signatário, etapas e metadados.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: Detalhes da transação retornados com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionDetailResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      operationId: cancelTransaction
      tags: [Transactions]
      summary: Cancelar transação
      description: |
        Cancela uma transação em andamento. Apenas transações que ainda não foram
        finalizadas podem ser canceladas.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: Transação cancelada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelTransactionResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/finalize:
    post:
      operationId: finalizeTransaction
      tags: [Transactions]
      summary: Finalizar transação
      description: |
        Finaliza uma transação com todas as etapas concluídas, gerando o pacote de evidência
        criptograficamente assinado (.p7m). O `evidenceId` retornado pode ser usado para
        verificação pública via `/v1/verify/{evidenceId}`.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: Transação finalizada e evidência gerada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinalizeResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Documents ───────────────────────────────────────────────────
  /v1/transactions/{transactionId}/document:
    post:
      operationId: uploadDocument
      tags: [Documents]
      summary: Upload de documento (base64)
      description: |
        Envia o documento codificado em base64 diretamente no corpo da requisição. Suporta PDF, DOCX, XLSX, PPTX, ODT, imagens e outros formatos.
        Para documentos maiores, utilize o fluxo de upload pré-assinado
        (`presign` → upload direto ao S3 → `confirm`).

        ### Validação de qualidade do PDF

        O documento é validado quanto à qualidade e integridade. Caso o PDF seja inválido,
        corrompido, protegido por senha ou não atenda aos critérios mínimos de qualidade,
        a API retorna `422 Unprocessable Entity` com detalhes do problema.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentUploadRequest'
      responses:
        '200':
          description: Documento enviado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentUploadResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/document/presign:
    post:
      operationId: presignDocumentUpload
      tags: [Documents]
      summary: Obter URL pré-assinada para upload
      description: |
        Gera uma URL pré-assinada do S3 para upload direto do documento.
        Após o upload, confirme com `POST /v1/transactions/{id}/document/confirm`.

        Recomendado para documentos maiores que 5 MB.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PresignRequest'
      responses:
        '200':
          description: URL pré-assinada gerada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PresignResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/document/confirm:
    post:
      operationId: confirmDocumentUpload
      tags: [Documents]
      summary: Confirmar upload pré-assinado
      description: |
        Confirma que o upload via URL pré-assinada foi concluído com sucesso.
        O `uploadToken` foi retornado na resposta de `presign`.

        Após a confirmação, o documento é validado quanto à qualidade e integridade (validação de qualidade aplica-se apenas a PDFs). Caso não
        atenda aos critérios mínimos, retorna `422 Unprocessable Entity` com detalhes do problema.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmDocumentRequest'
      responses:
        '200':
          description: Upload confirmado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmDocumentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/download:
    get:
      operationId: downloadDocument
      tags: [Documents]
      summary: Obter URLs de download do documento
      description: |
        Retorna URLs pré-assinadas para download do documento original e do documento assinado.
        As URLs expiram em 1 hora.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: URLs de download geradas com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentDownloadResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Steps ───────────────────────────────────────────────────────
  /v1/transactions/{transactionId}/steps:
    get:
      operationId: listSteps
      tags: [Steps]
      summary: Listar etapas da transação
      description: |
        Retorna todas as etapas de verificação de identidade vinculadas à transação,
        com seus status, tentativas e resultados.

        Requer scope `steps:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: Lista de etapas retornada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StepListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/steps/{stepId}/start:
    post:
      operationId: startStep
      tags: [Steps]
      summary: Iniciar etapa de verificação
      description: |
        Inicia uma etapa de verificação de identidade. A resposta varia conforme o tipo da etapa:

        - **BIOMETRIC_LIVENESS**: Retorna `livenessSessionId` (modo `BANK_APP`) ou `hostedUrl` (modo `HOSTED_PAGE`).
        - **OTP_CHALLENGE**: Envia o código OTP por e-mail e retorna mensagem de confirmação.
        - **OTP_VERIFY**: Retorna mensagem informando que o código já foi enviado.
        - **CLICK_ACCEPT**: Retorna mensagem com o termo a ser aceito.
        - **DOCUMENT_PHOTO_MATCH**: Retorna mensagem indicando prontidão para envio da foto do documento.
        - **SERPRO_IDENTITY_CHECK**: Retorna mensagem indicando prontidão para verificação SERPRO (requer `serpro.enabled`).
        - **DIGITAL_SIGN_A1**: Retorna mensagem indicando prontidão para assinatura digital.

        ### Reenvio de OTP

        Ao chamar `start` em uma etapa `OTP_CHALLENGE` quando `attempts > 1` (reenvio), a etapa
        pareada `OTP_VERIFY` é automaticamente resetada caso esteja em status `FAILED` ou `STARTED`.
        Isso permite que o usuário tente novamente com um novo código sem necessidade de intervenção manual.

        Requer scope `steps:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
        - name: stepId
          in: path
          required: true
          description: Identificador único da etapa.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StepStartRequest'
      responses:
        '200':
          description: Etapa iniciada com sucesso. A resposta varia conforme o tipo.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StepStartResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/steps/{stepId}/complete:
    post:
      operationId: completeStep
      tags: [Steps]
      summary: Concluir etapa de verificação
      description: |
        Submete os dados para conclusão de uma etapa de verificação. O corpo da requisição
        varia conforme o tipo da etapa:

        - **BIOMETRIC_LIVENESS / BIOMETRIC_MATCH**: `{ "livenessSessionId": "..." }`
        - **OTP_VERIFY**: `{ "code": "123456" }`
        - **CLICK_ACCEPT**: `{ "accepted": true }`
        - **DOCUMENT_PHOTO_MATCH**: `{ "documentImage": "<base64>", "documentType": "CNH" }` — Envia a foto do documento (base64, máx. 5MB). O tipo aceito é `RG`, `CNH`, `PASSPORT` ou `OTHER`. A face é extraída e comparada com a referência biométrica. Retorna `similarity` score.
        - **SERPRO_IDENTITY_CHECK**: `{ "cpf": "12345678901", "name": "João da Silva", "birthDate": "1990-01-15" }` — Valida identidade via SERPRO DataValid. Disponível apenas quando o tenant possui `serpro.enabled`.
        - **DIGITAL_SIGN_A1**: Não requer corpo (use o fluxo `/signing`).

        ### Geolocalização condicional

        Quando o tenant possui a flag `biometricRequired` habilitada, o campo `geolocation`
        é **obrigatório** para etapas biométricas (`BIOMETRIC_LIVENESS`, `BIOMETRIC_MATCH`, `DOCUMENT_PHOTO_MATCH`).
        Formato: `{ "latitude": -23.5505, "longitude": -46.6333 }`.

        Requer scope `steps:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
        - name: stepId
          in: path
          required: true
          description: Identificador único da etapa.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StepCompleteRequest'
      responses:
        '200':
          description: Etapa concluída (ou falhou). Verifique o campo `status` na resposta.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StepCompleteResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Signing ─────────────────────────────────────────────────────
  /v1/transactions/{transactionId}/signing/prepare:
    post:
      operationId: prepareSigning
      tags: [Signing]
      summary: Preparar assinatura digital
      description: |
        Prepara o documento para assinatura digital com certificado ICP-Brasil A1.
        Retorna o hash que deve ser assinado pelo certificado do cliente.

        O cliente deve enviar a cadeia de certificados PEM para que o servidor
        prepare o placeholder no PDF (para documentos PDF) ou compute o hash
        do arquivo inteiro (para documentos genéricos — CAdES .p7s com conteúdo anexado).

        O formato é determinado automaticamente pelo `documentFormat` da transação,
        definido no momento do upload do documento.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrepareSigningRequest'
      responses:
        '200':
          description: Hash para assinatura gerado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PrepareSigningResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/transactions/{transactionId}/signing/complete:
    post:
      operationId: completeSigning
      tags: [Signing]
      summary: Concluir assinatura digital
      description: |
        Submete a assinatura digital bruta (raw signature) gerada pelo certificado A1 do cliente.

        - Para documentos PDF: o servidor injeta a assinatura no PDF (PAdES).
        - Para documentos genéricos: o servidor gera um arquivo `.p7s` CAdES com o conteúdo do documento anexado (enveloping).

        Conclui a etapa `DIGITAL_SIGN_A1`.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteSigningRequest'
      responses:
        '200':
          description: Assinatura digital concluída com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SigningCompleteResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Evidence ────────────────────────────────────────────────────
  /v1/transactions/{transactionId}/evidence:
    get:
      operationId: getEvidence
      tags: [Evidence]
      summary: Consultar evidência da transação
      description: |
        Retorna os metadados do pacote de evidência gerado para a transação,
        incluindo hash, chave KMS e URL de download pré-assinada.

        Requer scope `evidence:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: transactionId
          in: path
          required: true
          description: Identificador único da transação.
          schema:
            type: string
      responses:
        '200':
          description: Evidência retornada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvidenceResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Users ───────────────────────────────────────────────────────
  /v1/users/{userExternalId}/enrollment:
    put:
      operationId: enrollUser
      tags: [Users]
      summary: Cadastrar ou atualizar enrollment biométrico
      description: |
        Registra ou atualiza a imagem de referência biométrica de um usuário.
        A imagem deve ser enviada em base64 (JPEG). O enrollment é versionado —
        cada chamada incrementa a versão.

        ### Fontes de enrollment

        - **BANK_PROVIDED** (padrão): Selfie ou foto fornecida pelo banco.
        - **FIRST_LIVENESS**: Imagem capturada na primeira sessão de liveness.
        - **DOCUMENT_PHOTO**: Foto de documento (CNH/RG/passaporte). O rosto é extraído automaticamente.
          Requer a feature flag `documentExtractionEnabled` habilitada no tenant.

        ### CPF obrigatório

        O campo `cpf` é obrigatório e utilizado para vincular o enrollment à identidade do titular.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: userExternalId
          in: path
          required: true
          description: Identificador externo do usuário no sistema do integrador.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EnrollmentRequest'
      responses:
        '200':
          description: Enrollment realizado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnrollmentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Document Groups ─────────────────────────────────────────────
  /v1/document-groups/{groupId}/combined-stamp:
    post:
      operationId: generateCombinedStamp
      tags: [DocumentGroups]
      summary: Gerar carimbo combinado do grupo
      description: |
        Gera o documento com carimbo visual combinado para todas as assinaturas do grupo de documentos (PDF: carimbo visual; outros formatos: .p7s combinado).
        Retorna URL pré-assinada para download do PDF gerado.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: groupId
          in: path
          required: true
          description: Identificador do grupo de documentos.
          schema:
            type: string
      responses:
        '200':
          description: Carimbo combinado gerado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CombinedStampResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Webhooks ────────────────────────────────────────────────────
  /v1/webhooks:
    post:
      operationId: registerWebhook
      tags: [Webhooks]
      summary: Registrar webhook
      description: |
        Registra um novo endpoint de webhook para receber notificações de eventos.
        O `secret` retornado na resposta é exibido apenas neste momento — armazene-o
        com segurança para validar as assinaturas HMAC-SHA256 dos payloads recebidos.

        Requer scope `webhooks:write`.
      security:
        - bearerJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterWebhookRequest'
      responses:
        '201':
          description: Webhook registrado com sucesso. O `secret` é retornado apenas nesta resposta.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterWebhookResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      operationId: listWebhooks
      tags: [Webhooks]
      summary: Listar webhooks
      description: |
        Retorna todos os webhooks registrados pelo tenant autenticado.

        Requer scope `webhooks:write`.
      security:
        - bearerJwt: []
      responses:
        '200':
          description: Lista de webhooks retornada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/webhooks/{webhookId}:
    delete:
      operationId: deleteWebhook
      tags: [Webhooks]
      summary: Remover webhook
      description: |
        Remove um webhook registrado. Após a remoção, nenhuma nova notificação será enviada
        para o endpoint.

        Requer scope `webhooks:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: webhookId
          in: path
          required: true
          description: Identificador único do webhook.
          schema:
            type: string
      responses:
        '204':
          description: Webhook removido com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/webhooks/{webhookId}/test:
    post:
      operationId: testWebhook
      tags: [Webhooks]
      summary: Testar webhook
      description: |
        Envia um payload de teste para o endpoint do webhook e retorna o resultado da entrega.
        Útil para verificar conectividade e configuração do webhook.

        Requer scope `webhooks:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: webhookId
          in: path
          required: true
          description: Identificador único do webhook.
          schema:
            type: string
      responses:
        '200':
          description: Resultado do teste de entrega.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Signing Sessions ──────────────────────────────────────────
  /v1/signing-sessions:
    post:
      operationId: createSigningSession
      tags: [SigningSessions]
      summary: Criar sessão de assinatura
      description: |
        Cria uma sessão de assinatura expressa (Assinatura Expressa). Cria atomicamente transação,
        etapas, documento e token de embed. Retorna sessionId, url e clientSecret.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: X-Idempotency-Key
          in: header
          description: Chave de idempotência para evitar criação duplicada.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSigningSessionRequest'
      responses:
        '201':
          description: Sessão de assinatura criada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            Location:
              description: URL do recurso criado.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSigningSessionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      operationId: listSigningSessions
      tags: [SigningSessions]
      summary: Listar sessões de assinatura
      description: |
        Retorna uma lista paginada de sessões de assinatura filtradas por status.
        Suporta paginação via cursor.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: status
          in: query
          required: true
          description: Filtrar por status da sessão.
          schema:
            $ref: '#/components/schemas/SigningSessionStatus'
        - name: limit
          in: query
          description: Número máximo de resultados por página (máximo 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor de paginação retornado pela consulta anterior.
          schema:
            type: string
      responses:
        '200':
          description: Lista de sessões retornada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SigningSessionListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/signing-sessions/{sessionId}:
    get:
      operationId: getSigningSession
      tags: [SigningSessions]
      summary: Obter dados de bootstrap da sessão
      description: |
        Retorna os dados de bootstrap para o aplicativo de assinatura hospedado ou widget.

        Autenticação via embedToken (clientSecret).
      security:
        - embedToken: []
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Identificador único da sessão de assinatura.
          schema:
            type: string
      responses:
        '200':
          description: Dados de bootstrap retornados com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SigningSessionBootstrap'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/signing-sessions/{sessionId}/advance:
    post:
      operationId: advanceSigningSession
      tags: [SigningSessions]
      summary: Avançar etapa da sessão
      description: |
        Avança a sessão de assinatura através de suas etapas. Suporta as ações:
        accept, verify_otp, resend_otp, start_liveness, complete_liveness,
        prepare_signing, complete_signing. Auto-finaliza na última etapa.

        Autenticação via embedToken (clientSecret).
      security:
        - embedToken: []
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Identificador único da sessão de assinatura.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvanceSessionRequest'
      responses:
        '200':
          description: Etapa avançada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvanceSessionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/signing-sessions/{sessionId}/status:
    get:
      operationId: getSigningSessionStatus
      tags: [SigningSessions]
      summary: Consultar status da sessão (polling leve)
      description: |
        Endpoint leve para polling de status da sessão. Retorna apenas sessionId,
        transactionId, status, completedAt e evidenceId.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Identificador único da sessão de assinatura.
          schema:
            type: string
      responses:
        '200':
          description: Status da sessão retornado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SigningSessionStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/signing-sessions/{sessionId}/cancel:
    post:
      operationId: cancelSigningSession
      tags: [SigningSessions]
      summary: Cancelar sessão de assinatura
      description: |
        Cancela uma sessão de assinatura ativa. Apenas sessões com status ACTIVE podem
        ser canceladas. O cancelamento cascateia para a transação subjacente.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Identificador único da sessão de assinatura.
          schema:
            type: string
      responses:
        '200':
          description: Sessão cancelada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelSigningSessionResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/signing-sessions/{sessionId}/resend-otp:
    post:
      operationId: resendSigningSessionOtp
      tags: [SigningSessions]
      summary: Reenviar código OTP da sessão
      description: |
        Reenvia o desafio OTP para a sessão de assinatura atual.

        Autenticação via embedToken (clientSecret).
      security:
        - embedToken: []
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Identificador único da sessão de assinatura.
          schema:
            type: string
      responses:
        '200':
          description: OTP reenviado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvanceSessionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # ── Envelopes ─────────────────────────────────────────────────────
  /v1/envelopes:
    post:
      operationId: createEnvelope
      tags: [Envelopes]
      summary: Criar envelope para múltiplos signatários
      description: |
        Cria um envelope que agrupa sessões de assinatura sobre o mesmo documento.
        Suporta modo PARALLEL (todos assinam simultaneamente) ou SEQUENTIAL (em ordem).

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: X-Idempotency-Key
          in: header
          description: Chave de idempotência para evitar criação duplicada.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEnvelopeRequest'
      responses:
        '201':
          description: Envelope criado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            Location:
              description: URL do recurso criado.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateEnvelopeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/envelopes/{envelopeId}:
    get:
      operationId: getEnvelope
      tags: [Envelopes]
      summary: Consultar detalhes do envelope
      description: |
        Retorna os detalhes completos do envelope, incluindo resumo de cada sessão.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: envelopeId
          in: path
          required: true
          description: Identificador único do envelope.
          schema:
            type: string
      responses:
        '200':
          description: Detalhes do envelope retornados com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetEnvelopeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/envelopes/{envelopeId}/sessions:
    post:
      operationId: addEnvelopeSession
      tags: [Envelopes]
      summary: Adicionar sessão de signatário ao envelope
      description: |
        Adiciona uma sessão de assinatura para um signatário no envelope. Retorna a URL
        e clientSecret para integração.

        Requer scope `transactions:write`.
      security:
        - bearerJwt: []
      parameters:
        - name: envelopeId
          in: path
          required: true
          description: Identificador único do envelope.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddEnvelopeSessionRequest'
      responses:
        '201':
          description: Sessão de signatário adicionada com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            Location:
              description: URL do recurso criado.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddEnvelopeSessionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/envelopes/{envelopeId}/combined-stamp:
    post:
      operationId: getEnvelopeCombinedStamp
      tags: [Envelopes]
      summary: Gerar carimbo combinado do envelope
      description: |
        Gera ou recupera o documento com carimbo combinado de todas as assinaturas do envelope.
        Disponível somente após todas as sessões serem concluídas.

        Requer scope `transactions:read`.
      security:
        - bearerJwt: []
      parameters:
        - name: envelopeId
          in: path
          required: true
          description: Identificador único do envelope.
          schema:
            type: string
      responses:
        '200':
          description: Carimbo combinado gerado com sucesso.
          headers:
            Strict-Transport-Security:
              $ref: '#/components/headers/Strict-Transport-Security'
            X-Content-Type-Options:
              $ref: '#/components/headers/X-Content-Type-Options'
            X-Frame-Options:
              $ref: '#/components/headers/X-Frame-Options'
            Referrer-Policy:
              $ref: '#/components/headers/Referrer-Policy'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            API-Version:
              $ref: '#/components/headers/API-Version'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnvelopeCombinedStampResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalServerError'

# ══════════════════════════════════════════════════════════════════
# Components
# ══════════════════════════════════════════════════════════════════
components:
  securitySchemes:
    bearerJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Token de acesso OAuth2 obtido via `POST /oauth2/token`.
    embedToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Token de sessão (clientSecret) retornado por `POST /v1/signing-sessions`.
        Usado pelos endpoints advance/get/resend-otp para autenticar a sessão.
        Contém claims: sub (sessionId), tenantId, transactionId, purpose: signing_session_embed.

  # ── Headers ─────────────────────────────────────────────────────
  headers:
    Strict-Transport-Security:
      description: Política HSTS.
      schema:
        type: string
        example: 'max-age=31536000; includeSubDomains'
    X-Content-Type-Options:
      description: Prevenção de MIME sniffing.
      schema:
        type: string
        example: nosniff
    X-Frame-Options:
      description: Prevenção de clickjacking.
      schema:
        type: string
        example: DENY
    Referrer-Policy:
      description: Política de referrer.
      schema:
        type: string
        example: strict-origin-when-cross-origin
    Cache-Control:
      description: Controle de cache.
      schema:
        type: string
        example: no-store
    API-Version:
      description: Versão da API.
      schema:
        type: string
        example: '1.0'
    RateLimit-Limit:
      description: Número máximo de requisições permitidas na janela atual.
      schema:
        type: integer
        example: 100
    RateLimit-Remaining:
      description: Número de requisições restantes na janela atual.
      schema:
        type: integer
        example: 97
    RateLimit-Reset:
      description: Timestamp Unix (segundos) de quando a janela de rate limit será resetada.
      schema:
        type: integer
        example: 1708646400
    Deprecation:
      description: |
        RFC 8594 §2 — Indica que o endpoint está depreciado.
        Valor é `true` ou um timestamp Unix prefixado com `@`.
      schema:
        type: string
        example: '@1725148800'
    Sunset:
      description: |
        RFC 8594 §3 — Data HTTP (RFC 7231) após a qual o endpoint será removido.
        Clientes devem migrar para o endpoint substituto antes desta data.
      schema:
        type: string
        example: 'Tue, 01 Sep 2026 00:00:00 GMT'

  # ── Reusable Responses ──────────────────────────────────────────
  responses:
    BadRequest:
      description: Requisição inválida — parâmetros ausentes ou malformados.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/bad-request
            title: Bad Request
            status: 400
            detail: O campo 'purpose' é obrigatório.
    Unauthorized:
      description: Autenticação ausente ou inválida.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/unauthorized
            title: Unauthorized
            status: 401
            detail: Invalid or missing authentication
    Forbidden:
      description: Permissões insuficientes para a operação solicitada.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/forbidden
            title: Forbidden
            status: 403
            detail: Insufficient permissions
    NotFound:
      description: Recurso não encontrado.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/not-found
            title: Not Found
            status: 404
            detail: Transaction not found
    Conflict:
      description: Conflito de estado — a operação não pode ser realizada no estado atual do recurso.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/conflict
            title: Conflict
            status: 409
            detail: Transaction already finalized
    UnprocessableEntity:
      description: Entidade não processável — a requisição é sintaticamente válida mas semanticamente incorreta.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          examples:
            generic:
              summary: Erro genérico de validação
              value:
                type: https://api.signdocs.com.br/errors/unprocessable-entity
                title: Unprocessable Entity
                status: 422
                detail: O documento deve ser um PDF válido.
            pdf-quality:
              summary: PDF com qualidade insuficiente
              value:
                type: https://api.signdocs.com.br/errors/pdf-quality-validation-failed
                title: PDF Quality Validation Failed
                status: 422
                detail: "O PDF não atende aos critérios mínimos de qualidade: resolução insuficiente, páginas em branco ou arquivo corrompido."
            pdf-password-protected:
              summary: PDF protegido por senha
              value:
                type: https://api.signdocs.com.br/errors/pdf-password-protected
                title: PDF Password Protected
                status: 422
                detail: O PDF está protegido por senha e não pode ser processado. Envie um PDF sem proteção.
            feature-not-enabled:
              summary: Feature flag não habilitada
              value:
                type: https://api.signdocs.com.br/errors/feature-not-enabled
                title: Feature Not Enabled
                status: 422
                detail: "A feature 'documentExtractionEnabled' não está habilitada para este tenant."
            geolocation-required:
              summary: Geolocalização obrigatória
              value:
                type: https://api.signdocs.com.br/errors/geolocation-required
                title: Geolocation Required
                status: 422
                detail: O campo 'geolocation' é obrigatório para etapas biométricas quando 'biometricRequired' está habilitado.
    TooManyRequests:
      description: Limite de requisições excedido.
      headers:
        Retry-After:
          description: Segundos a aguardar antes de tentar novamente.
          schema:
            type: integer
            example: 60
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/too-many-requests
            title: Too Many Requests
            status: 429
            detail: Rate limit exceeded
    InternalServerError:
      description: Erro interno do servidor.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/internal-server-error
            title: Internal Server Error
            status: 500
    ServiceUnavailable:
      description: Serviço temporariamente indisponível.
      headers:
        Retry-After:
          description: Segundos a aguardar antes de tentar novamente.
          schema:
            type: integer
            example: 5
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetail'
          example:
            type: https://api.signdocs.com.br/errors/service-unavailable
            title: Service Unavailable
            status: 503
            detail: A downstream service is temporarily unavailable. Please retry.

  # ── Schemas ─────────────────────────────────────────────────────
  schemas:
    # ── Enums ───────────────────────────────────────────────────
    TransactionStatus:
      type: string
      description: Status possíveis de uma transação.
      enum:
        - CREATED
        - DOCUMENT_UPLOADED
        - IN_PROGRESS
        - COMPLETED
        - CANCELLED
        - EXPIRED
        - FAILED

    StepType:
      type: string
      description: Tipos de etapa de verificação de identidade.
      enum:
        - CLICK_ACCEPT
        - OTP_CHALLENGE
        - OTP_VERIFY
        - BIOMETRIC_LIVENESS
        - BIOMETRIC_MATCH
        - DOCUMENT_PHOTO_MATCH
        - SERPRO_IDENTITY_CHECK
        - DIGITAL_SIGN_A1

    StepStatus:
      type: string
      description: Status possíveis de uma etapa.
      enum:
        - PENDING
        - STARTED
        - COMPLETED
        - FAILED

    PolicyProfile:
      type: string
      description: Perfis de política de verificação pré-definidos.
      enum:
        - CLICK_ONLY
        - CLICK_PLUS_OTP
        - BIOMETRIC
        - BIOMETRIC_PLUS_OTP
        - DIGITAL_CERTIFICATE
        - CUSTOM

    TransactionPurpose:
      type: string
      description: Finalidade da transação.
      enum:
        - DOCUMENT_SIGNATURE
        - ACTION_AUTHENTICATION

    CaptureMode:
      type: string
      description: Modo de captura biométrica.
      enum:
        - BANK_APP
        - HOSTED_PAGE

    WebhookEventType:
      type: string
      description: |
        Tipos de evento para notificação via webhook.

        Eventos marcados com (NT65) aplicam-se ao fluxo de crédito consignado INSS
        (Nota Técnica 65/ITI), e só são emitidos para tenants com
        `nt65ComplianceEnabled` habilitado. Ver `docs/18-nt65-consignado.md`.
      enum:
        - TRANSACTION.CREATED
        - TRANSACTION.COMPLETED
        - TRANSACTION.CANCELLED
        - TRANSACTION.FAILED
        - TRANSACTION.EXPIRED
        - TRANSACTION.FALLBACK
        - TRANSACTION.DEADLINE_APPROACHING
        - STEP.STARTED
        - STEP.COMPLETED
        - STEP.FAILED
        - STEP.PURPOSE_DISCLOSURE_SENT
        - QUOTA.WARNING
        - API.DEPRECATION_NOTICE
        - SIGNING_SESSION.CREATED
        - SIGNING_SESSION.COMPLETED
        - SIGNING_SESSION.CANCELLED
        - SIGNING_SESSION.EXPIRED
      x-enum-descriptions:
        TRANSACTION.CREATED: Transação criada.
        TRANSACTION.COMPLETED: Transação finalizada com sucesso; evidência disponível.
        TRANSACTION.CANCELLED: Transação cancelada pelo integrador ou usuário.
        TRANSACTION.FAILED: Transação falhou (erro irrecuperável).
        TRANSACTION.EXPIRED: Transação expirou sem finalização.
        TRANSACTION.FALLBACK: Fallback acionado (ex. SERPRO sem biometria).
        TRANSACTION.DEADLINE_APPROACHING: (NT65) Faltam ≤2 dias úteis para o prazo de submissão ao INSS.
        STEP.STARTED: Etapa iniciada.
        STEP.COMPLETED: Etapa concluída.
        STEP.FAILED: Etapa falhou.
        STEP.PURPOSE_DISCLOSURE_SENT: (NT65) Notificação de divulgação de finalidade enviada ao beneficiário.
        QUOTA.WARNING: Uso do tenant cruzou limiar de cota (80/90/100%).
        API.DEPRECATION_NOTICE: Uso de endpoint/campo marcado como deprecated.
        SIGNING_SESSION.CREATED: Sessão de assinatura criada.
        SIGNING_SESSION.COMPLETED: Sessão concluída.
        SIGNING_SESSION.CANCELLED: Sessão cancelada.
        SIGNING_SESSION.EXPIRED: Sessão expirou.

    # ── Common / Shared ─────────────────────────────────────────
    ProblemDetail:
      type: object
      description: Formato de erro RFC 7807 Problem Details.
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
          description: URI que identifica o tipo de erro.
          example: https://api.signdocs.com.br/errors/bad-request
        title:
          type: string
          description: Resumo legível do tipo de erro.
          example: Bad Request
        status:
          type: integer
          description: Código de status HTTP.
          example: 400
        detail:
          type: string
          description: Explicação específica da ocorrência do erro.
          example: O campo 'purpose' é obrigatório.
        instance:
          type: string
          description: URI que identifica a ocorrência específica do erro (path da requisição).
          example: /v1/transactions

    Policy:
      type: object
      description: Política de verificação de identidade da transação.
      required:
        - profile
      properties:
        profile:
          $ref: '#/components/schemas/PolicyProfile'
        customSteps:
          type: array
          description: Etapas customizadas (apenas quando `profile` = `CUSTOM`).
          items:
            $ref: '#/components/schemas/StepType'

    Owner:
      type: object
      description: |
        Identidade do solicitante da assinatura (quem cria a sessão/envelope), distinta
        do signatário. Quando informado, a SignDocs:

        1. **Envia automaticamente um e-mail de convite** para `signer.email` no momento
           da criação da sessão, se `signer.email` for diferente de `owner.email`
           (case-insensitive). Em envelopes, cada signatário recebe seu próprio convite.
        2. **Notifica o solicitante por e-mail** quando cada signatário concluir a
           assinatura, com o protocolo de evidência e link para o verificador.

        Omitir `owner` mantém o comportamento padrão: o integrador entrega a URL de
        assinatura por seus próprios canais e usa webhooks para acompanhar o status.
      properties:
        email:
          type: string
          format: email
          description: E-mail do solicitante. Recebe notificações de conclusão.
          example: solicitante@empresa.com.br
        name:
          type: string
          description: Nome do solicitante (usado na saudação dos e-mails).
          maxLength: 1024
          example: Maria Souza

    Signer:
      type: object
      description: |
        Dados do signatário.
        É obrigatório informar pelo menos um entre `cpf` e `cnpj`.
      required:
        - name
        - userExternalId
      anyOf:
        - required: [cpf]
        - required: [cnpj]
      properties:
        name:
          type: string
          description: Nome completo do signatário.
          example: João da Silva
        email:
          type: string
          format: email
          description: E-mail do signatário (obrigatório para etapas OTP).
          example: joao@exemplo.com.br
        userExternalId:
          type: string
          description: Identificador externo do usuário no sistema do integrador.
          example: usr-12345
        displayName:
          type: string
          description: Nome de exibição do signatário.
          example: João
        cpf:
          type: string
          description: CPF do signatário (11 dígitos, sem pontuação). Obrigatório se `cnpj` não for informado.
          pattern: '^\d{11}$'
          example: '12345678901'
        cnpj:
          type: string
          description: CNPJ do signatário (14 dígitos, sem pontuação). Obrigatório se `cpf` não for informado.
          pattern: '^\d{14}$'
          example: '12345678000199'
        phone:
          type: string
          description: Telefone do signatário em formato E.164 (obrigatório quando `otpChannel` = `sms`).
          pattern: '^\+[1-9]\d{7,14}$'
          example: '+5511999998888'
        otpChannel:
          $ref: '#/components/schemas/OtpChannel'

    OtpChannel:
      type: string
      description: Canal de entrega do OTP.
      enum:
        - email
        - sms
      default: email

    SmsConfig:
      type: object
      description: Configuração de SMS para o tenant.
      properties:
        enabled:
          type: boolean
          description: Se o envio de OTP via SMS está habilitado.
        defaultChannel:
          $ref: '#/components/schemas/OtpChannel'

    ActionMetadata:
      type: object
      description: Metadados da ação autenticada (para `ACTION_AUTHENTICATION`).
      required:
        - type
        - description
      properties:
        type:
          type: string
          description: Tipo da ação.
          example: PIX_TRANSFER
        description:
          type: string
          description: Descrição da ação.
          example: Transferência PIX de R$ 5.000,00
        reference:
          type: string
          description: Referência externa da ação.
          example: ref-abc-123

    DigitalSignatureMetadata:
      type: object
      description: Metadados adicionais para assinatura digital ICP-Brasil.
      properties:
        reason:
          type: string
          description: Motivo da assinatura.
          example: Assinatura de contrato
        location:
          type: string
          description: Local da assinatura.
          example: São Paulo, SP
        contactInfo:
          type: string
          description: Informação de contato do signatário.
          example: joao@exemplo.com.br

    StepResult:
      type: object
      description: Resultado detalhado da etapa de verificação.
      properties:
        liveness:
          type: object
          description: Resultado da verificação de prova de vida.
          properties:
            confidence:
              type: number
              format: double
              description: Nível de confiança da verificação (0-100).
            provider:
              type: string
              description: Provedor de biometria utilizado.
            captureMode:
              $ref: '#/components/schemas/CaptureMode'
        match:
          type: object
          description: Resultado da comparação facial.
          properties:
            similarity:
              type: number
              format: double
              description: Similaridade facial (0-100).
            threshold:
              type: number
              format: double
              description: Limiar mínimo configurado.
        otp:
          type: object
          description: Resultado da verificação OTP.
          properties:
            verified:
              type: boolean
              description: Se o código OTP foi verificado com sucesso.
            channel:
              $ref: '#/components/schemas/OtpChannel'
        click:
          type: object
          description: Resultado do aceite por clique.
          properties:
            accepted:
              type: boolean
              description: Se o termo foi aceito.
            textVersion:
              type: string
              description: Versão do texto do termo aceito.
        digitalSignature:
          type: object
          description: Resultado da assinatura digital.
          properties:
            certificateSubject:
              type: string
              description: Subject do certificado digital.
            certificateSerial:
              type: string
              description: Número serial do certificado.
            certificateIssuer:
              type: string
              description: Emissor do certificado.
            algorithm:
              type: string
              description: Algoritmo de assinatura utilizado.
              example: SHA256withRSA
            signedAt:
              type: string
              format: date-time
              description: Momento da assinatura.
            signedPdfHash:
              type: string
              description: Hash SHA-256 do PDF assinado.
            signedPdfS3Key:
              type: string
              description: Chave S3 do PDF assinado.
            signatureFieldName:
              type: string
              description: Nome do campo de assinatura no PDF.
        documentPhotoMatch:
          type: object
          description: Resultado da comparação facial com foto de documento.
          properties:
            similarity:
              type: number
              format: double
              description: Similaridade facial entre documento e referência (0-100).
            documentType:
              type: string
              description: Tipo do documento utilizado.
              enum:
                - RG
                - CNH
                - PASSPORT
                - OTHER
            extractionConfidence:
              type: number
              format: double
              description: Confiança da extração facial do documento (0-100).
        serproIdentityCheck:
          type: object
          description: Resultado da verificação de identidade via SERPRO DataValid.
          properties:
            verified:
              type: boolean
              description: Se a identidade foi verificada com sucesso.
            cpfMatch:
              type: boolean
              description: Se o CPF corresponde ao nome informado.
            nameMatch:
              type: boolean
              description: Se o nome corresponde ao cadastro.
            birthDateMatch:
              type: boolean
              description: Se a data de nascimento corresponde ao cadastro.
            governmentDatabase:
              $ref: '#/components/schemas/GovernmentDatabase'
        governmentDbValidation:
          $ref: '#/components/schemas/GovernmentDbValidation'
        enrollmentRef:
          $ref: '#/components/schemas/EnrollmentRef'
        providerTimestamp:
          type: string
          format: date-time
          description: Timestamp do provedor externo.

    GovernmentDatabase:
      type: string
      description: Base de dados governamental utilizada para validação de identidade.
      enum:
        - SERPRO_DATAVALID

    GovernmentDbValidation:
      type: object
      description: Resultado da validação contra base de dados governamental.
      properties:
        database:
          $ref: '#/components/schemas/GovernmentDatabase'
        validatedAt:
          type: string
          format: date-time
          description: Momento da validação original.
        cpfHash:
          type: string
          description: Hash SHA-256 do CPF validado.
        biometricScore:
          type: number
          format: double
          description: Score de similaridade biométrica (0-1).
        cached:
          type: boolean
          description: Se true, uma validação anterior foi reutilizada após verificação de similaridade facial.
        cacheVerifySimilarity:
          type: number
          format: double
          description: Score do CompareFaces utilizado para aceitar o cache. Presente apenas quando cached é true.

    EnrollmentRef:
      type: object
      description: Referência ao enrollment biométrico utilizado.
      properties:
        source:
          type: string
          description: Origem da imagem de referência utilizada.
          enum:
            - ENROLLMENT
            - TRANSACTION_PROVIDED
        enrolledAt:
          type: string
          format: date-time
          description: Momento do enrollment.
        enrollmentHash:
          type: string
          description: Hash da imagem de enrollment.
        version:
          type: integer
          description: Versão do enrollment.
        imageHash:
          type: string
          description: Hash da imagem utilizada na comparação.

    # ── Request Schemas ──────────────────────────────────────────
    TokenRequest:
      type: object
      description: Requisição de token OAuth2.
      required:
        - grant_type
      properties:
        grant_type:
          type: string
          description: Tipo de concessão OAuth2.
          enum:
            - client_credentials
            - 'urn:ietf:params:oauth:grant-type:jwt-bearer'
        client_id:
          type: string
          description: Identificador do cliente OAuth2 (obrigatório para `client_credentials`).
        client_secret:
          type: string
          description: Segredo do cliente OAuth2 (obrigatório para `client_credentials` sem `client_assertion`).
        client_assertion_type:
          type: string
          description: Tipo de asserção do cliente (para autenticação via `private_key_jwt`).
          enum:
            - 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
        client_assertion:
          type: string
          description: JWT assinado pelo cliente (para autenticação via `private_key_jwt`).
        scope:
          type: string
          description: Escopos solicitados, separados por espaço.
          example: transactions:write transactions:read steps:write evidence:read

    CreateTransactionRequest:
      type: object
      description: Requisição de criação de transação.
      required:
        - purpose
        - policy
        - signer
      properties:
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        policy:
          $ref: '#/components/schemas/Policy'
        signer:
          $ref: '#/components/schemas/Signer'
        document:
          type: object
          description: Documento em base64 (opcional — pode ser enviado posteriormente). Suporta PDF e outros formatos.
          properties:
            content:
              type: string
              format: byte
              description: Conteúdo do documento codificado em base64.
            filename:
              type: string
              description: Nome do arquivo.
              example: contrato.pdf
        action:
          $ref: '#/components/schemas/ActionMetadata'
        digitalSignature:
          $ref: '#/components/schemas/DigitalSignatureMetadata'
        documentGroupId:
          type: string
          description: Identificador do grupo de documentos (para assinatura com múltiplos signatários).
        signerIndex:
          type: integer
          description: Índice do signatário no grupo (inicia em 0).
          minimum: 0
        totalSigners:
          type: integer
          description: Total de signatários no grupo.
          minimum: 1
        metadata:
          type: object
          description: Metadados customizados (chave-valor string).
          additionalProperties:
            type: string
        expiresInMinutes:
          type: integer
          description: Tempo de expiração da transação em minutos.
          minimum: 1
          example: 60

    DocumentUploadRequest:
      type: object
      description: |
        Requisição de upload de documento inline (base64).
        Suporta PDF e outros formatos (DOCX, XLSX, imagens, XML, etc.).
        Para arquivos não-PDF, o campo `filename` é obrigatório.
      required:
        - content
      properties:
        content:
          type: string
          format: byte
          description: Conteúdo do documento codificado em base64.
        filename:
          type: string
          description: |
            Nome do arquivo com extensão. Obrigatório para documentos não-PDF.
            Extensões permitidas: .docx, .doc, .xlsx, .xls, .pptx, .ppt, .odt, .ods, .odp,
            .rtf, .txt, .csv, .jpg, .jpeg, .png, .tiff, .tif, .bmp, .svg, .xml, .json, .html, .pdf
          example: contrato.docx

    PresignRequest:
      type: object
      description: Requisição de URL pré-assinada para upload.
      required:
        - contentType
        - filename
      properties:
        contentType:
          type: string
          description: Tipo MIME do arquivo.
          example: application/pdf
        filename:
          type: string
          description: Nome do arquivo.
          example: contrato.pdf

    ConfirmDocumentRequest:
      type: object
      description: Requisição de confirmação de upload pré-assinado.
      required:
        - uploadToken
      properties:
        uploadToken:
          type: string
          description: Token retornado pela operação de presign.

    StepStartRequest:
      type: object
      description: |
        Requisição de início de etapa (opcional — necessário apenas para BIOMETRIC_LIVENESS e OTP_CHALLENGE).
        - `BIOMETRIC_LIVENESS`: envie `captureMode`.
        - `OTP_CHALLENGE`: opcionalmente envie `otpChannel` para selecionar SMS ou email.
      properties:
        captureMode:
          $ref: '#/components/schemas/CaptureMode'
        otpChannel:
          $ref: '#/components/schemas/OtpChannel'

    StepCompleteRequest:
      type: object
      description: |
        Dados para conclusão da etapa. Os campos variam conforme o tipo:
        - `BIOMETRIC_LIVENESS` / `BIOMETRIC_MATCH`: envie `livenessSessionId`.
        - `OTP_VERIFY`: envie `code`.
        - `CLICK_ACCEPT`: envie `accepted`.
        - `DOCUMENT_PHOTO_MATCH`: envie `documentImage` e `documentType`.
        - `SERPRO_IDENTITY_CHECK`: envie `cpf`, `name` e `birthDate`.
      properties:
        livenessSessionId:
          type: string
          description: ID da sessão de prova de vida (para etapas biométricas).
        code:
          type: string
          description: Código OTP de 6 dígitos (para etapa OTP_VERIFY).
          example: '123456'
        accepted:
          type: boolean
          description: Aceite do termo (para etapa CLICK_ACCEPT).
        documentImage:
          type: string
          format: byte
          description: Foto do documento codificada em base64 (máx. 5MB). Para etapa `DOCUMENT_PHOTO_MATCH`.
        documentType:
          type: string
          description: Tipo do documento enviado.
          enum:
            - RG
            - CNH
            - PASSPORT
            - OTHER
        cpf:
          type: string
          pattern: '^\d{11}$'
          description: CPF do signatário (para etapa `SERPRO_IDENTITY_CHECK`).
          example: '12345678901'
        name:
          type: string
          description: Nome completo (para etapa `SERPRO_IDENTITY_CHECK`).
        birthDate:
          type: string
          format: date
          description: Data de nascimento no formato `YYYY-MM-DD` (para etapa `SERPRO_IDENTITY_CHECK`).
          example: '1990-01-15'
        geolocation:
          type: object
          description: |
            Geolocalização do dispositivo. Obrigatório quando o tenant possui `biometricRequired`
            habilitado e a etapa é biométrica.
          properties:
            latitude:
              type: number
              format: double
              description: Latitude em graus decimais.
              example: -23.5505
            longitude:
              type: number
              format: double
              description: Longitude em graus decimais.
              example: -46.6333

    PrepareSigningRequest:
      type: object
      description: Requisição de preparação para assinatura digital.
      required:
        - certificateChainPems
      properties:
        certificateChainPems:
          type: array
          description: Cadeia de certificados PEM do signatário (certificado do signatário primeiro).
          items:
            type: string
          minItems: 1

    CompleteSigningRequest:
      type: object
      description: Requisição de conclusão de assinatura digital.
      required:
        - signatureRequestId
        - rawSignatureBase64
      properties:
        signatureRequestId:
          type: string
          description: Identificador da requisição de assinatura (retornado por `/signing/prepare`).
        rawSignatureBase64:
          type: string
          description: Assinatura digital bruta codificada em base64.

    RegisterWebhookRequest:
      type: object
      description: Requisição de registro de webhook.
      required:
        - url
        - events
      properties:
        url:
          type: string
          format: uri
          description: URL do endpoint que receberá as notificações (HTTPS obrigatório).
          example: https://integrador.com.br/webhooks/signdocs
        events:
          type: array
          description: Lista de tipos de evento para os quais o webhook será acionado.
          items:
            $ref: '#/components/schemas/WebhookEventType'
          minItems: 1

    EnrollmentRequest:
      type: object
      description: |
        Requisição de enrollment biométrico.
        Todas as imagens são validadas via DetectFaces para garantir presença de exatamente um rosto.
        Quando `source` é `DOCUMENT_PHOTO`, o rosto é extraído automaticamente do documento
        e armazenado como imagem de enrollment. Requer `documentExtractionEnabled` no tenant.
      required:
        - image
        - cpf
      properties:
        image:
          type: string
          format: byte
          description: Imagem facial de referência codificada em base64 (JPEG).
        cpf:
          type: string
          pattern: '^\d{11}$'
          description: CPF do titular (11 dígitos, sem pontuação).
          example: '12345678901'
        source:
          type: string
          description: |
            Origem da imagem de enrollment.
            - `BANK_PROVIDED`: Selfie ou foto fornecida pelo banco (padrão).
            - `FIRST_LIVENESS`: Imagem capturada na primeira sessão de liveness.
            - `DOCUMENT_PHOTO`: Foto de documento (CNH/RG/passaporte). O rosto será extraído automaticamente.
          enum:
            - BANK_PROVIDED
            - FIRST_LIVENESS
            - DOCUMENT_PHOTO
          default: BANK_PROVIDED
          example: BANK_PROVIDED

    # ── Response Schemas ─────────────────────────────────────────
    TokenResponse:
      type: object
      description: Resposta de emissão de token OAuth2.
      required:
        - access_token
        - token_type
        - expires_in
        - scope
      properties:
        access_token:
          type: string
          description: Token de acesso JWT.
        token_type:
          type: string
          description: Tipo do token.
          enum:
            - Bearer
        expires_in:
          type: integer
          description: Tempo de vida do token em segundos.
          example: 3600
        scope:
          type: string
          description: Escopos concedidos, separados por espaço.
          example: transactions:write transactions:read steps:write evidence:read

    CreateTransactionResponse:
      type: object
      description: Resposta de criação de transação.
      required:
        - transactionId
        - status
        - purpose
        - policy
        - steps
        - userExternalId
        - createdAt
      properties:
        transactionId:
          type: string
          description: Identificador único da transação.
        status:
          $ref: '#/components/schemas/TransactionStatus'
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        policy:
          $ref: '#/components/schemas/Policy'
        steps:
          type: array
          description: Etapas de verificação criadas para a transação.
          items:
            type: object
            required:
              - stepId
              - type
              - status
              - order
            properties:
              stepId:
                type: string
              type:
                $ref: '#/components/schemas/StepType'
              status:
                $ref: '#/components/schemas/StepStatus'
              order:
                type: integer
        userExternalId:
          type: string
          description: Identificador externo do usuário.
        documentHash:
          type: string
          description: Hash SHA-256 do documento (presente se documento foi enviado junto).
        createdAt:
          type: string
          format: date-time
          description: Data/hora de criação (UTC).
        expiresAt:
          type: string
          format: date-time
          description: Data/hora de expiração (UTC).

    TransactionListResponse:
      type: object
      description: Resposta de listagem de transações.
      required:
        - transactions
        - count
      properties:
        transactions:
          type: array
          items:
            type: object
            required:
              - transactionId
              - status
              - purpose
              - policy
              - userExternalId
              - createdAt
              - updatedAt
            properties:
              transactionId:
                type: string
              status:
                $ref: '#/components/schemas/TransactionStatus'
              purpose:
                $ref: '#/components/schemas/TransactionPurpose'
              policy:
                $ref: '#/components/schemas/Policy'
              userExternalId:
                type: string
              documentHash:
                type: string
              documentGroupId:
                type: string
              createdAt:
                type: string
                format: date-time
              updatedAt:
                type: string
                format: date-time
        nextToken:
          type: string
          description: Token para buscar a próxima página de resultados.
        count:
          type: integer
          description: Número de transações retornadas nesta página.

    TransactionDetailResponse:
      type: object
      description: Resposta com detalhes completos da transação.
      required:
        - transactionId
        - status
        - purpose
        - policy
        - userExternalId
        - signer
        - steps
        - createdAt
        - updatedAt
      properties:
        transactionId:
          type: string
        status:
          $ref: '#/components/schemas/TransactionStatus'
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        policy:
          $ref: '#/components/schemas/Policy'
        userExternalId:
          type: string
        signer:
          $ref: '#/components/schemas/Signer'
        action:
          $ref: '#/components/schemas/ActionMetadata'
        documentHash:
          type: string
        documentGroupId:
          type: string
        signerIndex:
          type: integer
        totalSigners:
          type: integer
        metadata:
          type: object
          additionalProperties:
            type: string
        steps:
          type: array
          items:
            type: object
            required:
              - stepId
              - type
              - status
              - order
              - attempts
            properties:
              stepId:
                type: string
              type:
                $ref: '#/components/schemas/StepType'
              status:
                $ref: '#/components/schemas/StepStatus'
              order:
                type: integer
              attempts:
                type: integer
              startedAt:
                type: string
                format: date-time
              completedAt:
                type: string
                format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time

    CancelTransactionResponse:
      type: object
      description: Resposta de cancelamento de transação.
      required:
        - transactionId
        - status
        - cancelledAt
      properties:
        transactionId:
          type: string
        status:
          type: string
          enum:
            - CANCELLED
        cancelledAt:
          type: string
          format: date-time

    FinalizeResponse:
      type: object
      description: Resposta de finalização de transação.
      required:
        - transactionId
        - status
        - evidenceId
        - evidenceHash
        - completedAt
      properties:
        transactionId:
          type: string
        status:
          type: string
          enum:
            - COMPLETED
        evidenceId:
          type: string
          description: Identificador da evidência gerada. Use em `/v1/verify/{evidenceId}`.
        evidenceHash:
          type: string
          description: Hash SHA-256 do pacote de evidência.
        completedAt:
          type: string
          format: date-time

    DocumentUploadResponse:
      type: object
      description: Resposta de upload de documento.
      required:
        - transactionId
        - documentHash
        - status
        - uploadedAt
        - documentFormat
      properties:
        transactionId:
          type: string
        documentHash:
          type: string
          description: Hash SHA-256 do documento.
        documentFormat:
          type: string
          description: Formato detectado do documento.
          enum:
            - pdf
            - generic
        status:
          type: string
          enum:
            - DOCUMENT_UPLOADED
        uploadedAt:
          type: string
          format: date-time

    PresignResponse:
      type: object
      description: Resposta com URL pré-assinada para upload.
      required:
        - uploadUrl
        - uploadToken
        - s3Key
        - expiresIn
        - contentType
        - instructions
      properties:
        uploadUrl:
          type: string
          format: uri
          description: URL pré-assinada do S3 para upload via PUT.
        uploadToken:
          type: string
          description: Token para confirmar o upload via `/document/confirm`.
        s3Key:
          type: string
          description: Chave do objeto no S3.
        expiresIn:
          type: integer
          description: Tempo de expiração da URL em segundos.
          example: 600
        contentType:
          type: string
          description: Content-Type esperado no upload.
          example: application/pdf
        instructions:
          type: string
          description: Instruções para o integrador realizar o upload.

    ConfirmDocumentResponse:
      type: object
      description: Resposta de confirmação de upload pré-assinado.
      required:
        - transactionId
        - status
        - documentHash
      properties:
        transactionId:
          type: string
        status:
          type: string
          enum:
            - DOCUMENT_UPLOADED
        documentHash:
          type: string
          description: Hash SHA-256 do documento confirmado.
        documentFormat:
          type: string
          description: Formato detectado do documento.
          enum:
            - pdf
            - generic

    DocumentDownloadResponse:
      type: object
      description: |
        Resposta com URLs de download do documento.
        Para documentos PDF, retorna `signedUrl` (PDF com assinatura embutida).
        Para documentos genéricos, retorna `signatureUrl` (arquivo .p7s CAdES enveloping, com conteúdo anexado).
      required:
        - transactionId
        - expiresIn
      properties:
        transactionId:
          type: string
        documentHash:
          type: string
        documentFormat:
          type: string
          description: Formato do documento.
          enum:
            - pdf
            - generic
        originalUrl:
          type: string
          format: uri
          description: URL pré-assinada para download do documento original.
        signedUrl:
          type: string
          format: uri
          description: URL pré-assinada para download do PDF assinado (apenas para `documentFormat=pdf`).
        signatureUrl:
          type: string
          format: uri
          description: |
            URL pré-assinada para download do arquivo .p7s (CAdES enveloping, conteúdo anexado).
            Retornado apenas para `documentFormat=generic`. Verifique com:
            `openssl cms -verify -inform DER -in signature.p7s -CAfile icpbrasil-roots.pem`
        expiresIn:
          type: integer
          description: Tempo de expiração das URLs em segundos.
          example: 3600

    StepListResponse:
      type: object
      description: Resposta com lista de etapas da transação.
      required:
        - steps
      properties:
        steps:
          type: array
          items:
            type: object
            required:
              - stepId
              - type
              - status
              - order
              - attempts
              - maxAttempts
            properties:
              stepId:
                type: string
              type:
                $ref: '#/components/schemas/StepType'
              status:
                $ref: '#/components/schemas/StepStatus'
              order:
                type: integer
              attempts:
                type: integer
              maxAttempts:
                type: integer
              captureMode:
                $ref: '#/components/schemas/CaptureMode'
              startedAt:
                type: string
                format: date-time
              completedAt:
                type: string
                format: date-time
              error:
                type: string

    StepStartResponse:
      type: object
      description: |
        Resposta de início de etapa. Os campos retornados variam conforme o tipo:
        - `BIOMETRIC_LIVENESS` (BANK_APP): `livenessSessionId`
        - `BIOMETRIC_LIVENESS` (HOSTED_PAGE): `hostedUrl`
        - `OTP_CHALLENGE`: `message`, `otpCode` (apenas sandbox)
        - `OTP_VERIFY`: `message`
        - `CLICK_ACCEPT`: `message`
        - `DIGITAL_SIGN_A1`: `message`
      required:
        - stepId
        - type
        - status
      properties:
        stepId:
          type: string
        type:
          $ref: '#/components/schemas/StepType'
        status:
          $ref: '#/components/schemas/StepStatus'
        livenessSessionId:
          type: string
          description: ID da sessão Rekognition (modo BANK_APP).
        hostedUrl:
          type: string
          format: uri
          description: URL da página hospedada de liveness (modo HOSTED_PAGE).
        message:
          type: string
          description: Mensagem informativa sobre a etapa.
        otpCode:
          type: string
          description: Código OTP (retornado apenas em ambiente sandbox para facilitar testes).
        channel:
          $ref: '#/components/schemas/OtpChannel'

    StepCompleteResponse:
      type: object
      description: Resposta de conclusão de etapa.
      required:
        - stepId
        - type
        - status
        - attempts
      properties:
        stepId:
          type: string
        type:
          $ref: '#/components/schemas/StepType'
        status:
          $ref: '#/components/schemas/StepStatus'
        attempts:
          type: integer
          description: Número de tentativas realizadas.
        result:
          $ref: '#/components/schemas/StepResult'

    PrepareSigningResponse:
      type: object
      description: Resposta de preparação para assinatura digital.
      required:
        - signatureRequestId
        - hashToSign
        - hashAlgorithm
        - signatureAlgorithm
      properties:
        signatureRequestId:
          type: string
          description: Identificador da requisição de assinatura.
        hashToSign:
          type: string
          description: Hash que deve ser assinado pelo certificado A1 do cliente.
        hashAlgorithm:
          type: string
          description: Algoritmo de hash utilizado.
          enum:
            - SHA-256
        signatureAlgorithm:
          type: string
          description: Algoritmo de assinatura esperado.
          enum:
            - RSASSA-PKCS1-v1_5

    SigningCompleteResponse:
      type: object
      description: |
        Resposta de conclusão de assinatura digital.
        Para PDF: inclui `signedPdfHash` e `signatureFieldName`.
        Para genérico: inclui `signedP7sHash` e `documentFormat`.
      required:
        - status
        - result
      properties:
        stepId:
          type: string
        status:
          type: string
          enum:
            - COMPLETED
        result:
          type: object
          required:
            - digitalSignature
          properties:
            digitalSignature:
              type: object
              properties:
                certificateSubject:
                  type: string
                certificateSerial:
                  type: string
                certificateIssuer:
                  type: string
                algorithm:
                  type: string
                  example: SHA256withRSA
                signedAt:
                  type: string
                  format: date-time
                signedPdfHash:
                  type: string
                  description: Hash SHA-256 do PDF assinado (apenas para documentos PDF).
                signatureFieldName:
                  type: string
                  description: Nome do campo de assinatura no PDF (apenas para documentos PDF).
                signedP7sHash:
                  type: string
                  description: Hash SHA-256 do arquivo .p7s (apenas para documentos genéricos).
                documentFormat:
                  type: string
                  description: Formato do documento assinado.
                  enum:
                    - pdf
                    - generic

    EvidenceResponse:
      type: object
      description: Resposta com metadados da evidência.
      required:
        - evidenceId
        - transactionId
        - evidenceHash
        - signatureAlg
        - createdAt
        - downloadUrl
      properties:
        evidenceId:
          type: string
        transactionId:
          type: string
        evidenceHash:
          type: string
          description: Hash SHA-256 do pacote de evidência.
        signatureAlg:
          type: string
          description: Algoritmo de assinatura utilizado.
          example: ECDSA_SHA_256
        createdAt:
          type: string
          format: date-time
        downloadUrl:
          type: string
          format: uri
          description: URL pré-assinada para download do pacote de evidência (.p7m).

    EnrollmentResponse:
      type: object
      description: Resposta de enrollment biométrico.
      required:
        - userExternalId
        - enrollmentHash
        - enrollmentVersion
        - enrollmentSource
        - enrolledAt
        - cpf
        - faceConfidence
      properties:
        userExternalId:
          type: string
        enrollmentHash:
          type: string
          description: Hash SHA-256 da imagem de enrollment (face crop quando DOCUMENT_PHOTO).
        enrollmentVersion:
          type: integer
          description: Versão do enrollment (incrementada a cada atualização).
        enrollmentSource:
          type: string
          description: Origem do enrollment.
          enum:
            - BANK_PROVIDED
            - FIRST_LIVENESS
            - DOCUMENT_PHOTO
        enrolledAt:
          type: string
          format: date-time
        cpf:
          type: string
          description: CPF do titular.
        faceConfidence:
          type: number
          description: Confiança da detecção facial (0-100%).
          example: 99.8
        documentImageHash:
          type: string
          description: Hash SHA-256 do documento original. Presente apenas quando `enrollmentSource` é `DOCUMENT_PHOTO`.
        extractionConfidence:
          type: number
          description: Confiança da extração facial do documento. Presente apenas quando `enrollmentSource` é `DOCUMENT_PHOTO`.

    CombinedStampResponse:
      type: object
      description: Resposta de geração de carimbo combinado.
      required:
        - groupId
        - signerCount
        - downloadUrl
        - expiresIn
      properties:
        groupId:
          type: string
        signerCount:
          type: integer
          description: Número de signatários incluídos no carimbo.
        downloadUrl:
          type: string
          format: uri
          description: URL pré-assinada para download do documento com carimbo combinado.
        expiresIn:
          type: integer
          description: Tempo de expiração da URL em segundos.
          example: 3600

    RegisterWebhookResponse:
      type: object
      description: |
        Resposta de registro de webhook. O campo `secret` é retornado **apenas nesta resposta** —
        armazene-o com segurança para validar assinaturas HMAC-SHA256.
      required:
        - webhookId
        - url
        - secret
        - events
        - status
        - createdAt
      properties:
        webhookId:
          type: string
        url:
          type: string
          format: uri
        secret:
          type: string
          description: Segredo HMAC-SHA256 para validação de payloads. Exibido apenas na criação.
        events:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
        status:
          type: string
          enum:
            - ACTIVE
        createdAt:
          type: string
          format: date-time

    WebhookListResponse:
      type: object
      description: Resposta de listagem de webhooks.
      required:
        - webhooks
        - count
      properties:
        webhooks:
          type: array
          items:
            type: object
            required:
              - webhookId
              - url
              - events
              - status
              - createdAt
              - updatedAt
            properties:
              webhookId:
                type: string
              url:
                type: string
                format: uri
              events:
                type: array
                items:
                  $ref: '#/components/schemas/WebhookEventType'
              status:
                type: string
                enum:
                  - ACTIVE
                  - DISABLED
              createdAt:
                type: string
                format: date-time
              updatedAt:
                type: string
                format: date-time
        count:
          type: integer
          description: Número total de webhooks registrados.

    WebhookTestResponse:
      type: object
      description: Resposta de teste de webhook.
      required:
        - webhookId
        - testDelivery
      properties:
        webhookId:
          type: string
        testDelivery:
          type: object
          required:
            - httpStatus
            - success
            - timestamp
          properties:
            httpStatus:
              type: integer
              description: Código HTTP retornado pelo endpoint.
            success:
              type: boolean
              description: Se a entrega de teste foi bem-sucedida.
            error:
              type: string
              description: Mensagem de erro (se a entrega falhou).
            timestamp:
              type: string
              format: date-time
              description: Momento da tentativa de entrega.

    VerificationResponse:
      type: object
      description: Resposta de verificação pública de evidência.
      required:
        - evidenceId
        - status
        - transactionId
        - purpose
        - evidenceHash
        - policy
        - steps
        - createdAt
        - completedAt
      properties:
        evidenceId:
          type: string
        status:
          type: string
          description: Status da verificação da evidência.
        transactionId:
          type: string
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        documentHash:
          type: string
        evidenceHash:
          type: string
        policy:
          $ref: '#/components/schemas/Policy'
        steps:
          type: array
          items:
            type: object
            required:
              - type
              - status
              - order
            properties:
              type:
                $ref: '#/components/schemas/StepType'
              status:
                $ref: '#/components/schemas/StepStatus'
              order:
                type: integer
              completedAt:
                type: string
                format: date-time
        signer:
          type: object
          description: Dados públicos do signatário.
          properties:
            displayName:
              type: string
            cpfCnpj:
              type: string
              description: |
                CPF ou CNPJ do signatário, quando informado pelo emissor da
                transação. Exposto na verificação pública para permitir que
                terceiros confiram a identidade do signatário.
        tenantName:
          type: string
          description: Nome da empresa/tenant que criou a transação.
        tenantCnpj:
          type: string
          description: CNPJ da empresa/tenant que criou a transação.
        createdAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time

    EnvelopeVerificationResponse:
      type: object
      description: |
        Resposta de verificação pública de envelope multi-signatário.
        Inclui status do envelope, lista de signatários e URLs de download
        consolidadas. Cada signatário traz o `evidenceId` da sua evidência
        individual para drill-down via `GET /v1/verify/{evidenceId}`.
      required:
        - envelopeId
        - status
        - signingMode
        - totalSigners
        - completedSessions
        - documentHash
        - signers
        - createdAt
      properties:
        envelopeId:
          type: string
        status:
          $ref: '#/components/schemas/EnvelopeStatus'
        signingMode:
          type: string
          enum:
            - PARALLEL
            - SEQUENTIAL
        totalSigners:
          type: integer
          description: Número total de signatários esperados no envelope.
        completedSessions:
          type: integer
          description: Quantidade de sessões já concluídas.
        documentHash:
          type: string
          description: Hash SHA-256 do documento original do envelope.
        tenantName:
          type: string
          description: Nome da empresa/tenant que criou o envelope.
        tenantCnpj:
          type: string
          description: CNPJ da empresa/tenant que criou o envelope.
        signers:
          type: array
          description: |
            Lista de signatários do envelope, ordenada por `signerIndex`.
          items:
            type: object
            required:
              - signerIndex
              - displayName
              - status
            properties:
              signerIndex:
                type: integer
                description: Posição do signatário no envelope (1-based).
              displayName:
                type: string
              cpfCnpj:
                type: string
                description: CPF ou CNPJ do signatário, quando informado.
              status:
                $ref: '#/components/schemas/TransactionStatus'
              policyProfile:
                type: string
                description: |
                  Perfil de política aplicado ao signatário (ex.:
                  `DIGITAL_CERTIFICATE`, `OTP_ONLY`).
              evidenceId:
                type: string
                description: |
                  Identificador da evidência individual deste signatário,
                  presente apenas quando a sessão foi concluída. Use com
                  `GET /v1/verify/{evidenceId}` para detalhes.
              completedAt:
                type: string
                format: date-time
        downloads:
          type: object
          description: |
            Artefatos consolidados disponíveis para download. Apenas
            populado para envelopes em status `COMPLETED`. Os campos
            individuais são mutuamente exclusivos por formato do
            documento (PDF vs não-PDF).
          properties:
            combinedSignedPdf:
              description: |
                PDF combinado com todas as assinaturas digitais embutidas.
                Presente apenas quando o envelope é um PDF e todos os
                signatários já concluíram. Pode ser omitido para envelopes
                em andamento ou para envelopes não-PDF.
              type: object
              required:
                - url
                - filename
              properties:
                url:
                  type: string
                  format: uri
                filename:
                  type: string
            consolidatedSignature:
              description: |
                Arquivo PKCS#7 / CMS detached (`.p7s`) consolidado
                contendo todos os `SignerInfo` do envelope em um único
                arquivo. Presente apenas quando o envelope é um documento
                não-PDF assinado com certificado digital e todos os
                signatários concluíram. Pode ser verificado com:

                ```
                openssl cms -verify -inform DER -in signature.p7s \
                  -CAfile icpbrasil-roots.pem
                ```
              type: object
              required:
                - url
                - filename
              properties:
                url:
                  type: string
                  format: uri
                filename:
                  type: string
        createdAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
          description: |
            Data/hora em que o último signatário concluiu a sessão.
            Presente apenas quando o envelope está em status `COMPLETED`.

    VerificationDownloadsResponse:
      type: object
      description: Resposta com URLs de download dos artefatos de evidência.
      required:
        - evidenceId
        - downloads
      properties:
        evidenceId:
          type: string
        downloads:
          type: object
          description: |
            Artefatos disponíveis para download. Os campos
            `originalDocument`, `evidencePack` e `finalPdf` estão sempre
            presentes mas podem ser `null` quando o arquivo correspondente
            ainda não foi gerado. O campo `signedSignature` é omitido
            inteiramente para evidências que pertencem a um envelope
            multi-signatário.
          properties:
            originalDocument:
              description: |
                Documento original enviado pelo cliente. Apenas disponível
                após a transação atingir o status `COMPLETED` (para evitar
                vazamento de rascunhos). Caso contrário será `null`.
              oneOf:
                - type: 'null'
                - type: object
                  required:
                    - url
                    - filename
                  properties:
                    url:
                      type: string
                      format: uri
                    filename:
                      type: string
            evidencePack:
              description: |
                Pacote de evidência (.p7m). `null` enquanto o pacote ainda
                não foi gerado.
              oneOf:
                - type: 'null'
                - type: object
                  required:
                    - url
                    - filename
                  properties:
                    url:
                      type: string
                      format: uri
                    filename:
                      type: string
            finalPdf:
              description: |
                PDF final com carimbo visual de assinatura. `null` enquanto
                o PDF final ainda não foi gerado.
              oneOf:
                - type: 'null'
                - type: object
                  required:
                    - url
                    - filename
                  properties:
                    url:
                      type: string
                      format: uri
                    filename:
                      type: string
            signedSignature:
              description: |
                Arquivo PKCS#7 / CMS detached (`.p7s`) gerado por assinatura
                digital com certificado A1/A3 sobre um documento não-PDF.

                **Apenas presente para sessões de assinatura standalone**
                (single-signer). Para evidências que pertencem a um envelope
                multi-signatário este campo é omitido — a assinatura
                consolidada de todos os signatários é exposta no endpoint
                `GET /v1/verify/envelope/{envelopeId}` como
                `downloads.consolidatedSignature`. Mesmo em sessões
                standalone pode ser `null` enquanto o arquivo ainda não foi
                gerado.
              oneOf:
                - type: 'null'
                - type: object
                  required:
                    - url
                    - filename
                  properties:
                    url:
                      type: string
                      format: uri
                    filename:
                      type: string

    # ── Signing Sessions Schemas ───────────────────────────────────
    SigningSessionStatus:
      type: string
      description: Status da sessão de assinatura.
      enum:
        - ACTIVE
        - COMPLETED
        - CANCELLED
        - EXPIRED
        - FAILED

    SigningSessionAppearance:
      type: object
      description: Configuração visual da página de assinatura hospedada.
      properties:
        brandColor:
          type: string
          pattern: '^#[0-9a-fA-F]{6}$'
          description: Cor primária da marca (hex).
          example: '#1a5276'
        logoUrl:
          type: string
          format: uri
          description: URL HTTPS do logotipo da empresa.
          example: 'https://cdn.example.com/logo.png'
        companyName:
          type: string
          description: Nome da empresa exibido na página.
          example: 'Empresa Exemplo Ltda'
        backgroundColor:
          type: string
          pattern: '^#[0-9a-fA-F]{6}$'
          description: Cor de fundo (hex).
          example: '#ffffff'
        textColor:
          type: string
          pattern: '^#[0-9a-fA-F]{6}$'
          description: Cor do texto (hex).
          example: '#333333'
        buttonTextColor:
          type: string
          pattern: '^#[0-9a-fA-F]{6}$'
          description: Cor do texto dos botões (hex).
          example: '#ffffff'
        borderRadius:
          type: string
          pattern: '^\d+px$'
          description: Raio de borda dos elementos (formato "Npx").
          example: '12px'
        headerStyle:
          type: string
          description: Estilo do cabeçalho da página.
          enum:
            - full
            - minimal
            - none
          default: full
        fontFamily:
          type: string
          description: Família tipográfica.
          enum:
            - system-ui
            - Inter
            - Roboto
            - Open Sans
            - Lato
            - Montserrat
            - Source Sans Pro
            - Nunito
            - Poppins
            - Raleway
            - Ubuntu
          default: system-ui

    CreateSigningSessionRequest:
      type: object
      description: Requisição para criar uma sessão de assinatura expressa.
      required:
        - purpose
        - policy
        - signer
      properties:
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        policy:
          $ref: '#/components/schemas/Policy'
        signer:
          $ref: '#/components/schemas/Signer'
        document:
          type: object
          description: Documento inline (base64). Máximo 10 MB. Suporta PDF e outros formatos.
          properties:
            content:
              type: string
              format: byte
              description: Conteúdo do documento em base64.
            filename:
              type: string
              description: Nome do arquivo.
              example: contrato.pdf
        action:
          $ref: '#/components/schemas/ActionMetadata'
        returnUrl:
          type: string
          format: uri
          description: URL de retorno após conclusão. O `session_id` será adicionado como query parameter.
          example: 'https://app.example.com/done'
        cancelUrl:
          type: string
          format: uri
          description: URL de retorno em caso de cancelamento pelo signatário.
          example: 'https://app.example.com/cancelled'
        metadata:
          type: object
          description: Metadados livres (chave-valor). Chaves até 256 chars, valores até 1024 chars.
          additionalProperties:
            type: string
        locale:
          type: string
          description: Idioma da página de assinatura.
          enum:
            - pt-BR
            - en
            - es
          default: pt-BR
        expiresInMinutes:
          type: integer
          description: Tempo de expiração em minutos (5 a 1440). Padrão 60.
          minimum: 5
          maximum: 1440
          default: 60
        appearance:
          $ref: '#/components/schemas/SigningSessionAppearance'
        owner:
          $ref: '#/components/schemas/Owner'

    CreateSigningSessionResponse:
      type: object
      description: Resposta da criação de sessão de assinatura.
      required:
        - sessionId
        - transactionId
        - status
        - url
        - clientSecret
        - expiresAt
        - createdAt
      properties:
        sessionId:
          type: string
          description: Identificador único da sessão.
          example: '01JXYZ...'
        transactionId:
          type: string
          description: Identificador da transação subjacente.
          example: '01JXYZ...'
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        url:
          type: string
          format: uri
          description: URL da página de assinatura hospedada.
          example: 'https://sign.signdocs.com.br/s/01JXYZ...'
        clientSecret:
          type: string
          description: Token de sessão para autenticação no widget/redirect. Formato `ss_secret_` + JWT.
          example: 'ss_secret_eyJhbGciOi...'
        expiresAt:
          type: string
          format: date-time
          description: Data/hora de expiração da sessão (ISO 8601 UTC).
        createdAt:
          type: string
          format: date-time
          description: Data/hora de criação (ISO 8601 UTC).
        inviteSent:
          type: boolean
          description: |
            Verdadeiro quando a SignDocs enviou um e-mail de convite para `signer.email`
            no momento da criação da sessão. Só é populado quando `owner` foi informado
            e `signer.email` é diferente de `owner.email` (case-insensitive).

    SigningSessionBootstrap:
      type: object
      description: Dados de bootstrap para o aplicativo de assinatura hospedado.
      required:
        - sessionId
        - transactionId
        - status
        - purpose
        - signer
        - steps
        - locale
        - expiresAt
      properties:
        sessionId:
          type: string
        transactionId:
          type: string
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        purpose:
          $ref: '#/components/schemas/TransactionPurpose'
        signer:
          type: object
          description: Dados mascarados do signatário.
          properties:
            name:
              type: string
              example: 'Maria Silva'
            maskedEmail:
              type: string
              example: 'm***a@example.com'
            maskedCpf:
              type: string
              example: '***.***.***-01'
        steps:
          type: array
          items:
            type: object
            properties:
              stepId:
                type: string
              type:
                type: string
              status:
                type: string
              order:
                type: integer
        document:
          type: object
          description: Documento para visualização (URL pré-assinada).
          properties:
            presignedUrl:
              type: string
              format: uri
            filename:
              type: string
            hash:
              type: string
        action:
          $ref: '#/components/schemas/ActionMetadata'
        appearance:
          $ref: '#/components/schemas/SigningSessionAppearance'
        locale:
          type: string
        returnUrl:
          type: string
          format: uri
        cancelUrl:
          type: string
          format: uri
        expiresAt:
          type: string
          format: date-time

    AdvanceSessionRequest:
      type: object
      description: |
        Requisição para avançar a sessão de assinatura. Os campos obrigatórios dependem da ação:

        | Ação | Campos obrigatórios | Descrição |
        |------|---------------------|-----------|
        | `accept` | — | Aceita o termo (CLICK_ACCEPT) |
        | `verify_otp` | `otpCode` | Verifica o código OTP |
        | `resend_otp` | — | Reenvia o desafio OTP |
        | `start_liveness` | — | Inicia sessão de liveness biométrico |
        | `complete_liveness` | `livenessSessionId` | Conclui liveness com resultado Rekognition |
        | `prepare_signing` | `certificateChainPems` | Prepara assinatura digital (retorna hash) |
        | `complete_signing` | `signatureRequestId`, `rawSignatureBase64` | Conclui assinatura digital |
      required:
        - action
      properties:
        action:
          type: string
          description: Ação a executar na sessão.
          enum:
            - accept
            - verify_otp
            - resend_otp
            - start_liveness
            - complete_liveness
            - prepare_signing
            - complete_signing
        otpCode:
          type: string
          description: Código OTP de 6 dígitos (obrigatório para `verify_otp`).
          example: '123456'
        livenessSessionId:
          type: string
          description: ID da sessão Rekognition (obrigatório para `complete_liveness`).
        certificateChainPems:
          type: array
          items:
            type: string
          description: Cadeia de certificados PEM (obrigatório para `prepare_signing`).
        signatureRequestId:
          type: string
          description: ID da requisição de assinatura (obrigatório para `complete_signing`).
        rawSignatureBase64:
          type: string
          format: byte
          description: Assinatura bruta em base64 (obrigatório para `complete_signing`).
        geolocation:
          type: object
          description: |
            Geolocalização do dispositivo. Aceito nas ações `accept`, `verify_otp`,
            `complete_liveness` e `complete_signing`. Obrigatório em `complete_liveness`
            quando o tenant possui `biometricRequired` habilitado.
          properties:
            latitude:
              type: number
              format: double
              description: Latitude em graus decimais (-90 a 90).
              example: -23.5505
            longitude:
              type: number
              format: double
              description: Longitude em graus decimais (-180 a 180).
              example: -46.6333
            accuracy:
              type: number
              format: double
              description: Precisão em metros (>= 0).
              example: 10
            source:
              type: string
              description: Origem da geolocalização.
              enum:
                - GPS
                - IP
                - WIFI
                - CELL

    AdvanceSessionResponse:
      type: object
      description: Resposta do avanço da sessão.
      required:
        - sessionId
        - status
      properties:
        sessionId:
          type: string
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        currentStep:
          type: object
          description: Etapa atual sendo processada.
          properties:
            stepId:
              type: string
            type:
              type: string
            status:
              type: string
        nextStep:
          type: object
          description: Próxima etapa pendente (se houver).
          properties:
            stepId:
              type: string
            type:
              type: string
        evidenceId:
          type: string
          description: ID da evidência (presente quando status é COMPLETED).
        redirectUrl:
          type: string
          format: uri
          description: URL de redirecionamento (returnUrl + session_id).
        completedAt:
          type: string
          format: date-time
          description: Data/hora de conclusão (quando status é COMPLETED).
        hostedUrl:
          type: string
          format: uri
          description: URL da página de liveness hospedada (para `start_liveness`).
        livenessSessionId:
          type: string
          description: ID da sessão Rekognition (para `start_liveness`).
        signatureRequestId:
          type: string
          description: ID da requisição de assinatura (para `prepare_signing`).
        hashToSign:
          type: string
          description: Hash a ser assinado pelo certificado (para `prepare_signing`).
        hashAlgorithm:
          type: string
          description: Algoritmo do hash (ex. SHA-256).
        signatureAlgorithm:
          type: string
          description: Algoritmo de assinatura esperado (ex. SHA256withRSA).
        sandbox:
          type: object
          description: Dados de sandbox (apenas em HML). Contém o OTP para testes.
          properties:
            otpCode:
              type: string
              example: '123456'

    SigningSessionStatusResponse:
      type: object
      description: Resposta leve de status da sessão (para polling).
      required:
        - sessionId
        - transactionId
        - status
      properties:
        sessionId:
          type: string
        transactionId:
          type: string
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        completedAt:
          type: string
          format: date-time
        evidenceId:
          type: string

    CancelSigningSessionResponse:
      type: object
      description: Resposta do cancelamento de sessão.
      required:
        - sessionId
        - transactionId
        - status
        - cancelledAt
      properties:
        sessionId:
          type: string
        transactionId:
          type: string
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        cancelledAt:
          type: string
          format: date-time

    SigningSessionListResponse:
      type: object
      description: Resposta da listagem de sessões de assinatura.
      required:
        - sessions
      properties:
        sessions:
          type: array
          items:
            $ref: '#/components/schemas/SigningSessionSummary'
        nextCursor:
          type: string
          description: Cursor para a próxima página. Ausente se não houver mais resultados.

    SigningSessionSummary:
      type: object
      description: Resumo de uma sessão de assinatura.
      required:
        - sessionId
        - transactionId
        - status
        - createdAt
        - expiresAt
        - locale
      properties:
        sessionId:
          type: string
        transactionId:
          type: string
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        createdAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
        locale:
          type: string

    # ── Envelope Schemas ──────────────────────────────────────────
    EnvelopeStatus:
      type: string
      description: Status possíveis de um envelope.
      enum:
        - CREATED
        - ACTIVE
        - COMPLETED
        - CANCELLED
        - EXPIRED

    CreateEnvelopeRequest:
      type: object
      description: Requisição para criar um envelope para múltiplos signatários.
      required:
        - signingMode
        - totalSigners
        - document
      properties:
        signingMode:
          type: string
          description: Modo de assinatura do envelope.
          enum:
            - PARALLEL
            - SEQUENTIAL
          example: PARALLEL
        totalSigners:
          type: integer
          description: Número total de signatários esperados (mínimo 2).
          minimum: 2
          example: 3
        document:
          type: object
          description: Documento inline (base64). Máximo 10 MB. Suporta PDF e outros formatos.
          required:
            - content
          properties:
            content:
              type: string
              format: byte
              description: Conteúdo do documento em base64.
            filename:
              type: string
              description: Nome do arquivo.
              example: contrato-multilateral.pdf
        metadata:
          type: object
          description: Metadados livres (chave-valor). Chaves até 256 chars, valores até 1024 chars.
          additionalProperties:
            type: string
        locale:
          type: string
          description: Idioma padrão das sessões do envelope.
          enum:
            - pt-BR
            - en
            - es
          default: pt-BR
        returnUrl:
          type: string
          format: uri
          description: URL de retorno padrão após conclusão de cada sessão.
          example: 'https://app.example.com/done'
        cancelUrl:
          type: string
          format: uri
          description: URL de retorno padrão em caso de cancelamento.
          example: 'https://app.example.com/cancelled'
        expiresInMinutes:
          type: integer
          description: Tempo de expiração do envelope em minutos (mínimo 5). Padrão 1440 (24h).
          minimum: 5
          default: 1440
        owner:
          $ref: '#/components/schemas/Owner'

    CreateEnvelopeResponse:
      type: object
      description: Resposta da criação de envelope.
      required:
        - envelopeId
        - status
        - signingMode
        - totalSigners
        - documentHash
        - createdAt
        - expiresAt
      properties:
        envelopeId:
          type: string
          description: Identificador único do envelope.
          example: 'env_01JXYZ...'
        status:
          $ref: '#/components/schemas/EnvelopeStatus'
        signingMode:
          type: string
          description: Modo de assinatura configurado.
          enum:
            - PARALLEL
            - SEQUENTIAL
          example: PARALLEL
        totalSigners:
          type: integer
          description: Número total de signatários esperados.
          example: 3
        documentHash:
          type: string
          description: Hash SHA-256 do documento enviado.
          example: 'sha256:a1b2c3d4e5f6...'
        createdAt:
          type: string
          format: date-time
          description: Data/hora de criação (ISO 8601 UTC).
        expiresAt:
          type: string
          format: date-time
          description: Data/hora de expiração do envelope (ISO 8601 UTC).

    AddEnvelopeSessionRequest:
      type: object
      description: Requisição para adicionar uma sessão de signatário ao envelope.
      required:
        - signer
        - policy
        - signerIndex
      properties:
        signer:
          type: object
          description: |
            Dados do signatário.
            É obrigatório informar pelo menos um entre `cpf` e `cnpj`.
          required:
            - name
            - userExternalId
          anyOf:
            - required: [cpf]
            - required: [cnpj]
          properties:
            name:
              type: string
              description: Nome completo do signatário.
              example: Maria Oliveira
            userExternalId:
              type: string
              description: Identificador externo do usuário no sistema do integrador.
              example: usr-67890
            cpf:
              type: string
              description: CPF do signatário (11 dígitos, sem pontuação).
              pattern: '^\d{11}$'
              example: '98765432109'
            cnpj:
              type: string
              description: CNPJ do signatário (14 dígitos, sem pontuação).
              pattern: '^\d{14}$'
              example: '98765432000188'
            email:
              type: string
              format: email
              description: E-mail do signatário (obrigatório para etapas OTP).
              example: maria@exemplo.com.br
            phone:
              type: string
              description: Telefone do signatário em formato E.164.
              pattern: '^\+[1-9]\d{7,14}$'
              example: '+5511988887777'
            birthDate:
              type: string
              format: date
              description: Data de nascimento do signatário (YYYY-MM-DD).
              example: '1990-05-15'
            otpChannel:
              $ref: '#/components/schemas/OtpChannel'
        policy:
          type: object
          description: Política de verificação de identidade da sessão.
          required:
            - profile
          properties:
            profile:
              type: string
              description: Perfil de verificação de identidade.
              example: STANDARD
        purpose:
          type: string
          description: Finalidade da sessão.
          enum:
            - DOCUMENT_SIGNATURE
            - ACTION_AUTHENTICATION
          default: DOCUMENT_SIGNATURE
        signerIndex:
          type: integer
          description: Índice do signatário no envelope (mínimo 1). Define a ordem em modo SEQUENTIAL.
          minimum: 1
          example: 1
        returnUrl:
          type: string
          format: uri
          description: URL de retorno após conclusão desta sessão. Sobrescreve a URL do envelope.
          example: 'https://app.example.com/signer/1/done'
        cancelUrl:
          type: string
          format: uri
          description: URL de retorno em caso de cancelamento. Sobrescreve a URL do envelope.
          example: 'https://app.example.com/signer/1/cancelled'
        metadata:
          type: object
          description: Metadados livres específicos desta sessão.
          additionalProperties:
            type: string

    AddEnvelopeSessionResponse:
      type: object
      description: Resposta da adição de sessão de signatário ao envelope.
      required:
        - sessionId
        - transactionId
        - signerIndex
        - status
        - url
        - clientSecret
        - expiresAt
      properties:
        sessionId:
          type: string
          description: Identificador único da sessão.
          example: '01JXYZ...'
        transactionId:
          type: string
          description: Identificador da transação subjacente.
          example: '01JXYZ...'
        signerIndex:
          type: integer
          description: Índice do signatário no envelope.
          example: 1
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        url:
          type: string
          format: uri
          description: URL da página de assinatura hospedada.
          example: 'https://sign.signdocs.com.br/s/01JXYZ...'
        clientSecret:
          type: string
          description: Token de sessão para autenticação no widget/redirect. Formato `ss_secret_` + JWT.
          example: 'ss_secret_eyJhbGciOi...'
        expiresAt:
          type: string
          format: date-time
          description: Data/hora de expiração da sessão (ISO 8601 UTC).
        inviteSent:
          type: boolean
          description: |
            Verdadeiro quando a SignDocs enviou um e-mail de convite para `signer.email`.
            Só é populado quando o envelope foi criado com `owner` e `signer.email`
            é diferente de `owner.email` (case-insensitive).

    EnvelopeSessionSummary:
      type: object
      description: Resumo de uma sessão de signatário dentro do envelope.
      required:
        - sessionId
        - transactionId
        - signerIndex
        - signerName
        - status
      properties:
        sessionId:
          type: string
          description: Identificador único da sessão.
          example: '01JXYZ...'
        transactionId:
          type: string
          description: Identificador da transação subjacente.
          example: '01JXYZ...'
        signerIndex:
          type: integer
          description: Índice do signatário no envelope.
          example: 1
        signerName:
          type: string
          description: Nome do signatário.
          example: Maria Oliveira
        status:
          $ref: '#/components/schemas/SigningSessionStatus'
        completedAt:
          type: string
          format: date-time
          description: Data/hora de conclusão (presente quando status é COMPLETED).
        evidenceId:
          type: string
          description: ID da evidência (presente quando status é COMPLETED).

    GetEnvelopeResponse:
      type: object
      description: Resposta com detalhes completos do envelope.
      required:
        - envelopeId
        - status
        - signingMode
        - totalSigners
        - addedSessions
        - completedSessions
        - documentHash
        - sessions
        - createdAt
        - updatedAt
        - expiresAt
      properties:
        envelopeId:
          type: string
          description: Identificador único do envelope.
          example: 'env_01JXYZ...'
        status:
          $ref: '#/components/schemas/EnvelopeStatus'
        signingMode:
          type: string
          description: Modo de assinatura configurado.
          enum:
            - PARALLEL
            - SEQUENTIAL
          example: PARALLEL
        totalSigners:
          type: integer
          description: Número total de signatários esperados.
          example: 3
        addedSessions:
          type: integer
          description: Número de sessões adicionadas ao envelope.
          example: 2
        completedSessions:
          type: integer
          description: Número de sessões concluídas.
          example: 1
        documentHash:
          type: string
          description: Hash SHA-256 do documento.
          example: 'sha256:a1b2c3d4e5f6...'
        sessions:
          type: array
          description: Lista de sessões de signatários do envelope.
          items:
            $ref: '#/components/schemas/EnvelopeSessionSummary'
        createdAt:
          type: string
          format: date-time
          description: Data/hora de criação (ISO 8601 UTC).
        updatedAt:
          type: string
          format: date-time
          description: Data/hora da última atualização (ISO 8601 UTC).
        expiresAt:
          type: string
          format: date-time
          description: Data/hora de expiração do envelope (ISO 8601 UTC).
        combinedSignedPdfUrl:
          type: string
          format: uri
          description: URL pré-assinada do documento com carimbo combinado (presente quando todas as sessões foram concluídas).

    EnvelopeCombinedStampResponse:
      type: object
      description: Resposta com URL do documento com carimbo combinado de todas as assinaturas.
      required:
        - envelopeId
        - downloadUrl
        - expiresIn
        - signerCount
      properties:
        envelopeId:
          type: string
          description: Identificador único do envelope.
          example: 'env_01JXYZ...'
        downloadUrl:
          type: string
          format: uri
          description: URL pré-assinada para download do documento com carimbo combinado.
          example: 'https://s3.amazonaws.com/signdocs-evidence/env_01JXYZ.pdf?...'
        expiresIn:
          type: integer
          description: Tempo de validade da URL em segundos.
          example: 3600
        signerCount:
          type: integer
          description: Número de assinaturas incluídas no carimbo combinado.
          example: 3

    HealthResponse:
      type: object
      description: Resposta de verificação de saúde da API.
      required:
        - status
        - timestamp
        - service
        - components
        - latencyMs
      properties:
        status:
          type: string
          description: Status geral da API.
          enum:
            - healthy
            - unhealthy
        timestamp:
          type: string
          format: date-time
        service:
          type: string
          description: Nome do serviço.
          example: signdocs-external-api
        components:
          type: object
          description: Status individual de cada componente.
          properties:
            dynamodb:
              $ref: '#/components/schemas/ComponentHealth'
            s3:
              $ref: '#/components/schemas/ComponentHealth'
            kms:
              $ref: '#/components/schemas/ComponentHealth'
            mtlsTruststore:
              $ref: '#/components/schemas/ComponentHealth'
        latencyMs:
          type: number
          description: Latência total da verificação em milissegundos.

    ComponentHealth:
      type: object
      description: Status de saúde de um componente individual.
      required:
        - status
        - latencyMs
      properties:
        status:
          type: string
          description: Status operacional do componente.
          enum:
            - operational
            - down
        latencyMs:
          type: number
          description: Latência da verificação do componente em milissegundos.

    HealthHistoryResponse:
      type: object
      description: Resposta com histórico de saúde da API.
      required:
        - period
        - from
        - to
        - totalChecks
        - uptime
        - incidents
      properties:
        period:
          type: string
          description: Período consultado.
          enum:
            - 24h
            - 7d
            - 30d
        from:
          type: string
          format: date-time
          description: Início do período.
        to:
          type: string
          format: date-time
          description: Fim do período.
        totalChecks:
          type: integer
          description: Total de verificações no período.
        uptime:
          type: object
          description: Percentual de disponibilidade por componente.
          properties:
            overall:
              type: number
              description: Uptime geral (0-100).
              example: 99.95
            dynamodb:
              type: number
            s3:
              type: number
            kms:
              type: number
            mtlsTruststore:
              type: number
        incidents:
          type: array
          description: Incidentes no período.
          items:
            type: object
            required:
              - startedAt
              - affectedComponents
            properties:
              startedAt:
                type: string
                format: date-time
              endedAt:
                type: string
                format: date-time
              affectedComponents:
                type: array
                items:
                  type: string
              durationMinutes:
                type: number
        latestCheck:
          type: object
          description: Última verificação realizada.
          properties:
            overallStatus:
              type: string
            components:
              type: object
              properties:
                dynamodb:
                  $ref: '#/components/schemas/ComponentHealth'
                s3:
                  $ref: '#/components/schemas/ComponentHealth'
                kms:
                  $ref: '#/components/schemas/ComponentHealth'
                mtlsTruststore:
                  $ref: '#/components/schemas/ComponentHealth'
            checkedAt:
              type: string
              format: date-time
            totalLatencyMs:
              type: number
