openapi: 3.1.0
info:
  title: Kodano API
  version: 1.0.0
  description: |-
    API Pública Kodano v1 para integração máquina a máquina: pagamentos (PIX, boleto e cartão), clientes, cofre de cartões, links de pagamento, saldo, extrato, liquidações, saques, destinos de saque, contratos e caução, câmbio, webhooks e eventos.

    **Contrato provisório**, gerado a partir da especificação técnica da v1 até a publicação do OpenAPI oficial. Em caso de divergência, vale o contrato oficial quando publicado.

    **Acesso por convite/piloto até o GA**: credenciais, produtos e escopos são concedidos pela Kodano.

    Convenções: JSON em camelCase, enums em UPPER_SNAKE, dinheiro em centavos (inteiro) + `currency` (`BRL`), datas ISO-8601 UTC, IDs prefixados e opacos, listas `{ data, hasMore, nextCursor }` com `?limit` (1–100, padrão 20) e `?cursor` opaco (ordem `createdAt` desc, sem `totalCount`). Erros em `application/problem+json`. `Idempotency-Key` (UUID, retida por 24 h) é obrigatória nos POST que criam recurso ou movem dinheiro; o replay devolve a resposta original com `X-Idempotent-Replay: true`. Rate limit por credencial: 429 `RATE_LIMIT.EXCEEDED` com `Retry-After`; nunca há `X-RateLimit-*`. Sandbox: 20 req/s (burst 50) e 10 000 req/dia; produção `standard` 50 req/s (burst 100), `enterprise` 200 req/s (burst 400), valores sujeitos a aprovação de Produto. Rotas fora do mapa (inclusive `admin/*`, `portal/*`, `internal/*`, `dev/*`, sem `/v1` e `OPTIONS`) respondem 404 `ROUTE.NOT_FOUND`.
servers:
- url: https://api.sandbox.kodano.com.br
  description: Sandbox (isolado de produção de ponta a ponta)
- url: https://api.kodano.com.br
  description: Produção
security:
- kodanoOAuth: []
tags:
- name: Pagamentos
  description: Cobranças PIX, boleto e cartão; captura, estorno, cancelamento e leituras.
- name: Clientes
  description: Clientes do merchant.
- name: Cartões
  description: Cofre de cartões (tokenização pelo SDK de tokenização; PAN e CVV nunca trafegam).
- name: Links de pagamento
  description: Checkout hospedado.
- name: Saldo e extrato
  description: Saldo da carteira `default` e lançamentos.
- name: Liquidações
  description: Agenda e lotes de liquidação.
- name: Saques
  description: Saques para destinos verificados.
- name: Destinos de saque
  description: Contas bancárias (`bank-accounts`) e chaves PIX (`pix-keys`) de saque; nascem `PENDING_VERIFICATION` e só um humano do merchant as verifica no portal.
- name: Webhook endpoints
  description: Endpoints que recebem eventos assinados.
- name: Eventos
  description: Eventos publicados (retenção de 30 dias) e reentrega.
- name: Webhooks
  description: Requisições que a Kodano envia ao parceiro.
- name: Contratos
  description: Emissão, assinatura e acompanhamento de contratos.
  x-displayName: Contratos
- name: Modelos de contrato
  description: Modelos .docx com tags preenchidas na emissão.
  x-displayName: Modelos de contrato
- name: Arquivos de contrato
  description: PDFs próprios do merchant para emitir contrato.
  x-displayName: Arquivos de contrato
- name: Caução
  description: Garantia no cartão do pagador, liberada ou capturada no fim.
  x-displayName: Caução
- name: Câmbio
  description: 'Câmbio à vista de real para dólar: simulação, cotação, operação, acompanhamento e comprovante.'
  x-displayName: Câmbio
- name: Contas de câmbio
  description: Contas no exterior que recebem o dólar (ACH, WIRE, SWIFT e SEPA).
  x-displayName: Contas de câmbio
x-tagGroups:
- name: Pagamentos
  tags:
  - Pagamentos
  - Clientes
  - Cartões
  - Links de pagamento
- name: Recebíveis
  tags:
  - Saldo e extrato
  - Liquidações
- name: Repasses
  tags:
  - Saques
  - Destinos de saque
- name: Plataforma
  tags:
  - Webhook endpoints
  - Eventos
  - Webhooks
- name: Contratos e caução
  tags:
  - Contratos
  - Modelos de contrato
  - Arquivos de contrato
  - Caução
- name: Câmbio
  tags:
  - Câmbio
  - Contas de câmbio
paths:
  /v1/payments/pix:
    post:
      operationId: createPixPayment
      summary: Criar cobrança PIX
      description: |-
        Cria uma cobrança PIX com QR Code dinâmico (EMV). Recusa do provedor não é erro HTTP: sai 201 `FAILED` com `failure.code` `PAYMENT.PROVIDER_REJECTED`; erro técnico sai 201 `FAILED` com `PAYMENT.PROVIDER_ERROR` e pode virar `SUCCEEDED` na reconciliação. Timeout do provedor sai 201 `PENDING` com `statusReason: PROVIDER_PENDING` e `pix: null`; o EMV aparece no `GET /v1/payments/{id}` quando recuperado. Sem pagamento até `expiresAt`, vai a `CANCELED` + `statusReason: EXPIRED`. Pagamento recebido → `SUCCEEDED` e evento `payment.succeeded`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePixPaymentRequest'
            example:
              amount: 15990
              currency: BRL
              description: Pedido 123
              customer:
                name: Ana Souza
                document: '12345678909'
                email: ana@exemplo.com.br
                phone:
                  areaCode: '11'
                  number: '999990000'
              expiresIn: 3600
              metadata:
                orderRef: '123'
      responses:
        '201':
          description: Cobrança criada (ou replay da resposta original).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
              example:
                id: pay_OhbVrpoiVgRV5IfLBcbfnoGMbJmT
                status: PENDING
                statusReason: null
                method: PIX
                amount: 15990
                currency: BRL
                description: Pedido 123
                customer:
                  id: cus_iGFfWd3hjOkYRBMe
                  name: Ana Souza
                  document: 123******09
                pix:
                  qrCode: 00020101021226870014br.gov.bcb.pix2565pix.exemplo.com.br/qr/v3/at/3f1c9e2a-8b7d-4e51-9a0c-6d2f1e7b4c93520400005303986540515990.005802BR5913LOJA EXEMPLO6009SAO PAULO62070503***6304B14A
                  copyPaste: 00020101021226870014br.gov.bcb.pix2565pix.exemplo.com.br/qr/v3/at/3f1c9e2a-8b7d-4e51-9a0c-6d2f1e7b4c93520400005303986540515990.005802BR5913LOJA EXEMPLO6009SAO PAULO62070503***6304B14A
                  expiresAt: '2026-10-16T14:00:00Z'
                  endToEndId: null
                boleto: null
                card: null
                nextAction: null
                failure: null
                refundedAmount: 0
                refundedAt: null
                paymentLinkId: null
                metadata:
                  orderRef: '123'
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T13:00:01Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem `payments:pix:write` (antes da validação do corpo e da checagem de merchant)
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `errors[].code` `VALIDATION.MIN`, `MAX`, `REQUIRED`, `FORMAT`, `ONE_OF`, `CURRENCY_UNSUPPORTED`
            - `MERCHANT.NOT_CONFIGURED`: merchant sem conector PIX, endereço ou MCC (extensão `missing`); nada é persistido
            - `CUSTOMER.NOT_FOUND`: `customer.id` inexistente, malformado ou de outro merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                MERCHANT.NOT_CONFIGURED:
                  $ref: '#/components/examples/MERCHANT.NOT_CONFIGURED'
                CUSTOMER.NOT_FOUND:
                  $ref: '#/components/examples/CUSTOMER.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: provedor indisponível; `Retry-After: 30`; a chave fica `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments/boleto:
    post:
      operationId: createBoletoPayment
      summary: Emitir boleto
      description: |-
        Emite um boleto com multa, juros e desconto opcionais. Recusa (ex.: CBE053) sai 201 `FAILED` + `PAYMENT.PROVIDER_REJECTED`; 5xx sai 201 `FAILED` + `PAYMENT.PROVIDER_ERROR`. `charge-paid` preenche `boleto.paidAt` e mantém `PENDING`; a liquidação leva a `SUCCEEDED`; vencido/cancelado → `CANCELED` + `statusReason: EXPIRED`. Timeout → 201 `PENDING` + `PROVIDER_PENDING` com `boleto: null`. Boleto não tem estorno nem cancelamento na v1.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:boleto:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBoletoPaymentRequest'
            example:
              amount: 25000
              currency: BRL
              description: Fatura outubro
              dueDate: '2026-10-30'
              customer:
                name: Loja XYZ Ltda
                document: '12345678000195'
                email: financeiro@xyz.com.br
                address:
                  postalCode: '01310100'
                  publicArea: Av. Paulista
                  number: '1000'
                  complement: Conj. 101
                  neighborhood: Bela Vista
                  city: São Paulo
                  state: SP
              fine:
                percent: 2
              interest:
                monthlyPercent: 1
              discount:
                amount: 500
                limitDate: '2026-10-25'
              metadata:
                invoice: FAT-2026-10
      responses:
        '201':
          description: Boleto emitido (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
              example:
                id: pay_PSIAoCLrZ3aWZkSBvrjn9Wvgfygw
                status: PENDING
                statusReason: null
                method: BOLETO
                amount: 25000
                currency: BRL
                description: Fatura outubro
                customer:
                  id: cus_yyMDHqJ38aRUhR4I
                  name: Loja XYZ Ltda
                  document: 12*********95
                pix: null
                boleto:
                  barcode: '34191915700000250001790001043510047910201500'
                  digitableLine: 34191.79001 01043.510047 91020.150008 1 91570000025000
                  pdfUrl: null
                  dueDate: '2026-10-30'
                  paidAt: null
                card: null
                nextAction: null
                failure: null
                refundedAmount: 0
                refundedAt: null
                paymentLinkId: null
                metadata:
                  invoice: FAT-2026-10
                createdAt: '2026-10-16T13:05:00Z'
                updatedAt: '2026-10-16T13:05:02Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem `payments:boleto:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `dueDate` fora de D+1..D+180, percentual fora da faixa, `discount.amount` ≥ `amount`, `limitDate` fora de hoje..`dueDate`, documento inválido
            - `MERCHANT.NOT_CONFIGURED`: merchant sem conector do provedor, chave PIX ou documento do recebedor (`missing`)
            - `CUSTOMER.NOT_FOUND`: `cus_` inexistente ou de outro merchant
            - `CUSTOMER.ADDRESS_REQUIRED`: pessoa por `id` sem endereço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                MERCHANT.NOT_CONFIGURED:
                  $ref: '#/components/examples/MERCHANT.NOT_CONFIGURED'
                CUSTOMER.NOT_FOUND:
                  $ref: '#/components/examples/CUSTOMER.NOT_FOUND'
                CUSTOMER.ADDRESS_REQUIRED:
                  $ref: '#/components/examples/CUSTOMER.ADDRESS_REQUIRED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: provedor indisponível; `Retry-After: 30`; a chave fica `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments/cards:
    post:
      operationId: createCardPayment
      summary: Cobrar cartão
      description: |-
        Cobra um cartão do cofre (`card.id`) ou um token do SDK de tokenização (`card.token`, só CIT). PAN e CVV nunca trafegam. Desfechos em 201: `capture=false` → `AUTHORIZED`; `capture=true` → `SUCCEEDED` (autorização + captura imediata); 3DS → `PENDING` + `nextAction`; negado/4xx/401/403 → `FAILED` + `failure.code`; 5xx/timeout → `PENDING` com fallback. Captura imediata recusada → 201 `CANCELED` + `statusReason: CAPTURE_FAILED`. `card.store=true` registra no cofre e devolve `card.id`; exige também `payments:vault:write`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:cards:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCardPaymentRequest'
            examples:
              cartaoDoCofre:
                summary: Cartão do cofre
                value:
                  amount: 25000
                  currency: BRL
                  installments: 3
                  capture: false
                  initiator: CUSTOMER
                  card:
                    id: card_WrXPvhsBkDa9U4UqGWlG6g3Ot1O
                  browser:
                    ip: 203.0.113.10
                    userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
                  threeDs:
                    returnUrl: https://parceiro.com.br/3ds/retorno
                  description: Reserva 4471
                  metadata:
                    booking: '4471'
              tokenDoSdk:
                summary: Token do SDK checkout-v2
                value:
                  amount: 9990
                  currency: BRL
                  installments: 1
                  capture: true
                  initiator: CUSTOMER
                  card:
                    token: <cryptData do checkout-v2>
                    holderName: ANA SOUZA
                    expiry: 12/28
                    lastFour: '4242'
                    store: true
                  customer:
                    id: cus_iGFfWd3hjOkYRBMe
                  browser:
                    ip: 203.0.113.10
                    userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
      responses:
        '201':
          description: Pagamento criado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
              example:
                id: pay_2wMqZcUDIh7yfJs1ON43xKmTecQo
                status: AUTHORIZED
                statusReason: null
                method: CARD
                amount: 25000
                currency: BRL
                description: Reserva 4471
                customer:
                  id: cus_iGFfWd3hjOkYRBMe
                  name: Ana Souza
                  document: 123******09
                pix: null
                boleto: null
                card:
                  id: card_WrXPvhsBkDa9U4UqGWlG6g3Ot1O
                  brand: VISA
                  last4: '4242'
                  installments: 3
                  capturedAmount: null
                  capturedAt: null
                nextAction: null
                failure: null
                refundedAmount: 0
                refundedAt: null
                paymentLinkId: null
                metadata:
                  booking: '4471'
                createdAt: '2026-10-16T13:10:00Z'
                updatedAt: '2026-10-16T13:10:02Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem `payments:cards:write`; `card.store` sem `payments:vault:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: `card.id` inexistente ou de outro merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `card.id` e `token` juntos ou nenhum (`VALIDATION.ONE_OF`), `MERCHANT` com token, `installments` fora de 1–12
            - `CARD.PAN_NOT_ALLOWED`: cartão em claro no corpo (após escopo e idempotência)
            - `CARD.TOKEN_FIELDS_REQUIRED`: token sem `holderName`/`expiry`/`lastFour`
            - `CARD.PAYER_IP_REQUIRED`: CIT sem `browser.ip`
            - `CARD.TOKEN_INVALID`: token expirado, reusado ou ≤ 255 caracteres
            - `CARD.INACTIVE`: cartão do cofre inativo
            - `CARD.CUSTOMER_MISMATCH`: cartão de outro cliente
            - `CARD.INSTALLMENTS_NOT_AVAILABLE`: sem taxa bandeira × parcelas
            - `CARD.PREAUTH_NOT_ENABLED`: `capture=false` sem pré-autorização habilitada no merchant
            - `CUSTOMER.NOT_FOUND`: `cus_` inválido ou de outro merchant
            - `CUSTOMER.PHONE_REQUIRED`: antifraude ligado e cliente sem telefone
            - `MERCHANT.NOT_CONFIGURED`: merchant sem conector de cartão
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CARD.PAN_NOT_ALLOWED:
                  $ref: '#/components/examples/CARD.PAN_NOT_ALLOWED'
                CARD.TOKEN_FIELDS_REQUIRED:
                  $ref: '#/components/examples/CARD.TOKEN_FIELDS_REQUIRED'
                CARD.PAYER_IP_REQUIRED:
                  $ref: '#/components/examples/CARD.PAYER_IP_REQUIRED'
                CARD.TOKEN_INVALID:
                  $ref: '#/components/examples/CARD.TOKEN_INVALID'
                CARD.INACTIVE:
                  $ref: '#/components/examples/CARD.INACTIVE'
                CARD.CUSTOMER_MISMATCH:
                  $ref: '#/components/examples/CARD.CUSTOMER_MISMATCH'
                CARD.INSTALLMENTS_NOT_AVAILABLE:
                  $ref: '#/components/examples/CARD.INSTALLMENTS_NOT_AVAILABLE'
                CARD.PREAUTH_NOT_ENABLED:
                  $ref: '#/components/examples/CARD.PREAUTH_NOT_ENABLED'
                CUSTOMER.NOT_FOUND:
                  $ref: '#/components/examples/CUSTOMER.NOT_FOUND'
                CUSTOMER.PHONE_REQUIRED:
                  $ref: '#/components/examples/CUSTOMER.PHONE_REQUIRED'
                MERCHANT.NOT_CONFIGURED:
                  $ref: '#/components/examples/MERCHANT.NOT_CONFIGURED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: 5xx no registro do token no cofre; `Retry-After: 30`; chave `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments/{id}/captures:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    post:
      operationId: capturePayment
      summary: Capturar pré-autorização
      description: |-
        Captura um pagamento de cartão `AUTHORIZED`, total (sem `amount`) ou parcial. Recusa do adquirente → 201 com a captura `FAILED`; o pagamento continua `AUTHORIZED`. Captura parcial: o serviço captura o total e estorna a diferença como `CAPTURE_ADJUSTMENT` (`adjustment`), que nasce `PENDING`, não aparece em `/refunds`, não soma em `refundedAmount` e não gera `payment.refunded`; o pagamento fica `CAPTURED`. Captura, estorno e cancelamento são serializados por pagamento.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:cards:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CaptureRequest'
            example:
              amount: 20000
      responses:
        '201':
          description: Captura registrada (ou replay). `Location` aponta para o pagamento.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Capture'
              example:
                id: cap_K4TunPFz46PDjqipVJIqVLB5Lzxo
                paymentId: pay_2wMqZcUDIh7yfJs1ON43xKmTecQo
                status: SUCCEEDED
                amount: 20000
                currency: BRL
                authorizedAmount: 25760
                adjustment:
                  amount: 5760
                  status: PENDING
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem `payments:cards:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: `pay_` inexistente, malformado ou de outro merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `PAYMENT.OPERATION_IN_PROGRESS`: outra captura, estorno ou cancelamento no mesmo `pay_` (`Retry-After: 2`)
            - `CAPTURE.NOT_AUTHORIZED`: sem autorização aberta (já capturada, cancelada ou não aprovada)
            - `CAPTURE.AUTHORIZATION_EXPIRED`: autorização vencida
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                PAYMENT.OPERATION_IN_PROGRESS:
                  $ref: '#/components/examples/PAYMENT.OPERATION_IN_PROGRESS'
                CAPTURE.NOT_AUTHORIZED:
                  $ref: '#/components/examples/CAPTURE.NOT_AUTHORIZED'
                CAPTURE.AUTHORIZATION_EXPIRED:
                  $ref: '#/components/examples/CAPTURE.AUTHORIZATION_EXPIRED'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `amount` < 1 ou maior que o autorizado
            - `CAPTURE.METHOD_NOT_SUPPORTED`: pagamento PIX ou boleto
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CAPTURE.METHOD_NOT_SUPPORTED:
                  $ref: '#/components/examples/CAPTURE.METHOD_NOT_SUPPORTED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: provedor indisponível; chave `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments/{id}/refunds:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    post:
      operationId: createRefund
      summary: Estornar pagamento
      description: |-
        Estorna, total ou parcialmente, um pagamento PIX ou cartão. Parciais sequenciais somam no máximo o valor estornável (PIX: pago; cartão: capturado). O 201 sai `PENDING` (PIX até o webhook do provedor; cartão até o próximo relatório de liquidação) ou `SUCCEEDED`; o desfecho chega por `payment.refunded` ou `GET /v1/payments/{id}/refunds`. A borda aceita qualquer `payments:{pix,boleto,cards}:write`; o serviço exige o `write` do método do pagamento. Sandbox (simulador): ≤ 300000 → `SUCCEEDED`; 300001–400000 → 422 `REFUND.PROVIDER_REJECTED`; 400001–600000 → `PENDING` sem desfecho; acima → `SUCCEEDED`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:write
      - kodanoOAuth:
        - payments:boleto:write
      - kodanoOAuth:
        - payments:cards:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundRequest'
            example:
              amount: 5000
              reason: REQUESTED_BY_CUSTOMER
              metadata:
                ticket: SUP-123
      responses:
        '201':
          description: 'Estorno criado (ou replay). `Location: /v1/payments/{id}/refunds`.'
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Refund'
              example:
                id: ref_Xsf2o3gyrDO1xkxwnQrS7RPeMOkI
                paymentId: pay_2wMqZcUDIh7yfJs1ON43xKmTecQo
                amount: 5000
                currency: BRL
                status: PENDING
                reason: REQUESTED_BY_CUSTOMER
                metadata:
                  ticket: SUP-123
                failure: null
                createdAt: '2026-10-16T16:20:00Z'
                updatedAt: '2026-10-16T16:20:01Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem o `write` do método do pagamento, tendo o `read` dele
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant, ou método sem `write` e sem `read`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `REFUND.IN_PROGRESS`: já há estorno em voo no pagamento
            - `PAYMENT.OPERATION_IN_PROGRESS`: captura, estorno ou cancelamento concorrente (`Retry-After: 2`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                REFUND.IN_PROGRESS:
                  $ref: '#/components/examples/REFUND.IN_PROGRESS'
                PAYMENT.OPERATION_IN_PROGRESS:
                  $ref: '#/components/examples/PAYMENT.OPERATION_IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: corpo inválido
            - `REFUND.NOT_SUPPORTED`: boleto
            - `REFUND.PAYMENT_NOT_REFUNDABLE`: `PENDING`, `FAILED` ou `CANCELED`
            - `REFUND.PAYMENT_NOT_CAPTURED`: `AUTHORIZED` (usar `cancel`)
            - `REFUND.PAYMENT_FULLY_REFUNDED`: `REFUNDED`
            - `REFUND.AMOUNT_EXCEEDS_REFUNDABLE`: `amount` maior que o saldo estornável (extensão `availableRefundAmount`)
            - `REFUND.WINDOW_EXPIRED`: PIX além de 90 dias do processamento
            - `REFUND.CHARGEBACK_OPEN`: status `CHARGEBACK`
            - `REFUND.PAYMENT_MANAGED_INTERNALLY`: pagamento de caução
            - `REFUND.PROVIDER_REJECTED`: recusa síncrona do provedor (extensão `refundId`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                REFUND.NOT_SUPPORTED:
                  $ref: '#/components/examples/REFUND.NOT_SUPPORTED'
                REFUND.PAYMENT_NOT_REFUNDABLE:
                  $ref: '#/components/examples/REFUND.PAYMENT_NOT_REFUNDABLE'
                REFUND.PAYMENT_NOT_CAPTURED:
                  $ref: '#/components/examples/REFUND.PAYMENT_NOT_CAPTURED'
                REFUND.PAYMENT_FULLY_REFUNDED:
                  $ref: '#/components/examples/REFUND.PAYMENT_FULLY_REFUNDED'
                REFUND.AMOUNT_EXCEEDS_REFUNDABLE:
                  $ref: '#/components/examples/REFUND.AMOUNT_EXCEEDS_REFUNDABLE'
                REFUND.WINDOW_EXPIRED:
                  $ref: '#/components/examples/REFUND.WINDOW_EXPIRED'
                REFUND.CHARGEBACK_OPEN:
                  $ref: '#/components/examples/REFUND.CHARGEBACK_OPEN'
                REFUND.PAYMENT_MANAGED_INTERNALLY:
                  $ref: '#/components/examples/REFUND.PAYMENT_MANAGED_INTERNALLY'
                REFUND.PROVIDER_REJECTED:
                  $ref: '#/components/examples/REFUND.PROVIDER_REJECTED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '502':
          description: |-
            Erro do provedor.

            - `REFUND.PROVIDER_ERROR`: `ERROR` local; nova tentativa só com chave nova
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REFUND.PROVIDER_ERROR:
                  $ref: '#/components/examples/REFUND.PROVIDER_ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: provedor indisponível; `Retry-After: 30`; chave `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listPaymentRefunds
      summary: Listar estornos do pagamento
      description: |-
        Estornos do pagamento, inclusive os feitos pelo portal ou pela caução (`reason: null`, `metadata: {}`). Ajustes de captura parcial não aparecem.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Estornos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RefundList'
              example:
                data:
                - id: ref_Xsf2o3gyrDO1xkxwnQrS7RPeMOkI
                  paymentId: pay_2wMqZcUDIh7yfJs1ON43xKmTecQo
                  amount: 5000
                  currency: BRL
                  status: SUCCEEDED
                  reason: REQUESTED_BY_CUSTOMER
                  metadata:
                    ticket: SUP-123
                  failure: null
                  createdAt: '2026-10-16T16:20:00Z'
                  updatedAt: '2026-10-17T09:10:00Z'
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant ou de método sem `read` (corpo idêntico)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payments/{id}/cancel:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    post:
      operationId: cancelPayment
      summary: Cancelar autorização de cartão
      description: |-
        Cancela cartão `AUTHORIZED` (reversão, libera o limite) ou cartão `PENDING` que o provedor confirma não enviado à operadora (ex.: 3DS não concluído). Já `CANCELED` → 200 com o mesmo objeto, sem chamar o provedor. Capturado → 422 `PAYMENT.NOT_CANCELABLE` com `reason: CAPTURED` (usar estorno). Sem evento v1 de cancelamento. Corpo vazio ou `{}`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:write
      - kodanoOAuth:
        - payments:boleto:write
      - kodanoOAuth:
        - payments:cards:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CancelPaymentRequest'
            example: {}
      responses:
        '200':
          description: Pagamento cancelado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
              example:
                id: pay_4K8ffUF0eWIXiiQE8JkqH3MB9n7I
                status: CANCELED
                statusReason: null
                method: CARD
                amount: 25000
                currency: BRL
                description: Reserva 4480
                customer:
                  id: cus_iGFfWd3hjOkYRBMe
                  name: Ana Souza
                  document: 123******09
                pix: null
                boleto: null
                card:
                  id: card_WrXPvhsBkDa9U4UqGWlG6g3Ot1O
                  brand: VISA
                  last4: '4242'
                  installments: 1
                  capturedAmount: null
                  capturedAt: null
                nextAction: null
                failure: null
                refundedAmount: 0
                refundedAt: null
                paymentLinkId: null
                metadata: {}
                createdAt: '2026-10-16T10:00:00Z'
                updatedAt: '2026-10-16T10:05:12Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: sem o `write` do método, tendo o `read` dele
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant, ou método sem `write` e sem `read`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `PAYMENT.OPERATION_IN_PROGRESS`: operação concorrente no mesmo `pay_` (`Retry-After: 2`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                PAYMENT.OPERATION_IN_PROGRESS:
                  $ref: '#/components/examples/PAYMENT.OPERATION_IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: corpo com campos
            - `PAYMENT.NOT_CANCELABLE`: extensão `reason`: `METHOD_NOT_SUPPORTED` (PIX, boleto), `CAPTURED`, `FINAL_STATUS` (`FAILED`), `MANAGED_INTERNALLY` (caução), `PROVIDER_REJECTED`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                PAYMENT.NOT_CANCELABLE:
                  $ref: '#/components/examples/PAYMENT.NOT_CANCELABLE'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: leitura do provedor indeterminada ou reversão com desfecho desconhecido; `Retry-After: 30`; chave `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments:
    get:
      operationId: listPayments
      summary: Listar pagamentos
      description: |-
        Lista os pagamentos do merchant (inclusive os criados pelo portal e por link), recortados pelos métodos com `read` concedido. Ordem `createdAt` desc, desempate por id; sem `totalCount`. `?status` aplica exatamente o mapeamento de status públicos.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Um dos nove status públicos.
        schema:
          $ref: '#/components/schemas/PaymentStatus'
      - name: method
        in: query
        required: false
        description: Fora dos métodos concedidos → 403.
        schema:
          $ref: '#/components/schemas/PaymentMethod'
      - name: createdFrom
        in: query
        required: false
        description: Início inclusivo `[from, to)`.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: Fim exclusivo; `from > to` → `VALIDATION.RANGE`.
        schema:
          type: string
          format: date-time
      - name: customerId
        in: query
        required: false
        description: Malformado ou de outro merchant → lista vazia.
        schema:
          type: string
          pattern: ^cus_
      - name: paymentLinkId
        in: query
        required: false
        description: Pagamentos de um link.
        schema:
          type: string
          pattern: ^ord_
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de pagamentos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentList'
              example:
                data:
                - id: pay_OhbVrpoiVgRV5IfLBcbfnoGMbJmT
                  status: SUCCEEDED
                  statusReason: null
                  method: PIX
                  amount: 15990
                  currency: BRL
                  description: Pedido 123
                  customer:
                    id: cus_iGFfWd3hjOkYRBMe
                    name: Ana Souza
                    document: 123******09
                  pix:
                    qrCode: 00020101021226870014br.gov.bcb.pix2565pix.exemplo.com.br/qr/v3/at/3f1c9e2a-8b7d-4e51-9a0c-6d2f1e7b4c93520400005303986540515990.005802BR5913LOJA EXEMPLO6009SAO PAULO62070503***6304B14A
                    copyPaste: 00020101021226870014br.gov.bcb.pix2565pix.exemplo.com.br/qr/v3/at/3f1c9e2a-8b7d-4e51-9a0c-6d2f1e7b4c93520400005303986540515990.005802BR5913LOJA EXEMPLO6009SAO PAULO62070503***6304B14A
                    expiresAt: '2026-10-16T14:00:00Z'
                    endToEndId: E13935893202610161302a1B2c3D4e5F
                  boleto: null
                  card: null
                  nextAction: null
                  failure: null
                  refundedAmount: 0
                  refundedAt: null
                  paymentLinkId: null
                  metadata:
                    orderRef: '123'
                  createdAt: '2026-10-16T13:00:00Z'
                  updatedAt: '2026-10-16T13:02:11Z'
                hasMore: true
                nextCursor: eyJjIjoiMjAyNi0xMC0xNlQxMzowMDowMFoiLCJpIjo5ODIxLCJkIjoiZGVzYyIsImYiOiI0YzFhOWUwMiJ9.k3JxQ0
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read` (ex.: só `payments:links:read`); `?method` fora dos concedidos
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `errors[].code` `VALIDATION.ENUM`, `RANGE`, `FORMAT` (status ou método desconhecido, `from > to`, `limit` fora de 1–100)
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payments/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    get:
      operationId: getPayment
      summary: Consultar pagamento
      description: |-
        Devolve o pagamento (PIX, boleto ou cartão). `failure` só em `FAILED`; o instrumento recuperado pelo fallback aparece aqui. Leitura sem cache.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Pagamento.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
              example:
                id: pay_2wMqZcUDIh7yfJs1ON43xKmTecQo
                status: PARTIALLY_REFUNDED
                statusReason: null
                method: CARD
                amount: 25000
                currency: BRL
                description: Reserva 4471
                customer:
                  id: cus_iGFfWd3hjOkYRBMe
                  name: Ana Souza
                  document: 123******09
                pix: null
                boleto: null
                card:
                  id: card_WrXPvhsBkDa9U4UqGWlG6g3Ot1O
                  brand: VISA
                  last4: '4242'
                  installments: 3
                  capturedAmount: 20000
                  capturedAt: '2026-10-16T15:00:00Z'
                nextAction: null
                failure: null
                refundedAmount: 5000
                refundedAt: '2026-10-17T09:10:00Z'
                paymentLinkId: null
                metadata:
                  booking: '4471'
                createdAt: '2026-10-16T13:10:00Z'
                updatedAt: '2026-10-17T09:10:00Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant ou de método sem `read` (corpo idêntico)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payments/{id}/timeline:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    get:
      operationId: getPaymentTimeline
      summary: Linha do tempo do pagamento
      description: |-
        Eventos do ciclo de vida do pagamento em ordem cronológica, sempre numa página (`hasMore: false`). `payment.created` sempre; `payment.captured` por captura bem-sucedida; `payment.refunded` por estorno bem-sucedido (o ajuste de captura parcial não entra).

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Linha do tempo.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimelineList'
              example:
                data:
                - type: payment.created
                  occurredAt: '2026-10-16T13:10:00Z'
                - type: payment.authorized
                  occurredAt: '2026-10-16T13:10:02Z'
                - type: payment.captured
                  occurredAt: '2026-10-16T15:00:00Z'
                  amount: 20000
                  resourceId: cap_K4TunPFz46PDjqipVJIqVLB5Lzxo
                - type: payment.refunded
                  occurredAt: '2026-10-17T09:10:00Z'
                  amount: 5000
                  resourceId: ref_Xsf2o3gyrDO1xkxwnQrS7RPeMOkI
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant ou de método sem `read` (corpo idêntico)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payments/{id}/fees:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    get:
      operationId: getPaymentFees
      summary: Tarifas do pagamento
      description: |-
        Bruto, líquido e tarifas do pagamento. Sem lançamento financeiro → `status: PENDING` com `fees: []`; boleto pago fica `PENDING`. Sem MDR na v1.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Tarifas.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentFees'
              example:
                paymentId: pay_Lq7Tz2WcR9hN4bVx1sKe6pYd3MfA
                currency: BRL
                grossAmount: 25000
                netAmount: 24375
                status: ESTIMATED
                fees:
                - type: KODANO_FEE
                  amount: 625
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant ou de método sem `read` (corpo idêntico)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `FEES.NOT_AVAILABLE`: status fora de `SUCCEEDED`, `CAPTURED`, `PARTIALLY_REFUNDED`, `REFUNDED`, `CHARGEBACK`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                FEES.NOT_AVAILABLE:
                  $ref: '#/components/examples/FEES.NOT_AVAILABLE'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: serviço financeiro indisponível; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/payments/{id}/receipt:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do pagamento (`pay_…`). Malformado → 404.
      schema:
        type: string
    get:
      operationId: getPaymentReceipt
      summary: Comprovante do pagamento
      description: |-
        Emite o comprovante de PIX, boleto ou cartão, com pagador mascarado. PDF renderizado a cada chamada, sem cache; URL pré-assinada de 15 min. Throttle próprio: 5 req/s (burst 10).

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Pagamentos
      security:
      - kodanoOAuth:
        - payments:pix:read
      - kodanoOAuth:
        - payments:boleto:read
      - kodanoOAuth:
        - payments:cards:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: 'Comprovante. Com `Accept: application/json` (ou ausente) vem o JSON com URL pré-assinada do PDF (`pdf: null` onde não há bucket, como no sandbox); com `Accept: application/pdf` vem o PDF direto com `Content-Disposition: attachment`.'
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Receipt'
              example:
                paymentId: pay_Lq7Tz2WcR9hN4bVx1sKe6pYd3MfA
                status: PARTIALLY_REFUNDED
                paidAt: '2026-10-15T18:00:01Z'
                amount: 25000
                currency: BRL
                refundedAmount: 5000
                method: CARD
                card:
                  brand: VISA
                  last4: '4242'
                payer:
                  name: Loja XYZ Ltda
                  document: '**.***.678/0001-**'
                pdf:
                  url: https://files.kodano.com.br/receipts/pay_Lq7Tz2WcR9hN4bVx1sKe6pYd3MfA.pdf?expires=900&signature=...
                  expiresAt: '2026-10-16T13:15:00Z'
            application/pdf:
              schema:
                type: string
                contentMediaType: application/pdf
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: nenhum `payments:{pix,boleto,cards}:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PAYMENT.NOT_FOUND`: inexistente, malformado, de outro merchant ou de método sem `read` (corpo idêntico)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PAYMENT.NOT_FOUND:
                  $ref: '#/components/examples/PAYMENT.NOT_FOUND'
        '406':
          description: |-
            Formato não aceito.

            - `ACCEPT.NOT_SUPPORTED`: `Accept` diferente de JSON e PDF
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ACCEPT.NOT_SUPPORTED:
                  $ref: '#/components/examples/ACCEPT.NOT_SUPPORTED'
        '422':
          description: |-
            Não processável.

            - `RECEIPT.NOT_AVAILABLE`: status fora de `SUCCEEDED`, `CAPTURED`, `PARTIALLY_REFUNDED`, `REFUNDED`, `CHARGEBACK`; pagamento de caução
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RECEIPT.NOT_AVAILABLE:
                  $ref: '#/components/examples/RECEIPT.NOT_AVAILABLE'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/customers:
    post:
      operationId: createCustomer
      summary: Cadastrar cliente
      description: |-
        Cadastra um cliente do merchant. `type` é derivado do documento. Pessoa que já existe só como pagadora é promovida a cliente com os dados do corpo. Documento que já é cliente → 409 com `existingCustomerId` (nunca upsert silencioso); é a única forma de achar cliente por documento.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Clientes
      security:
      - kodanoOAuth:
        - payments:customers:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomerRequest'
            example:
              name: Ana Souza
              document: '12345678909'
              email: ana.souza@example.com
              phone:
                areaCode: '11'
                number: '999990000'
              address:
                postalCode: '01310100'
                publicArea: Av. Paulista
                number: '1000'
                complement: Conj. 101
                neighborhood: Bela Vista
                city: São Paulo
                state: SP
      responses:
        '201':
          description: Cliente criado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
              example:
                id: cus_8kQ2mZ7xW1pL4nR0
                type: INDIVIDUAL
                name: Ana Souza
                document: '12345678909'
                email: ana.souza@example.com
                phone:
                  areaCode: '11'
                  number: '999990000'
                address:
                  postalCode: '01310100'
                  publicArea: Av. Paulista
                  number: '1000'
                  complement: Conj. 101
                  neighborhood: Bela Vista
                  city: São Paulo
                  state: SP
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T13:00:00Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:customers:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `CUSTOMER.DOCUMENT_CONFLICT`: documento já é cliente deste merchant; extensão `existingCustomerId`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                CUSTOMER.DOCUMENT_CONFLICT:
                  $ref: '#/components/examples/CUSTOMER.DOCUMENT_CONFLICT'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `errors[].code` `VALIDATION.REQUIRED` (`name`, `document`, `email`), `VALIDATION.FORMAT` (CPF/CNPJ, e-mail, telefone, CEP, UF), `VALIDATION.MAX`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    get:
      operationId: listCustomers
      summary: Listar clientes
      description: |-
        Lista as pessoas com papel de cliente do merchant. Sem filtro por documento ou e-mail (PII fora da URL). Ordem `createdAt` desc (data em que virou cliente), `id` desc.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Clientes
      security:
      - kodanoOAuth:
        - payments:customers:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: type
        in: query
        required: false
        description: Tipo do cliente.
        schema:
          type: string
          enum:
          - INDIVIDUAL
          - COMPANY
      - name: createdFrom
        in: query
        required: false
        description: Inclusivo.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: Exclusivo.
        schema:
          type: string
          format: date-time
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de clientes.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerList'
              example:
                data:
                - id: cus_8kQ2mZ7xW1pL4nR0
                  type: INDIVIDUAL
                  name: Ana Souza
                  document: '12345678909'
                  email: ana.souza@example.com
                  phone:
                    areaCode: '11'
                    number: '999990000'
                  address: null
                  createdAt: '2026-10-16T13:00:00Z'
                  updatedAt: '2026-10-16T13:00:00Z'
                hasMore: true
                nextCursor: eyJjIjoiMjAyNi0xMC0xNlQxMzowMDowMFoiLCJpIjoxMjM0fQ.4Kq2
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:customers:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `limit` fora de 1–100; `VALIDATION.RANGE` (`createdFrom > createdTo`) ou `VALIDATION.ENUM` (`type`)
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/customers/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do cliente (`cus_…`).
      schema:
        type: string
    get:
      operationId: getCustomer
      summary: Consultar cliente
      description: |-
        Devolve o cliente com documento completo (só aqui e na lista).

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Clientes
      security:
      - kodanoOAuth:
        - payments:customers:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Cliente.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
              example:
                id: cus_8kQ2mZ7xW1pL4nR0
                type: INDIVIDUAL
                name: Ana Souza
                document: '12345678909'
                email: ana.souza@example.com
                phone:
                  areaCode: '11'
                  number: '999990000'
                address:
                  postalCode: '01310100'
                  publicArea: Av. Paulista
                  number: '1000'
                  complement: Conj. 101
                  neighborhood: Bela Vista
                  city: São Paulo
                  state: SP
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T13:00:00Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:customers:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant, pessoa só pagadora ou `cus_` malformado; corpo idêntico
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    patch:
      operationId: updateCustomer
      summary: Atualizar cliente
      description: |-
        Atualização parcial de `name`, `email`, `phone` e `address`. Campo omitido não muda; `null` não apaga (422). Em `COMPANY`, `name` substitui a razão social.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Clientes
      security:
      - kodanoOAuth:
        - payments:customers:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomerRequest'
            example:
              email: ana@novoemail.com.br
              phone:
                areaCode: '21'
                number: '988887777'
      responses:
        '200':
          description: Cliente atualizado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
              example:
                id: cus_8kQ2mZ7xW1pL4nR0
                type: INDIVIDUAL
                name: Ana Souza
                document: '12345678909'
                email: ana@novoemail.com.br
                phone:
                  areaCode: '21'
                  number: '988887777'
                address:
                  postalCode: '01310100'
                  publicArea: Av. Paulista
                  number: '1000'
                  complement: Conj. 101
                  neighborhood: Bela Vista
                  city: São Paulo
                  state: SP
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T15:42:10Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:customers:write` (só `:read` não basta)
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant, pessoa só pagadora ou `cus_` malformado
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: campo enviado como `null`, ou fora das regras do POST
            - `CUSTOMER.DOCUMENT_IMMUTABLE`: corpo contém `document` ou `type`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CUSTOMER.DOCUMENT_IMMUTABLE:
                  $ref: '#/components/examples/CUSTOMER.DOCUMENT_IMMUTABLE'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/cards:
    post:
      operationId: createCard
      summary: Salvar cartão no cofre
      description: |-
        Registra no cofre um cartão tokenizado pelo SDK de tokenização (`token` = `cryptData`, uso único, 5 min). `brand` vem do provedor. Replay devolve o mesmo `card_`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Cartões
      security:
      - kodanoOAuth:
        - payments:vault:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCardRequest'
            example:
              customerId: cus_8kQ2mZ7xW1pL4nR0
              token: <token gerado pelo SDK de tokenização no navegador do portador>
              holderName: ANA SOUZA
              expiry: 12/28
              lastFour: '4242'
      responses:
        '201':
          description: Cartão salvo (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
              example:
                id: card_8f2kq1Zp0rT7wXc3vLm9NbQ4aYs
                customerId: cus_8kQ2mZ7xW1pL4nR0
                brand: VISA
                last4: '4242'
                expiry: 12/28
                holderName: ANA SOUZA
                status: ACTIVE
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:vault:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `customerId`, `token`, `holderName`, `expiry` ou `lastFour` ausente ou fora do formato
            - `CARD.PAN_NOT_ALLOWED`: `number`, `pan`, `cvv` ou `cvc` no corpo
            - `CARD.TOKEN_INVALID`: token com ≤ 255 caracteres, expirado (5 min) ou já usado
            - `CUSTOMER.NOT_FOUND`: `customerId` inexistente, de outro merchant, sem papel de cliente ou malformado
            - `CARD.BRAND_UNSUPPORTED`: bandeira não aceita
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CARD.PAN_NOT_ALLOWED:
                  $ref: '#/components/examples/CARD.PAN_NOT_ALLOWED'
                CARD.TOKEN_INVALID:
                  $ref: '#/components/examples/CARD.TOKEN_INVALID'
                CUSTOMER.NOT_FOUND:
                  $ref: '#/components/examples/CUSTOMER.NOT_FOUND'
                CARD.BRAND_UNSUPPORTED:
                  $ref: '#/components/examples/CARD.BRAND_UNSUPPORTED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: 5xx ou timeout no registro no provedor; `Retry-After: 30`, chave fica `IN_PROGRESS`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listCards
      summary: Listar cartões do cliente
      description: |-
        Lista os cartões do cofre de um cliente (`customerId` obrigatório). `customerId` de outro merchant, inexistente ou malformado → 200 com lista vazia.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Cartões
      security:
      - kodanoOAuth:
        - payments:vault:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: customerId
        in: query
        required: true
        description: Cliente dono dos cartões.
        schema:
          type: string
          pattern: ^cus_
      - name: status
        in: query
        required: false
        description: Status do cartão.
        schema:
          type: string
          enum:
          - ACTIVE
          - INACTIVE
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de cartões.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardList'
              example:
                data:
                - id: card_8f2kq1Zp0rT7wXc3vLm9NbQ4aYs
                  customerId: cus_8kQ2mZ7xW1pL4nR0
                  brand: VISA
                  last4: '4242'
                  expiry: 12/28
                  holderName: ANA SOUZA
                  status: ACTIVE
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:vault:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: sem `customerId` (`VALIDATION.REQUIRED`); `status` desconhecido (`VALIDATION.ENUM`); `limit` fora de 1–100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/cards/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do cartão do cofre (`card_…`).
      schema:
        type: string
    get:
      operationId: getCard
      summary: Consultar cartão
      description: |-
        Devolve o cartão do cofre; `INACTIVE` continua legível.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Cartões
      security:
      - kodanoOAuth:
        - payments:vault:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Cartão.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
              example:
                id: card_8f2kq1Zp0rT7wXc3vLm9NbQ4aYs
                customerId: cus_8kQ2mZ7xW1pL4nR0
                brand: VISA
                last4: '4242'
                expiry: 12/28
                holderName: ANA SOUZA
                status: ACTIVE
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:vault:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant ou malformado
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    delete:
      operationId: deleteCard
      summary: Inativar cartão
      description: |-
        Inativa o cartão (`status: INACTIVE`); o provedor não exclui. Idempotente por natureza: já `INACTIVE` → 204 de novo. Depois disso, `card.id` em `POST /v1/payments/cards` → 422 `CARD.INACTIVE`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Cartões
      security:
      - kodanoOAuth:
        - payments:vault:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Cartão inativado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:vault:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant ou malformado
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `CARD.IN_USE`: cartão de caução não encerrada
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                CARD.IN_USE:
                  $ref: '#/components/examples/CARD.IN_USE'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payment-links:
    post:
      operationId: createPaymentLink
      summary: Criar link de pagamento
      description: |-
        Cria um link de checkout hospedado. Com `customer` → uso único (`maxPayments` 1). Sem `customer` → uso múltiplo com teto `maxPayments` (padrão 1). Captura automática, sem split, itens, caução ou repasse de tarifa. `externalReference` é o dedupe recomendado quando a chave se perde.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Links de pagamento
      security:
      - kodanoOAuth:
        - payments:links:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePaymentLinkRequest'
            example:
              amount: 15990
              currency: BRL
              name: Plano anual
              description: Assinatura anual do plano Pro
              methods:
              - PIX
              - CARD
              maxPayments: 1
              maxInstallments: 3
              expiresAt: '2026-10-23T13:00:00Z'
              customer:
                id: cus_8kQ2mZ7xW1pL4nR0
              externalReference: pedido-123
              successUrl: https://loja.example.com/obrigado
              failureUrl: https://loja.example.com/pagamento-falhou
              metadata:
                orderRef: '123'
      responses:
        '201':
          description: Link criado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentLink'
              example:
                id: ord_2mK9xQ7vR4tL8nW1pZ6cY3bH5jD0
                url: https://pay.kodano.com.br/8b1f6c0e-3d2a-4f7b-9c1e-5a6d7e8f9a0b
                status: ACTIVE
                amount: 15990
                currency: BRL
                name: Plano anual
                description: Assinatura anual do plano Pro
                methods:
                - PIX
                - CARD
                maxPayments: 1
                paymentsCount: 0
                maxInstallments: 3
                expiresAt: '2026-10-23T13:00:00Z'
                customer:
                  id: cus_8kQ2mZ7xW1pL4nR0
                  name: Ana Souza
                  document: 123******09
                externalReference: pedido-123
                successUrl: https://loja.example.com/obrigado
                failureUrl: https://loja.example.com/pagamento-falhou
                metadata:
                  orderRef: '123'
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T13:00:00Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:links:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `PAYMENT_LINK.DUPLICATE_REFERENCE`: `externalReference` já usada neste merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                PAYMENT_LINK.DUPLICATE_REFERENCE:
                  $ref: '#/components/examples/PAYMENT_LINK.DUPLICATE_REFERENCE'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `VALIDATION.MIN|MAX` (faixas de `amount`, `maxPayments`, `maxInstallments`, `expiresAt`, tamanhos), `VALIDATION.CURRENCY_UNSUPPORTED`, `VALIDATION.ONE_OF`, `VALIDATION.FORMAT` (URL não https, `methods` repetido, `maxInstallments` sem `CARD`, `metadata`)
            - `CUSTOMER.NOT_FOUND`: `customer.id` inexistente, de outro merchant, sem papel de cliente ou malformado
            - `PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE`: `customer` com `maxPayments` > 1
            - `MERCHANT.NOT_CONFIGURED`: merchant sem pricing ou sem conector ativo para algum método
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CUSTOMER.NOT_FOUND:
                  $ref: '#/components/examples/CUSTOMER.NOT_FOUND'
                PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE:
                  $ref: '#/components/examples/PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE'
                MERCHANT.NOT_CONFIGURED:
                  $ref: '#/components/examples/MERCHANT.NOT_CONFIGURED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: pricing do merchant indisponível; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listPaymentLinks
      summary: Listar links de pagamento
      description: |-
        Lista os links do merchant pelo status efetivo (link vencido sai em `EXPIRED`). Links de caução nunca aparecem.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Links de pagamento
      security:
      - kodanoOAuth:
        - payments:links:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status efetivo.
        schema:
          $ref: '#/components/schemas/PaymentLinkStatus'
      - name: customerId
        in: query
        required: false
        description: De outro merchant, inexistente ou malformado → lista vazia.
        schema:
          type: string
          pattern: ^cus_
      - name: externalReference
        in: query
        required: false
        description: Igualdade exata.
        schema:
          type: string
          maxLength: 100
      - name: createdFrom
        in: query
        required: false
        description: Inclusivo.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: Exclusivo.
        schema:
          type: string
          format: date-time
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de links.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentLinkList'
              example:
                data:
                - id: ord_2mK9xQ7vR4tL8nW1pZ6cY3bH5jD0
                  url: https://pay.kodano.com.br/8b1f6c0e-3d2a-4f7b-9c1e-5a6d7e8f9a0b
                  status: ACTIVE
                  amount: 15990
                  currency: BRL
                  name: Plano anual
                  description: Assinatura anual do plano Pro
                  methods:
                  - PIX
                  - CARD
                  maxPayments: 1
                  paymentsCount: 0
                  maxInstallments: 3
                  expiresAt: '2026-10-23T13:00:00Z'
                  customer:
                    id: cus_8kQ2mZ7xW1pL4nR0
                    name: Ana Souza
                    document: 123******09
                  externalReference: pedido-123
                  successUrl: https://loja.example.com/obrigado
                  failureUrl: https://loja.example.com/pagamento-falhou
                  metadata:
                    orderRef: '123'
                  createdAt: '2026-10-16T13:00:00Z'
                  updatedAt: '2026-10-16T13:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:links:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `limit` fora de 1–100; `VALIDATION.RANGE` ou `VALIDATION.ENUM` (`status`)
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payment-links/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do link (`ord_…`).
      schema:
        type: string
    get:
      operationId: getPaymentLink
      summary: Consultar link de pagamento
      description: |-
        Devolve o link. O desfecho chega por `payment.succeeded` com `paymentLinkId`; os pagamentos do link saem em `GET /v1/payments?paymentLinkId=`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Links de pagamento
      security:
      - kodanoOAuth:
        - payments:links:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Link.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentLink'
              example:
                id: ord_2mK9xQ7vR4tL8nW1pZ6cY3bH5jD0
                url: https://pay.kodano.com.br/8b1f6c0e-3d2a-4f7b-9c1e-5a6d7e8f9a0b
                status: COMPLETED
                amount: 15990
                currency: BRL
                name: Plano anual
                description: Assinatura anual do plano Pro
                methods:
                - PIX
                - CARD
                maxPayments: 1
                paymentsCount: 1
                maxInstallments: 3
                expiresAt: '2026-10-23T13:00:00Z'
                customer:
                  id: cus_8kQ2mZ7xW1pL4nR0
                  name: Ana Souza
                  document: 123******09
                externalReference: pedido-123
                successUrl: https://loja.example.com/obrigado
                failureUrl: https://loja.example.com/pagamento-falhou
                metadata:
                  orderRef: '123'
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T13:07:41Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:links:read`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant, malformado ou link de caução
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/payment-links/{id}/cancel:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do link (`ord_…`).
      schema:
        type: string
    post:
      operationId: cancelPaymentLink
      summary: Cancelar link de pagamento
      description: |-
        Impede novos pagamentos no link; PIX e boleto já emitidos continuam válidos até o próprio vencimento. Sem corpo. A segunda chamada com chave nova dá 409 `PAYMENT_LINK.NOT_CANCELABLE` (`currentStatus: CANCELED`); com a mesma chave, replay do 200.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Links de pagamento
      security:
      - kodanoOAuth:
        - payments:links:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Link cancelado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentLink'
              example:
                id: ord_2mK9xQ7vR4tL8nW1pZ6cY3bH5jD0
                url: https://pay.kodano.com.br/8b1f6c0e-3d2a-4f7b-9c1e-5a6d7e8f9a0b
                status: CANCELED
                amount: 15990
                currency: BRL
                name: Plano anual
                description: Assinatura anual do plano Pro
                methods:
                - PIX
                - CARD
                maxPayments: 1
                paymentsCount: 0
                maxInstallments: 3
                expiresAt: '2026-10-23T13:00:00Z'
                customer:
                  id: cus_8kQ2mZ7xW1pL4nR0
                  name: Ana Souza
                  document: 123******09
                externalReference: pedido-123
                successUrl: https://loja.example.com/obrigado
                failureUrl: https://loja.example.com/pagamento-falhou
                metadata:
                  orderRef: '123'
                createdAt: '2026-10-16T13:00:00Z'
                updatedAt: '2026-10-16T14:20:05Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: credencial sem `payments:links:write`
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `RESOURCE.NOT_FOUND`: inexistente, de outro merchant, malformado ou link de caução
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                RESOURCE.NOT_FOUND:
                  $ref: '#/components/examples/RESOURCE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `PAYMENT_LINK.NOT_CANCELABLE`: status efetivo `COMPLETED`, `EXPIRED` ou `CANCELED`; extensão `currentStatus`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                PAYMENT_LINK.NOT_CANCELABLE:
                  $ref: '#/components/examples/PAYMENT_LINK.NOT_CANCELABLE'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/balance:
    get:
      operationId: getBalance
      summary: Consultar saldo
      description: |-
        Saldo da carteira `default` do merchant em `Money` por bucket. Merchant sem ledger → 200 com buckets zerados e `asOf` = agora. Saldo e extrato lidos em momentos diferentes podem divergir.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Saldo e extrato
      security:
      - kodanoOAuth:
        - receivables:balance:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Saldo.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Balance'
              example:
                available:
                  amount: 1523490
                  currency: BRL
                pending:
                  amount: 830000
                  currency: BRL
                blocked:
                  amount: 120000
                  currency: BRL
                blockedBreakdown:
                  reserve:
                    amount: 100000
                    currency: BRL
                  chargeback:
                    amount: 20000
                    currency: BRL
                asOf: '2026-10-16T14:05:12Z'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: treasury com erro, timeout ou indisponível; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/statement:
    get:
      operationId: getStatement
      summary: Consultar extrato
      description: |-
        Lançamentos da conta `available` da carteira `default`, em `occurredAt` decrescente; `balanceAfter` fecha com o saldo do treasury em toda página. Tarifas vêm somadas no lançamento (o saque sai num só `WITHDRAWAL` de `totalAmount`). `type` e `category` filtram depois do cálculo do saldo: a página pode vir menor que `limit` com `hasMore: true`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Saldo e extrato
      security:
      - kodanoOAuth:
        - receivables:statement:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: from
        in: query
        required: false
        description: Início inclusivo; padrão `to` − 30 dias.
        schema:
          type: string
          format: date-time
      - name: to
        in: query
        required: false
        description: Fim exclusivo; padrão agora; janela máxima de 93 dias.
        schema:
          type: string
          format: date-time
      - name: type
        in: query
        required: false
        description: Tipo do lançamento.
        schema:
          type: string
          enum:
          - CREDIT
          - DEBIT
      - name: category
        in: query
        required: false
        description: Categoria.
        schema:
          type: string
          enum:
          - PAYMENT
          - FEE
          - WITHDRAWAL
          - REFUND
          - CHARGEBACK
          - ADJUSTMENT
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página do extrato.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatementList'
              example:
                data:
                - id: ste_k3v9q0d2m7x1c8b5n4r6t2y0wa
                  type: DEBIT
                  category: WITHDRAWAL
                  amount: 150150
                  currency: BRL
                  balanceAfter: 1523490
                  referenceId: wdr_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
                  occurredAt: '2026-10-16T13:58:02Z'
                - id: ste_p8n2w5c1x9v4b7m3k6q0r2t5ya
                  type: CREDIT
                  category: PAYMENT
                  amount: 48250
                  currency: BRL
                  balanceAfter: 1673640
                  referenceId: pay_8f2c1e9a
                  occurredAt: '2026-10-16T12:40:11Z'
                hasMore: true
                nextCursor: eyJ2IjoxLCJ0YyI6IiIsIm8iOjIsInJiIjoxNjczNjQwfQ.Xq3vM8pL2nR7
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `VALIDATION.INVALID_ENUM` (`type`, `category`), `VALIDATION.FROM_AFTER_TO` ou `VALIDATION.RANGE_TOO_LARGE` (> 93 dias); `limit` fora de 1 a 100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: treasury fora ou com timeout; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/settlements:
    get:
      operationId: listSettlements
      summary: Listar liquidações
      description: |-
        Um lote por data de liquidação, em ordem decrescente de data, sem `items`. Agenda futura sai `SCHEDULED`; data chegada e ainda sem liquidação sai `PROCESSING`. O sync é diário: o status pode atrasar até 24 h.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Liquidações
      security:
      - kodanoOAuth:
        - receivables:settlements:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: from
        in: query
        required: false
        description: Data de liquidação, inclusiva; padrão hoje − 30.
        schema:
          type: string
          format: date
      - name: to
        in: query
        required: false
        description: Data de liquidação, inclusiva; padrão hoje + 30; janela máxima de 93 dias.
        schema:
          type: string
          format: date
      - name: status
        in: query
        required: false
        description: Status do lote.
        schema:
          $ref: '#/components/schemas/SettlementStatus'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de liquidações.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettlementList'
              example:
                data:
                - id: stl_202610179c4e2a7f01b3d58e6a2c4f17
                  status: SCHEDULED
                  settlementDate: '2026-10-17'
                  currency: BRL
                  transactionCount: 12
                  grossAmount: 250000
                  feesAmount: 7250
                  netAmount: 242750
                  settledAt: null
                - id: stl_2026101501a33e1fc42bb5898fe70dbb
                  status: SETTLED
                  settlementDate: '2026-10-15'
                  currency: BRL
                  transactionCount: 3
                  grossAmount: 99000
                  feesAmount: 2871
                  netAmount: 96129
                  settledAt: '2026-10-15T00:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `VALIDATION.INVALID_ENUM` (`status`), `VALIDATION.FROM_AFTER_TO`, `VALIDATION.RANGE_TOO_LARGE`; data malformada; `limit` fora de 1 a 100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: falha ao resolver a conta de liquidação ou ler o read model; `Retry-After: 30`; nunca 200 vazio
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/settlements/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da liquidação (`stl_` + `yyyyMMdd` + 24 hex).
      schema:
        type: string
    get:
      operationId: getSettlement
      summary: Consultar liquidação
      description: |-
        Lote com `items` paginados por `itemsLimit`/`itemsCursor`. Itens `FAILED` e `REVERSED` aparecem em `items` mas ficam fora dos totais. O id embute o hash do merchant: com outro `X-Merchant-Id` responde 404. O `id` do evento `settlement.closed` abre nesta rota.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Liquidações
      security:
      - kodanoOAuth:
        - receivables:settlements:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: itemsLimit
        in: query
        required: false
        description: Tamanho da página de itens.
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
      - name: itemsCursor
        in: query
        required: false
        description: '`items.nextCursor` da chamada anterior.'
        schema:
          type: string
      responses:
        '200':
          description: Liquidação com itens.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettlementDetail'
              example:
                id: stl_2026101501a33e1fc42bb5898fe70dbb
                status: SETTLED
                settlementDate: '2026-10-15'
                currency: BRL
                transactionCount: 3
                grossAmount: 99000
                feesAmount: 2871
                netAmount: 96129
                settledAt: '2026-10-15T00:00:00Z'
                items:
                  data:
                  - paymentId: pay_8f2c1e9a
                    method: CREDIT_CARD
                    installment: 2
                    status: SETTLED
                    anticipated: false
                    grossAmount: 33000
                    feesAmount: 957
                    netAmount: 32043
                  - paymentId: pay_5d1a7e3b
                    method: CREDIT_CARD
                    installment: 1
                    status: SETTLED
                    anticipated: true
                    grossAmount: 33000
                    feesAmount: 957
                    netAmount: 32043
                  hasMore: true
                  nextCursor: eyJtIjoiQ1JFRElUX0NBUkQiLCJ0IjoxMjM0NSwiaSI6MSwiciI6ZmFsc2V9.Lr8vT2
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `SETTLEMENT.NOT_FOUND`: id malformado ou data sem linhas para o merchant do contexto
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                SETTLEMENT.NOT_FOUND:
                  $ref: '#/components/examples/SETTLEMENT.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `itemsLimit` fora de 1 a 100
            - `CURSOR.INVALID`: `itemsCursor` inválido ou adulterado
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: banco do payments fora; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/withdrawals:
    post:
      operationId: createWithdrawal
      summary: Solicitar saque
      description: |-
        Solicita saque para destino `VERIFIED` do próprio merchant (`bankAccountId` ou `pixKeyId`). A tarifa é cobrada por cima: `totalAmount` sai do saldo e `netAmount` (= `amount`) chega ao destino. Chave PIX (ou conta com chave) → `PIX_KEY`; conta sem chave → `BANK_ACCOUNT`; sem TED na v1. O 201 não garante pagamento: acima do limite de aprovação automática fica `REQUESTED` até aprovação manual; o desfecho chega por `withdrawal.completed`/`withdrawal.failed`. Limite diário por credencial (padrão R$ 50.000,00, dia de America/Sao_Paulo) cumulativo com o teto do merchant. Throttle próprio: 1 req/s (burst 2).

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Saques
      security:
      - kodanoOAuth:
        - payouts:withdrawals:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWithdrawalRequest'
            example:
              amount: 150000
              currency: BRL
              destination:
                pixKeyId: pxk_0192f3a2b7c84d1e9f0a1b2c3d4e5f60
              description: Repasse semanal outubro
      responses:
        '201':
          description: Saque solicitado (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Withdrawal'
              example:
                id: wdr_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
                status: REQUESTED
                amount: 150000
                currency: BRL
                fee: 150
                totalAmount: 150150
                netAmount: 150000
                method: PIX_KEY
                destination:
                  type: PIX_KEY
                  id: pxk_0192f3a2b7c84d1e9f0a1b2c3d4e5f60
                failure: null
                createdVia: API
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: credencial do token deixou de estar `ACTIVE` (checagem do serviço)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com outro corpo, inclusive depois das 24 h (a chave derivada fica gravada no saque)
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `amount` não inteiro ou ≤ 0, `currency` ≠ `BRL`, `destination` com zero ou dois ids, dados bancários no corpo, id malformado, `description` > 140
            - `WITHDRAWAL.DESTINATION_NOT_FOUND`: `bka_`/`pxk_` inexistente, removido ou de outro merchant
            - `WITHDRAWAL.DESTINATION_NOT_VERIFIED`: destino não `VERIFIED`
            - `WITHDRAWAL.METHOD_NOT_ALLOWED`: merchant sem capability para o trilho do destino
            - `WITHDRAWAL.AMOUNT_OUT_OF_RANGE`: abaixo de 1000, acima de 10000000 ou acima do limite individual do merchant
            - `WITHDRAWAL.INSUFFICIENT_BALANCE`: `available` menor que `amount + fee`
            - `WITHDRAWAL.DAILY_LIMIT_EXCEEDED`: estoura o limite da credencial ou o teto diário do merchant; extensões `limitScope`, `remainingAmount`, `resetAt`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                WITHDRAWAL.DESTINATION_NOT_FOUND:
                  $ref: '#/components/examples/WITHDRAWAL.DESTINATION_NOT_FOUND'
                WITHDRAWAL.DESTINATION_NOT_VERIFIED:
                  $ref: '#/components/examples/WITHDRAWAL.DESTINATION_NOT_VERIFIED'
                WITHDRAWAL.METHOD_NOT_ALLOWED:
                  $ref: '#/components/examples/WITHDRAWAL.METHOD_NOT_ALLOWED'
                WITHDRAWAL.AMOUNT_OUT_OF_RANGE:
                  $ref: '#/components/examples/WITHDRAWAL.AMOUNT_OUT_OF_RANGE'
                WITHDRAWAL.INSUFFICIENT_BALANCE:
                  $ref: '#/components/examples/WITHDRAWAL.INSUFFICIENT_BALANCE'
                WITHDRAWAL.DAILY_LIMIT_EXCEEDED:
                  $ref: '#/components/examples/WITHDRAWAL.DAILY_LIMIT_EXCEEDED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: treasury fora antes do INSERT; `Retry-After: 30`; a chave é liberada (repetir com a mesma)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listWithdrawals
      summary: Listar saques
      description: |-
        Saques do merchant (API e portal, ver `createdVia`), do mais novo para o mais antigo. Saque do portal com destino informado na hora vem com `destination.id: null`. Um `FAILED` pode virar `COMPLETED` ou `REQUESTED` depois.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Saques
      security:
      - kodanoOAuth:
        - payouts:withdrawals:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status do saque.
        schema:
          type: string
          enum:
          - REQUESTED
          - PROCESSING
          - COMPLETED
          - FAILED
      - name: createdFrom
        in: query
        required: false
        description: Criados a partir de.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: Criados até.
        schema:
          type: string
          format: date-time
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de saques.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WithdrawalList'
              example:
                data:
                - id: wdr_0192e8c4a1b27d3e9f4a5b6c7d8e9f01
                  status: FAILED
                  amount: 50000
                  currency: BRL
                  fee: 150
                  totalAmount: 50150
                  netAmount: 50000
                  method: BANK_ACCOUNT
                  destination:
                    type: BANK_ACCOUNT
                    id: bka_018f6c2de9a47b3c8d1e2f3a4b5c6d7e
                  failure:
                    code: WITHDRAWAL.PAYOUT_FAILED
                  createdVia: API
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `status` fora do enum, data malformada, `limit` fora de 1 a 100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/withdrawals/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do saque (`wdr_…`).
      schema:
        type: string
    get:
      operationId: getWithdrawal
      summary: Consultar saque
      description: |-
        Devolve o saque. `PROCESSING` cobre payout com desfecho desconhecido até a reconciliação; o motivo interno de rejeição nunca sai, só `failure.code`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Saques
      security:
      - kodanoOAuth:
        - payouts:withdrawals:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Saque.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Withdrawal'
              example:
                id: wdr_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
                status: PROCESSING
                amount: 150000
                currency: BRL
                fee: 150
                totalAmount: 150150
                netAmount: 150000
                method: PIX_KEY
                destination:
                  type: PIX_KEY
                  id: pxk_0192f3a2b7c84d1e9f0a1b2c3d4e5f60
                failure: null
                createdVia: API
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WITHDRAWAL.NOT_FOUND`: id inexistente, malformado ou de outro merchant, com corpo idêntico
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WITHDRAWAL.NOT_FOUND:
                  $ref: '#/components/examples/WITHDRAWAL.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/bank-accounts:
    post:
      operationId: createBankAccount
      summary: Cadastrar conta bancária de saque
      description: |-
        Cadastra conta bancária como destino de saque. Nasce `PENDING_VERIFICATION`, `createdVia: API`, nunca primária; só vira `VERIFIED` pela confirmação humana no portal. O merchant recebe e-mail. Máximo de 20 destinos pendentes por merchant (contas + chaves). `holderMatchesMerchant: false` não bloqueia na v1.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBankAccountRequest'
            example:
              bankCode: '341'
              branch: '1234'
              account: 56789-0
              type: CHECKING
              holderName: ACME LTDA
              holderTaxId: '12345678000199'
      responses:
        '201':
          description: Conta cadastrada (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankAccount'
              example:
                id: bka_0192f3a1c4e87b2d9a0e1f2a3b4c5d6e
                status: PENDING_VERIFICATION
                bankCode: '341'
                branch: '1234'
                accountLast4: '7890'
                type: CHECKING
                holderName: ACME LTDA
                holderMatchesMerchant: true
                createdVia: API
                createdAt: '2026-10-16T13:00:00Z'
                verifiedAt: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `BANK_ACCOUNT.ALREADY_REGISTERED`: mesmo banco, agência e conta já cadastrados e não removidos
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                BANK_ACCOUNT.ALREADY_REGISTERED:
                  $ref: '#/components/examples/BANK_ACCOUNT.ALREADY_REGISTERED'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: campo ausente, `account` sem dígito, `branch` fora do padrão, `type` fora do enum, documento inválido
            - `BANK_ACCOUNT.TYPE_NOT_ALLOWED`: tipo aceito no portal e não na API (`SALARY`)
            - `DESTINATION.PENDING_LIMIT_REACHED`: o merchant já tem 20 destinos pendentes
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                BANK_ACCOUNT.TYPE_NOT_ALLOWED:
                  $ref: '#/components/examples/BANK_ACCOUNT.TYPE_NOT_ALLOWED'
                DESTINATION.PENDING_LIMIT_REACHED:
                  $ref: '#/components/examples/DESTINATION.PENDING_LIMIT_REACHED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    get:
      operationId: listBankAccounts
      summary: Listar contas bancárias de saque
      description: |-
        Contas vivas do merchant, de qualquer origem, da mais nova para a mais antiga; conta removida não aparece. Conta verificada antes da migration vem `VERIFIED` com `verifiedAt: null`.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status do destino.
        schema:
          type: string
          enum:
          - PENDING_VERIFICATION
          - VERIFIED
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de contas.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankAccountList'
              example:
                data:
                - id: bka_018f6c2de9a47b3c8d1e2f3a4b5c6d7e
                  status: VERIFIED
                  bankCode: '001'
                  branch: 4321-5
                  accountLast4: '0123'
                  type: CHECKING
                  holderName: ACME LTDA
                  holderMatchesMerchant: true
                  createdVia: PORTAL
                  createdAt: '2026-03-02T17:21:08Z'
                  verifiedAt: null
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `status` fora do enum ou `limit` fora de 1 a 100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/bank-accounts/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da conta bancária de saque (`bka_…`).
      schema:
        type: string
    delete:
      operationId: deleteBankAccount
      summary: Remover conta bancária de saque
      description: |-
        Soft delete: a conta some das listas; saques que já a usam continuam legíveis. Remover destino pendente equivale a rejeitá-lo. Repetir o `DELETE` responde 404.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Conta removida.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `BANK_ACCOUNT.NOT_FOUND`: id inexistente, malformado, já removido ou de outro merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                BANK_ACCOUNT.NOT_FOUND:
                  $ref: '#/components/examples/BANK_ACCOUNT.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN`: conta primária do merchant enquanto houver outras contas
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN:
                  $ref: '#/components/examples/BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/pix-keys:
    post:
      operationId: createPixKey
      summary: Cadastrar chave PIX de saque
      description: |-
        Cadastra chave PIX como destino de saque (não é chave de recebimento). O DICT é consultado só aqui: `holderName`, `institutionIspb` e `holderMatchesMerchant` saem dele. Nasce `PENDING_VERIFICATION`; só vira `VERIFIED` pela confirmação no portal.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePixKeyRequest'
            example:
              type: EMAIL
              key: financeiro@acme.com.br
      responses:
        '201':
          description: Chave cadastrada (ou replay).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PixKey'
              example:
                id: pxk_0192f3a2b7c84d1e9f0a1b2c3d4e5f60
                status: PENDING_VERIFICATION
                type: EMAIL
                holderName: ACME LTDA
                institutionIspb: '60701190'
                holderMatchesMerchant: true
                createdVia: API
                createdAt: '2026-10-16T13:02:41Z'
                verifiedAt: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `PIX_KEY.ALREADY_REGISTERED`: mesma chave (`type` + valor) viva no merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                PIX_KEY.ALREADY_REGISTERED:
                  $ref: '#/components/examples/PIX_KEY.ALREADY_REGISTERED'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `type` fora do enum ou `key` vazia ou malformada
            - `PIX_KEY.NOT_FOUND_IN_DICT`: o DICT não conhece a chave
            - `PIX_KEY.TYPE_MISMATCH`: `type` diferente do `keyType` devolvido pelo DICT
            - `PIX_KEY.NO_ACTIVE_ACCOUNT`: merchant sem conta digital ativa para consultar o DICT
            - `DESTINATION.PENDING_LIMIT_REACHED`: o merchant já tem 20 destinos pendentes
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                PIX_KEY.NOT_FOUND_IN_DICT:
                  $ref: '#/components/examples/PIX_KEY.NOT_FOUND_IN_DICT'
                PIX_KEY.TYPE_MISMATCH:
                  $ref: '#/components/examples/PIX_KEY.TYPE_MISMATCH'
                PIX_KEY.NO_ACTIVE_ACCOUNT:
                  $ref: '#/components/examples/PIX_KEY.NO_ACTIVE_ACCOUNT'
                DESTINATION.PENDING_LIMIT_REACHED:
                  $ref: '#/components/examples/DESTINATION.PENDING_LIMIT_REACHED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: DICT ou adquirente fora antes do INSERT; `Retry-After: 30`; a chave é liberada
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listPixKeys
      summary: Listar chaves PIX de saque
      description: |-
        Chaves de saque vivas do merchant, da mais nova para a mais antiga. Não consulta o DICT: os dados do titular são os gravados no cadastro.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status do destino.
        schema:
          type: string
          enum:
          - PENDING_VERIFICATION
          - VERIFIED
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de chaves.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PixKeyList'
              example:
                data:
                - id: pxk_0192f3a2b7c84d1e9f0a1b2c3d4e5f60
                  status: PENDING_VERIFICATION
                  type: EMAIL
                  holderName: ACME LTDA
                  institutionIspb: '60701190'
                  holderMatchesMerchant: true
                  createdVia: API
                  createdAt: '2026-10-16T13:02:41Z'
                  verifiedAt: null
                hasMore: false
                nextCursor: null
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `status` fora do enum ou `limit` fora de 1 a 100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/pix-keys/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da chave PIX de saque (`pxk_…`).
      schema:
        type: string
    delete:
      operationId: deletePixKey
      summary: Remover chave PIX de saque
      description: |-
        Soft delete: a chave some das listas; saques que já a usam continuam legíveis. Repetir o `DELETE` responde 404.

        `X-Merchant-Id` é obrigatório com credencial de organização; com credencial de merchant é opcional (ausente = o próprio merchant).
      tags:
      - Destinos de saque
      security:
      - kodanoOAuth:
        - payouts:destinations:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Chave removida.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '400':
          description: |-
            Requisição inválida.

            - `MERCHANT.HEADER_REQUIRED`: credencial de organização sem `X-Merchant-Id`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                MERCHANT.HEADER_REQUIRED:
                  $ref: '#/components/examples/MERCHANT.HEADER_REQUIRED'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `PIX_KEY.NOT_FOUND`: id inexistente, malformado, já removido ou de outro merchant
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                PIX_KEY.NOT_FOUND:
                  $ref: '#/components/examples/PIX_KEY.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/webhook-endpoints:
    post:
      operationId: createWebhookEndpoint
      summary: Criar webhook endpoint
      description: |-
        Cria um endpoint que recebe eventos assinados. Credencial de organização sem `X-Merchant-Id` cria `ORGANIZATION`; com o header (ou credencial de merchant) cria `MERCHANT`; escopo e `merchantId` não mudam depois. URL `https` na porta 443 ou 8443, sem fragmento nem userinfo, que não resolva para IP privado. Até 10 endpoints por escopo e ambiente. O `secret` aparece só nesta resposta; o replay devolve 201 com o mesmo `id` e `secret: null`.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookEndpointRequest'
            example:
              url: https://hooks.parceiro.com.br/kodano
              events:
              - payment.succeeded
              - payment.failed
              - payment.refunded
              description: ERP financeiro
      responses:
        '201':
          description: 'Endpoint criado (ou replay com `secret: null`).'
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpointWithSecret'
              example:
                id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                url: https://hooks.parceiro.com.br/kodano
                description: ERP financeiro
                events:
                - payment.succeeded
                - payment.failed
                - payment.refunded
                status: ACTIVE
                scope: MERCHANT
                merchantId: mrc_e3cc4727054da558273162251e310d29
                failureCount: 0
                lastSuccessAt: null
                lastFailureAt: null
                disabledAt: null
                disabledReason: null
                previousSecretExpiresAt: null
                createdAt: '2026-10-16T12:40:00Z'
                updatedAt: '2026-10-16T12:40:00Z'
                secret: whsec_XsH9ts25IljV8jgbNWhU4DWlbQzMdJ3q_Sc4GTU51tY
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: `X-Auth-Entitlements` não cobre o escopo de leitura de algum tipo de `events` do endpoint resultante
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `url` ou `events` ausentes; `events` vazio, com mais de 20 itens ou repetido; `description` acima de 255
            - `WEBHOOK.URL_INVALID`: URL fora da política; motivo em `errors[].code` (`WEBHOOK.URL_MALFORMED`, `URL_CREDENTIALS_NOT_ALLOWED`, `URL_NOT_HTTPS`, `URL_PORT_NOT_ALLOWED`, `URL_HOST_NOT_ALLOWED`, `URL_PRIVATE_ADDRESS`, `URL_UNRESOLVABLE`)
            - `WEBHOOK.EVENT_UNKNOWN`: tipo fora do catálogo v1 ou não assinável (`ping`)
            - `WEBHOOK.LIMIT_EXCEEDED`: já há 10 endpoints não excluídos no escopo e ambiente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                WEBHOOK.URL_INVALID:
                  $ref: '#/components/examples/WEBHOOK.URL_INVALID'
                WEBHOOK.EVENT_UNKNOWN:
                  $ref: '#/components/examples/WEBHOOK.EVENT_UNKNOWN'
                WEBHOOK.LIMIT_EXCEEDED:
                  $ref: '#/components/examples/WEBHOOK.LIMIT_EXCEEDED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: KMS indisponível ao cifrar o segredo; nada gravado; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
    get:
      operationId: listWebhookEndpoints
      summary: Listar webhook endpoints
      description: |-
        Escopo `ORGANIZATION` lista os endpoints da organização e de todos os merchants do tenant; escopo `MERCHANT` lista só os do merchant. Excluídos e o `secret` nunca aparecem; só o ambiente do host.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status do endpoint.
        schema:
          type: string
          enum:
          - ACTIVE
          - DISABLED
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de endpoints.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpointList'
              example:
                data:
                - id: whe_izfjJZWGfTqJPVzq8t9rvtgg8t9kJStn
                  url: https://hooks.parceiro.com.br/kodano-org
                  description: Conciliação central
                  events:
                  - settlement.closed
                  - withdrawal.completed
                  - withdrawal.failed
                  - webhook_endpoint.disabled
                  status: ACTIVE
                  scope: ORGANIZATION
                  merchantId: null
                  failureCount: 0
                  lastSuccessAt: '2026-10-16T13:20:04Z'
                  lastFailureAt: null
                  disabledAt: null
                  disabledReason: null
                  previousSecretExpiresAt: null
                  createdAt: '2026-10-16T12:50:00Z'
                  updatedAt: '2026-10-16T13:20:04Z'
                - id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                  url: https://hooks.parceiro.com.br/kodano
                  description: ERP financeiro
                  events:
                  - payment.succeeded
                  - payment.failed
                  - payment.refunded
                  status: ACTIVE
                  scope: MERCHANT
                  merchantId: mrc_e3cc4727054da558273162251e310d29
                  failureCount: 0
                  lastSuccessAt: '2026-10-16T13:02:12Z'
                  lastFailureAt: null
                  disabledAt: null
                  disabledReason: null
                  previousSecretExpiresAt: null
                  createdAt: '2026-10-16T12:40:00Z'
                  updatedAt: '2026-10-16T13:02:12Z'
                hasMore: false
                nextCursor: null
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `status` fora do enum; `limit` fora de 1–100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/webhook-endpoints/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do webhook endpoint (`whe_…`).
      schema:
        type: string
    get:
      operationId: getWebhookEndpoint
      summary: Consultar webhook endpoint
      description: |-
        Devolve o endpoint. Organização vê endpoint de merchant do tenant; merchant não vê endpoint de organização nem de outro merchant (404).

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Endpoint.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
              example:
                id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                url: https://hooks.parceiro.com.br/kodano
                description: ERP financeiro
                events:
                - payment.succeeded
                - payment.failed
                - payment.refunded
                status: ACTIVE
                scope: MERCHANT
                merchantId: mrc_e3cc4727054da558273162251e310d29
                failureCount: 0
                lastSuccessAt: '2026-10-16T13:02:12Z'
                lastFailureAt: null
                disabledAt: null
                disabledReason: null
                previousSecretExpiresAt: null
                createdAt: '2026-10-16T12:40:00Z'
                updatedAt: '2026-10-16T13:02:12Z'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: inexistente, excluído, de outro tenant ou ambiente, ou fora do escopo do chamador
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    patch:
      operationId: updateWebhookEndpoint
      summary: Atualizar webhook endpoint
      description: |-
        Atualiza `url`, `events`, `description` e/ou `status`. `status: DISABLED` grava `disabledReason: MANUAL` (sem evento nem e-mail; entregas pendentes terminam `CANCELED`). `status: ACTIVE` zera `failureCount`, `disabledAt` e `disabledReason`; o que não foi entregue no período não é reenviado (usar `redeliver`).

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookEndpointRequest'
            example:
              events:
              - payment.succeeded
              - payment.failed
              - payment.refunded
              - chargeback.received
              description: ERP financeiro v2
      responses:
        '200':
          description: Endpoint atualizado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
              example:
                id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                url: https://hooks.parceiro.com.br/kodano
                description: ERP financeiro v2
                events:
                - payment.succeeded
                - payment.failed
                - payment.refunded
                - chargeback.received
                status: ACTIVE
                scope: MERCHANT
                merchantId: mrc_e3cc4727054da558273162251e310d29
                failureCount: 0
                lastSuccessAt: '2026-10-16T13:02:12Z'
                lastFailureAt: null
                disabledAt: null
                disabledReason: null
                previousSecretExpiresAt: null
                createdAt: '2026-10-16T12:40:00Z'
                updatedAt: '2026-10-16T14:05:00Z'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: `X-Auth-Entitlements` não cobre o escopo de leitura de algum tipo de `events` do endpoint resultante
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: inexistente, excluído ou fora do escopo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `RESOURCE.CONFLICT`: o endpoint mudou em paralelo (auto-desativação, outro PATCH, rotação ou DELETE)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RESOURCE.CONFLICT:
                  $ref: '#/components/examples/RESOURCE.CONFLICT'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: nenhum campo aceito; `scope` ou `merchantId` enviados; `status` fora de `ACTIVE`/`DISABLED`; limites de `events` e `description`
            - `WEBHOOK.URL_INVALID`: nova `url` fora da política
            - `WEBHOOK.EVENT_UNKNOWN`: tipo fora do catálogo ou `ping`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                WEBHOOK.URL_INVALID:
                  $ref: '#/components/examples/WEBHOOK.URL_INVALID'
                WEBHOOK.EVENT_UNKNOWN:
                  $ref: '#/components/examples/WEBHOOK.EVENT_UNKNOWN'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
    delete:
      operationId: deleteWebhookEndpoint
      summary: Excluir webhook endpoint
      description: |-
        Exclusão lógica: o endpoint some de todas as rotas (404 depois), os segredos são apagados e as entregas pendentes terminam `CANCELED`. Não conta no teto de 10.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Endpoint excluído.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: inexistente, fora do escopo ou já excluído
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `RESOURCE.CONFLICT`: o endpoint mudou em paralelo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RESOURCE.CONFLICT:
                  $ref: '#/components/examples/RESOURCE.CONFLICT'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/webhook-endpoints/{id}/rotate-secret:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do webhook endpoint (`whe_…`).
      schema:
        type: string
    post:
      operationId: rotateWebhookEndpointSecret
      summary: Rotacionar segredo do webhook endpoint
      description: |-
        Gera novo segredo. Até `previousSecretExpiresAt` cada entrega leva duas assinaturas (`v1=<novo>,v1=<anterior>`); com `overlapHours: 0` o anterior deixa de assinar na hora. Nova rotação dentro da janela descarta o mais antigo (no máximo dois vigentes). O novo `secret` aparece só nesta resposta; o replay devolve 200 com `secret: null`.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RotateSecretRequest'
            example:
              overlapHours: 24
      responses:
        '200':
          description: 'Segredo rotacionado (ou replay com `secret: null`).'
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RotateSecretResponse'
              example:
                id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                secret: whsec_Ra8Kq2Vn7Tz1Lm4Px9Cw3Fs6Hb0Jd5Gy2Ue8Io1Kp7Q
                previousSecretExpiresAt: '2026-10-17T14:10:00Z'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: `X-Auth-Entitlements` não cobre os tipos de `events` do endpoint
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: inexistente, excluído ou fora do escopo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `IDEMPOTENCY.CONFLICT`: mesma chave com corpo ou path concreto diferente
            - `IDEMPOTENCY.IN_PROGRESS`: mesma chave em execução ou com desfecho desconhecido; extensão `resourceId` e `Retry-After: 2`
            - `RESOURCE.CONFLICT`: o endpoint mudou em paralelo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IDEMPOTENCY.CONFLICT'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IDEMPOTENCY.IN_PROGRESS'
                RESOURCE.CONFLICT:
                  $ref: '#/components/examples/RESOURCE.CONFLICT'
        '413':
          description: |-
            Corpo grande demais.

            - `REQUEST.TOO_LARGE`: corpo acima de 1 MB em rota idempotente
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                REQUEST.TOO_LARGE:
                  $ref: '#/components/examples/REQUEST.TOO_LARGE'
        '422':
          description: |-
            Não processável.

            - `IDEMPOTENCY.KEY_REQUIRED`: `Idempotency-Key` ausente ou não-UUID
            - `VALIDATION.FAILED`: `overlapHours` fora de 0–72
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.KEY_REQUIRED:
                  $ref: '#/components/examples/IDEMPOTENCY.KEY_REQUIRED'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
            - `PAYMENT.PROVIDER_UNAVAILABLE`: KMS indisponível; nada gravado; `Retry-After: 30`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
                PAYMENT.PROVIDER_UNAVAILABLE:
                  $ref: '#/components/examples/PAYMENT.PROVIDER_UNAVAILABLE'
  /v1/webhook-endpoints/{id}/test:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do webhook endpoint (`whe_…`).
      schema:
        type: string
    post:
      operationId: testWebhookEndpoint
      summary: Testar webhook endpoint
      description: |-
        Grava um evento `ping` e uma entrega `kind: TEST` e responde sem esperar o POST no parceiro; o `ping` chega assinado com o segredo atual em até ~15 s. Uma tentativa só, sem retry; falha não altera `failureCount`. Permitido em endpoint `DISABLED`. Sem corpo.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '202':
          description: Teste agendado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResponse'
              example:
                deliveryId: dlv_TJHo7OZTJLzpjeUuP14rbelsntNNVz9Y
                eventId: evt_3p9AGoWE8UWuBC7IXAOVmKwQSr82BLkj
                status: PENDING
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: `X-Auth-Entitlements` não cobre os tipos de `events` do endpoint
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: inexistente, excluído ou fora do escopo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
            - `WEBHOOK.TEST_RATE_LIMITED`: mais de 10 testes por minuto no endpoint, com `Retry-After`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
                WEBHOOK.TEST_RATE_LIMITED:
                  $ref: '#/components/examples/WEBHOOK.TEST_RATE_LIMITED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/webhook-endpoints/{id}/deliveries:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do webhook endpoint (`whe_…`).
      schema:
        type: string
    get:
      operationId: listWebhookDeliveries
      summary: Listar entregas do webhook endpoint
      description: |-
        Uma linha por entrega (`dlv_`); `attempt` conta as tentativas feitas, e `at`, `httpStatus`, `latencyMs` e `error` são da última. `FAILED` = esgotou as 8 tentativas ou falhou o `TEST`; `CANCELED` = endpoint desativado/excluído ou cliente dono sem entitlement. Retenção de 30 dias.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Webhook endpoints
      security:
      - kodanoOAuth:
        - webhooks:endpoints:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      - name: status
        in: query
        required: false
        description: Status da entrega.
        schema:
          type: string
          enum:
          - PENDING
          - RETRYING
          - SUCCEEDED
          - FAILED
          - CANCELED
      - name: eventId
        in: query
        required: false
        description: Entregas de um evento.
        schema:
          type: string
          pattern: ^evt_
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de entregas.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveryList'
              example:
                data:
                - id: dlv_ZrkZZHHC1NoGGox6w0q4q7qbATrRtvOO
                  eventId: evt_M2yotFuA2lvn2cxfaDitt0GDJbOlKMAj
                  eventType: payment.failed
                  kind: EVENT
                  status: RETRYING
                  attempt: 2
                  httpStatus: 503
                  latencyMs: 412
                  at: '2026-10-16T13:16:05Z'
                  nextAttemptAt: '2026-10-16T13:21:05Z'
                  error: HTTP_5XX
                - id: dlv_TJHo7OZTJLzpjeUuP14rbelsntNNVz9Y
                  eventId: evt_3p9AGoWE8UWuBC7IXAOVmKwQSr82BLkj
                  eventType: ping
                  kind: TEST
                  status: SUCCEEDED
                  attempt: 1
                  httpStatus: 200
                  latencyMs: 184
                  at: '2026-10-16T14:12:03Z'
                  nextAttemptAt: null
                  error: null
                hasMore: false
                nextCursor: null
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `WEBHOOK.NOT_FOUND`: endpoint inexistente, excluído ou fora do escopo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `status` fora do enum; `eventId` malformado; `limit` fora de 1–100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/events:
    get:
      operationId: listEvents
      summary: Listar eventos
      description: |-
        Cada item é o envelope exatamente como foi assinado e entregue. Escopo `ORGANIZATION` vê os eventos da organização e de todos os merchants do tenant; `MERCHANT` só os do merchant; sempre só o ambiente do host. Tipos cujo escopo de leitura o chamador não tem somem da lista, sem erro. `ping` só aparece com `type=ping`. Retenção de 30 dias.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Eventos
      security:
      - kodanoOAuth:
        - events:events:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      - name: type
        in: query
        required: false
        description: Tipo do catálogo, até 10 tipos; `ping` só quando pedido explicitamente.
        style: form
        explode: true
        schema:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/EventType'
      - name: createdFrom
        in: query
        required: false
        description: Não pode ser anterior à retenção de 30 dias.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: '`createdFrom > createdTo` → 422.'
        schema:
          type: string
          format: date-time
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Página de eventos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventList'
              example:
                data:
                - id: evt_NAReVGpteI9IpjpimY69TtHrjIyl5ykV
                  type: payment.succeeded
                  apiVersion: '2026-10-01'
                  createdAt: '2026-10-16T13:02:11Z'
                  environment: sandbox
                  merchantId: mrc_e3cc4727054da558273162251e310d29
                  data:
                    object:
                      id: pay_Nj7ZJCZ9jM7SGGmnjGl9UAXLW7BpYwCL
                      status: SUCCEEDED
                      method: PIX
                      amount: 15990
                      currency: BRL
                      description: Pedido 123
                      customer:
                        id: cus_Lbfbv3xA5PQh8H5jKlF7yXqRNJxlSZXf
                        name: Ana Souza
                        document: 123******09
                      pix:
                        qrCode: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                        copyPaste: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                        expiresAt: '2026-10-16T14:00:00Z'
                        endToEndId: E18236120202610161302s0a1b2c3d4e
                      failure: null
                      statusReason: null
                      refundedAmount: 0
                      refundedAt: null
                      paymentLinkId: null
                      metadata:
                        orderRef: '123'
                      createdAt: '2026-10-16T13:00:00Z'
                      updatedAt: '2026-10-16T13:02:10Z'
                hasMore: true
                nextCursor: eyJjIjoiMjAyNi0xMC0xNlQxMzowMjoxMVoiLCJpIjoiZXZ0X05BUmVWR3B0ZUk5SXBqcGltWTY5VHRIcmpJeWw1eWtWIn0
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
        '422':
          description: |-
            Não processável.

            - `VALIDATION.FAILED`: `createdFrom > createdTo`; mais de 10 tipos; data malformada; `limit` fora de 1–100
            - `CURSOR.INVALID`: cursor adulterado, inválido, de outro merchant ou de outro conjunto de filtros/`limit`
            - `EVENT.TYPE_UNKNOWN`: `type` fora do catálogo v1
            - `EVENT.RANGE_OUT_OF_RETENTION`: `createdFrom` anterior a 30 dias
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
                CURSOR.INVALID:
                  $ref: '#/components/examples/CURSOR.INVALID'
                EVENT.TYPE_UNKNOWN:
                  $ref: '#/components/examples/EVENT.TYPE_UNKNOWN'
                EVENT.RANGE_OUT_OF_RETENTION:
                  $ref: '#/components/examples/EVENT.RANGE_OUT_OF_RETENTION'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/events/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do evento (`evt_…`).
      schema:
        type: string
    get:
      operationId: getEvent
      summary: Consultar evento
      description: |-
        Devolve o envelope do evento; `data.object` retrata o recurso no instante do fato (o estado atual está no `GET` do recurso).

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Eventos
      security:
      - kodanoOAuth:
        - events:events:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Event'
              example:
                id: evt_eLAeXfIkpXkuN51gMG1J3aYxprFLfIAo
                type: payment.refunded
                apiVersion: '2026-10-01'
                createdAt: '2026-10-16T18:30:02Z'
                environment: sandbox
                merchantId: mrc_e3cc4727054da558273162251e310d29
                data:
                  object:
                    id: pay_Nj7ZJCZ9jM7SGGmnjGl9UAXLW7BpYwCL
                    status: PARTIALLY_REFUNDED
                    method: PIX
                    amount: 15990
                    currency: BRL
                    description: Pedido 123
                    customer:
                      id: cus_Lbfbv3xA5PQh8H5jKlF7yXqRNJxlSZXf
                      name: Ana Souza
                      document: 123******09
                    pix:
                      qrCode: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                      copyPaste: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                      expiresAt: '2026-10-16T14:00:00Z'
                      endToEndId: E18236120202610161302s0a1b2c3d4e
                    failure: null
                    statusReason: null
                    refundedAmount: 5000
                    refundedAt: '2026-10-16T18:30:00Z'
                    paymentLinkId: null
                    metadata:
                      orderRef: '123'
                    createdAt: '2026-10-16T13:00:00Z'
                    updatedAt: '2026-10-16T18:30:00Z'
                  refund:
                    id: ref_EN9MqxQZIpd3nf12U7pIXifnAXOs7j69
                    amount: 5000
                    currency: BRL
                    status: SUCCEEDED
                    reason: null
                    failure: null
                    createdAt: '2026-10-16T18:29:48Z'
                    updatedAt: '2026-10-16T18:30:00Z'
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `EVENT.NOT_FOUND`: inexistente, fora do escopo ou ambiente, tipo sem escopo de leitura do chamador, ou com mais de 30 dias
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                EVENT.NOT_FOUND:
                  $ref: '#/components/examples/EVENT.NOT_FOUND'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/events/{id}/redeliver:
    parameters:
    - name: id
      in: path
      required: true
      description: Id do evento (`evt_…`).
      schema:
        type: string
    post:
      operationId: redeliverEvent
      summary: Reentregar evento
      description: |-
        Reenvia o mesmo `evt_` com o mesmo corpo (novo `t`, nova assinatura) numa entrega nova (`kind: REDELIVER`), com a política de retry completa de 24 h. Sem `endpointId`, vai a todos os endpoints `ACTIVE` do escopo que assinam o tipo. Limite: 5 reentregas por evento e endpoint por hora.

        `X-Merchant-Id` é opcional: credencial de organização sem o header atua no escopo `ORGANIZATION` (organização e todos os merchants do tenant); com o header, no escopo `MERCHANT` daquele merchant. Credencial de merchant atua sempre no próprio merchant.
      tags:
      - Eventos
      security:
      - kodanoOAuth:
        - webhooks:endpoints:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdOptional'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RedeliverRequest'
            example:
              endpointId: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
      responses:
        '202':
          description: Reentrega agendada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RedeliverResponse'
              example:
                eventId: evt_NAReVGpteI9IpjpimY69TtHrjIyl5ykV
                deliveries:
                - id: dlv_HEjT8DDrbYeRWCNhu1R9EWa2ppn6Pr5k
                  endpointId: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                  status: PENDING
        '401':
          description: |-
            Não autenticado.

            - `AUTH.TOKEN_INVALID`: sem `Authorization: Bearer`, token inativo ou credencial não `ACTIVE` (corpo único no fio, com `WWW-Authenticate: Bearer`)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TOKEN_INVALID:
                  $ref: '#/components/examples/AUTH.TOKEN_INVALID'
        '403':
          description: |-
            Proibido.

            - `AUTH.TENANT_MISMATCH`: organização do token diferente da organização da credencial
            - `AUTH.ENVIRONMENT_MISMATCH`: credencial de um ambiente chamando o host do outro
            - `AUTH.ENTITLEMENT_MISSING`: escopo da rota não concedido; `detail` nomeia o escopo
            - `AUTH.CONTEXT_MISSING`: contexto `X-Auth-*` ausente, incompleto ou repetido no serviço
            - `MERCHANT.FORBIDDEN`: `X-Merchant-Id` malformado, repetido, inexistente, fora do tenant ou diferente do merchant da credencial de merchant (nunca 404)
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TENANT_MISMATCH:
                  $ref: '#/components/examples/AUTH.TENANT_MISMATCH'
                AUTH.ENVIRONMENT_MISMATCH:
                  $ref: '#/components/examples/AUTH.ENVIRONMENT_MISMATCH'
                AUTH.ENTITLEMENT_MISSING:
                  $ref: '#/components/examples/AUTH.ENTITLEMENT_MISSING'
                AUTH.CONTEXT_MISSING:
                  $ref: '#/components/examples/AUTH.CONTEXT_MISSING'
                MERCHANT.FORBIDDEN:
                  $ref: '#/components/examples/MERCHANT.FORBIDDEN'
        '404':
          description: |-
            Não encontrado.

            - `ROUTE.NOT_FOUND`: rota inexistente ou fora da v1
            - `EVENT.NOT_FOUND`: evento inexistente, fora do escopo ou com mais de 30 dias
            - `WEBHOOK.NOT_FOUND`: `endpointId` inexistente, excluído ou fora do escopo
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                ROUTE.NOT_FOUND:
                  $ref: '#/components/examples/ROUTE.NOT_FOUND'
                EVENT.NOT_FOUND:
                  $ref: '#/components/examples/EVENT.NOT_FOUND'
                WEBHOOK.NOT_FOUND:
                  $ref: '#/components/examples/WEBHOOK.NOT_FOUND'
        '409':
          description: |-
            Conflito.

            - `WEBHOOK.ENDPOINT_DISABLED`: `endpointId` está `DISABLED`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                WEBHOOK.ENDPOINT_DISABLED:
                  $ref: '#/components/examples/WEBHOOK.ENDPOINT_DISABLED'
        '422':
          description: |-
            Não processável.

            - `WEBHOOK.EVENT_NOT_SUBSCRIBED`: `endpointId` não assina o tipo do evento
            - `WEBHOOK.NO_MATCHING_ENDPOINT`: sem `endpointId`, nenhum endpoint `ACTIVE` do escopo assina o tipo
            - `VALIDATION.FAILED`: `endpointId` malformado
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                WEBHOOK.EVENT_NOT_SUBSCRIBED:
                  $ref: '#/components/examples/WEBHOOK.EVENT_NOT_SUBSCRIBED'
                WEBHOOK.NO_MATCHING_ENDPOINT:
                  $ref: '#/components/examples/WEBHOOK.NO_MATCHING_ENDPOINT'
                VALIDATION.FAILED:
                  $ref: '#/components/examples/VALIDATION.FAILED'
        '429':
          description: |-
            Limite de requisições.

            - `RATE_LIMIT.EXCEEDED`: rate, burst ou quota diária (só sandbox) da credencial; `Retry-After: 1` ou `3600`
            - `WEBHOOK.REDELIVER_RATE_LIMITED`: todos os endpoints alvo já tiveram 5 reentregas do evento na última hora, com `Retry-After`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RATE_LIMIT.EXCEEDED'
                WEBHOOK.REDELIVER_RATE_LIMITED:
                  $ref: '#/components/examples/WEBHOOK.REDELIVER_RATE_LIMITED'
        '500':
          description: |-
            Erro interno.

            - `INTERNAL.ERROR`: falha não mapeada no serviço
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                INTERNAL.ERROR:
                  $ref: '#/components/examples/INTERNAL.ERROR'
        '503':
          description: |-
            Indisponível.

            - `AUTH.TEMPORARILY_UNAVAILABLE`: IdP, banco ou authorizer indisponível; `Retry-After: 1`
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                AUTH.TEMPORARILY_UNAVAILABLE:
                  $ref: '#/components/examples/AUTH.TEMPORARILY_UNAVAILABLE'
  /v1/contract-templates:
    get:
      operationId: listContractTemplates
      tags:
      - Modelos de contrato
      summary: Listar modelos de contrato
      description: "Modelos (.docx com tags) ativos do merchant, criados no portal ou pela API, do mais novo para o mais antigo.\n\n- Lista curta, numa página só: `hasMore` é sempre `false` e `nextCursor`, `null`.\n- `isReadyToIssue: false` → o modelo ainda não pode emitir contrato (`CONTRACT.TEMPLATE_NOT_READY`).\n- Sem `contracts:templates:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:templates:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Modelos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractTemplateList'
              example:
                data:
                - id: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                  name: Contrato de locação por temporada
                  description: null
                  isReadyToIssue: true
                  tags:
                  - tag: imovel
                    label: Imóvel
                    inputType: text
                    requiredForSigner: false
                  - tag: inicio
                    label: Início
                    inputType: date
                    requiredForSigner: false
                  createdAt: '2026-10-20T12:00:00Z'
                  updatedAt: '2026-10-20T12:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    post:
      operationId: createContractTemplate
      tags:
      - Modelos de contrato
      summary: Enviar modelo de contrato
      description: "Cria um modelo a partir de um .docx com tags `{{tag}}`, com `Location: /v1/contract-templates/{id}`.\n\n- O mesmo arquivo (SHA-256) já enviado pelo merchant → 200 com o modelo existente, sem criar outro.\n- Arquivo que não é .docx, acima do limite, sem tags ou com tag inválida → 422 `CONTRACT.FILE_INVALID`.\n- O corpo inteiro cabe em 10 MB (limite do gateway); acima disso → 413.\n- Merchant sem contratos habilitados → 422 `CONTRACT.NOT_ENABLED`.\n- Sem `contracts:templates:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:templates:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        description: Arquivo .docx e metadados.
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContractTemplateUpload'
      responses:
        '200':
          description: O mesmo arquivo já era um modelo do merchant.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractTemplate'
              example:
                id: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                name: Contrato de locação por temporada
                description: null
                isReadyToIssue: true
                tags:
                - tag: imovel
                  label: Imóvel
                  inputType: text
                  requiredForSigner: false
                - tag: inicio
                  label: Início
                  inputType: date
                  requiredForSigner: false
                createdAt: '2026-10-20T12:00:00Z'
                updatedAt: '2026-10-20T12:00:00Z'
        '201':
          description: Modelo criado.
          headers:
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractTemplate'
              example:
                id: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                name: Contrato de locação por temporada
                description: null
                isReadyToIssue: true
                tags:
                - tag: imovel
                  label: Imóvel
                  inputType: text
                  requiredForSigner: false
                - tag: inicio
                  label: Início
                  inputType: date
                  requiredForSigner: false
                createdAt: '2026-10-20T12:00:00Z'
                updatedAt: '2026-10-20T12:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Mesma Idempotency-Key com outro corpo, ainda em execução, ou conflito de estado.
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IdempotencyConflict'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IdempotencyInProgress'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contract-templates/analyze:
    post:
      operationId: analyzeContractTemplate
      tags:
      - Modelos de contrato
      summary: Analisar modelo de contrato
      description: "Lê o .docx e devolve as tags encontradas, sem criar nada. Útil para conferir o arquivo antes do envio.\n\n- `existingTemplateId` aponta o modelo do merchant com o mesmo conteúdo, quando houver.\n- Arquivo inválido → 422 `CONTRACT.FILE_INVALID`.\n- Sem `contracts:templates:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:templates:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        description: Arquivo .docx.
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContractTemplateUpload'
      responses:
        '200':
          description: Análise do arquivo.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractTemplateAnalysis'
              example:
                fileName: locacao.docx
                sizeBytes: 48210
                sha256: 9f2c6a0e1b4d7c3a8e5f2b1d0c9a8b7e6f5d4c3b2a1908f7e6d5c4b3a2918f7e
                tags:
                - imovel
                - inicio
                - fim
                - valor
                existingTemplateId: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contract-templates/{id}:
    parameters:
    - $ref: '#/components/parameters/ContractTemplateIdPath'
    get:
      operationId: getContractTemplate
      tags:
      - Modelos de contrato
      summary: Consultar modelo de contrato
      description: "Devolve o modelo com as tags a preencher em `tagValues` na emissão.\n\n- Inexistente, malformado ou de outro merchant → 404 `CONTRACT_TEMPLATE.NOT_FOUND`.\n- Sem `contracts:templates:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:templates:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Modelo.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractTemplate'
              example:
                id: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                name: Contrato de locação por temporada
                description: null
                isReadyToIssue: true
                tags:
                - tag: imovel
                  label: Imóvel
                  inputType: text
                  requiredForSigner: false
                - tag: inicio
                  label: Início
                  inputType: date
                  requiredForSigner: false
                createdAt: '2026-10-20T12:00:00Z'
                updatedAt: '2026-10-20T12:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractTemplateNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contract-files:
    get:
      operationId: listContractFiles
      tags:
      - Arquivos de contrato
      summary: Listar arquivos de contrato
      description: "PDFs ativos da biblioteca do merchant, do mais novo para o mais antigo.\n\n- Lista curta, numa página só: `hasMore` é sempre `false` e `nextCursor`, `null`.\n- Sem `contracts:contracts:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Arquivos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractFileList'
              example:
                data:
                - id: cfl_0192f3a09e8d4c7b6a5f4e3d2c1b0a90
                  name: Regulamento do condomínio
                  description: null
                  fileName: regulamento.pdf
                  sizeBytes: 182340
                  sha256: 9f2c6a0e1b4d7c3a8e5f2b1d0c9a8b7e6f5d4c3b2a1908f7e6d5c4b3a2918f7e
                  createdAt: '2026-10-20T12:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    post:
      operationId: createContractFile
      tags:
      - Arquivos de contrato
      summary: Enviar PDF de contrato
      description: "Guarda um PDF próprio na biblioteca do merchant para emitir contrato com `contractFileId`.\n\n- O mesmo arquivo (SHA-256) já enviado → 200 com o arquivo existente.\n- Não é PDF, está criptografado ou passa do limite → 422 `CONTRACT.FILE_INVALID`; corpo acima de 10 MB → 413.\n- Merchant sem contratos habilitados → 422 `CONTRACT.NOT_ENABLED`.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        description: Arquivo PDF e metadados.
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContractFileUpload'
      responses:
        '200':
          description: O mesmo arquivo já estava na biblioteca.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractFile'
              example:
                id: cfl_0192f3a09e8d4c7b6a5f4e3d2c1b0a90
                name: Regulamento do condomínio
                description: null
                fileName: regulamento.pdf
                sizeBytes: 182340
                sha256: 9f2c6a0e1b4d7c3a8e5f2b1d0c9a8b7e6f5d4c3b2a1908f7e6d5c4b3a2918f7e
                createdAt: '2026-10-20T12:00:00Z'
        '201':
          description: Arquivo criado.
          headers:
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractFile'
              example:
                id: cfl_0192f3a09e8d4c7b6a5f4e3d2c1b0a90
                name: Regulamento do condomínio
                description: null
                fileName: regulamento.pdf
                sizeBytes: 182340
                sha256: 9f2c6a0e1b4d7c3a8e5f2b1d0c9a8b7e6f5d4c3b2a1908f7e6d5c4b3a2918f7e
                createdAt: '2026-10-20T12:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Mesma Idempotency-Key com outro corpo, ainda em execução, ou conflito de estado.
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IdempotencyConflict'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IdempotencyInProgress'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts:
    post:
      operationId: createContract
      tags:
      - Contratos
      summary: Emitir contrato
      description: "Emite um contrato para assinatura eletrônica, a partir de um modelo (`contractTemplateId` + `tagValues`) **ou** de um\nPDF da biblioteca (`contractFileId`), com `Location: /v1/contracts/{id}`.\n\n- Cada signatário recebe por e-mail o pedido de assinatura e volta com `signUrl`, o link da Kodano que abre a\n  assinatura. Mande esse link no seu fluxo, se quiser; nunca guarde nem exponha outro link.\n- De 1 a 10 signatários; exatamente um `payer` (sem nenhum `role`, o primeiro é o `payer`).\n- Toda tag do modelo exigida precisa vir em `tagValues`; tag desconhecida → 422 `CONTRACT.TAG_MISSING`.\n- `expiresAt` padrão: 30 dias. Vencido sem todas as assinaturas, o contrato vira `expired`.\n- Modelo ou arquivo inexistente, inativo ou de outro merchant → 422 `CONTRACT.SOURCE_INVALID`.\n- Merchant sem contratos habilitados → 422 `CONTRACT.NOT_ENABLED`.\n- Provedor de assinatura indisponível → 503 `CONTRACT.PROVIDER_UNAVAILABLE` com `Retry-After`; repita com a\n  mesma chave.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractRequest'
            example:
              contractTemplateId: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
              name: Locação Villa Trancoso 21–28/11
              signers:
              - name: Ana Souza
                email: ana@exemplo.com
                phone:
                  areaCode: '71'
                  number: '999990000'
                role: payer
              tagValues:
              - tag: imovel
                value: Villa Trancoso
              - tag: inicio
                value: 21/11/2026
              expiresAt: '2026-11-27T15:00:00Z'
              allowRefusal: true
      responses:
        '201':
          description: Contrato emitido.
          headers:
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
              example:
                id: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                status: pending_signature
                name: Locação Villa Trancoso 21–28/11
                description: null
                contractTemplateId: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                contractFileId: null
                signersExpected: 1
                signersSigned: 0
                requiredSignersExpected: 1
                requiredSignersSigned: 0
                allowRefusal: true
                advancedAuth: false
                refusedReason: null
                expiresAt: '2026-11-27T15:00:00Z'
                effectiveAt: null
                signedAt: null
                canceledAt: null
                hasSignedFile: false
                tagValues:
                - tag: imovel
                  value: Villa Trancoso
                - tag: inicio
                  value: 21/11/2026
                signers:
                - id: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                  sequence: 1
                  role: payer
                  isRequired: true
                  name: Ana Souza
                  email: ana@exemplo.com
                  status: new
                  timesViewed: 0
                  lastViewedAt: null
                  signedAt: null
                  refusedAt: null
                  signUrl: https://api.kodano.com.br/merchant/v1/public/contracts/r/q8Zt3mW1xY7pK2vB9nL4cR6hD0sF5gJ
                createdAt: '2026-10-28T15:00:00Z'
                updatedAt: '2026-10-28T15:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Mesma Idempotency-Key com outro corpo, ainda em execução, ou conflito de estado.
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IdempotencyConflict'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IdempotencyInProgress'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    get:
      operationId: listContracts
      tags:
      - Contratos
      summary: Listar contratos
      description: "Contratos do merchant, do mais novo para o mais antigo, criados pela API ou pelo portal.\n\n- Cada item é um resumo; signatários, tags e o resto do contrato estão em `GET /contracts/{id}`.\n- Filtros: `status` e `contractTemplateId`. Valor fora do enum → 422 `VALIDATION.FAILED`.\n- Sem `contracts:contracts:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:read
      parameters:
      - name: status
        in: query
        required: false
        schema:
          $ref: '#/components/schemas/ContractStatus'
      - name: contractTemplateId
        in: query
        required: false
        schema:
          type: string
        example: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Página de contratos.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractList'
              example:
                data:
                - id: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                  status: pending_signature
                  name: Locação Villa Trancoso 21–28/11
                  description: null
                  contractTemplateId: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                  signersExpected: 1
                  signersSigned: 0
                  expiresAt: '2026-11-27T15:00:00Z'
                  effectiveAt: null
                  signedAt: null
                  hasSignedFile: false
                  createdAt: '2026-10-28T15:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    get:
      operationId: getContract
      tags:
      - Contratos
      summary: Consultar contrato
      description: "Devolve o contrato com os signatários.\n\n- `signUrl` fica `null` para quem já assinou ou recusou e quando o contrato está encerrado.\n- Inexistente, malformado ou de outro merchant → 404 `CONTRACT.NOT_FOUND`.\n- Sem `contracts:contracts:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Contrato.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
              example:
                id: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                status: pending_signature
                name: Locação Villa Trancoso 21–28/11
                description: null
                contractTemplateId: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                contractFileId: null
                signersExpected: 1
                signersSigned: 0
                requiredSignersExpected: 1
                requiredSignersSigned: 0
                allowRefusal: true
                advancedAuth: false
                refusedReason: null
                expiresAt: '2026-11-27T15:00:00Z'
                effectiveAt: null
                signedAt: null
                canceledAt: null
                hasSignedFile: false
                tagValues:
                - tag: imovel
                  value: Villa Trancoso
                - tag: inicio
                  value: 21/11/2026
                signers:
                - id: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                  sequence: 1
                  role: payer
                  isRequired: true
                  name: Ana Souza
                  email: ana@exemplo.com
                  status: new
                  timesViewed: 0
                  lastViewedAt: null
                  signedAt: null
                  refusedAt: null
                  signUrl: https://api.kodano.com.br/merchant/v1/public/contracts/r/q8Zt3mW1xY7pK2vB9nL4cR6hD0sF5gJ
                createdAt: '2026-10-28T15:00:00Z'
                updatedAt: '2026-10-28T15:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}/cancel:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    post:
      operationId: cancelContract
      tags:
      - Contratos
      summary: Cancelar contrato
      description: "Cancela um contrato ainda não encerrado (`canceled`): o documento sai do provedor e os links de assinatura param de\nfuncionar.\n\n- Contrato `signed`, `refused`, `expired` ou `canceled` → 409 `CONTRACT.NOT_EDITABLE`.\n- Repetir com a mesma chave devolve o contrato cancelado.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CancelContractRequest'
            example:
              reason: Reserva cancelada pelo hóspede
      responses:
        '200':
          description: Contrato cancelado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
              example:
                id: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                status: canceled
                name: Locação Villa Trancoso 21–28/11
                description: null
                contractTemplateId: ctt_0192f3a0b1c24d3e8f9a0b1c2d3e4f50
                contractFileId: null
                signersExpected: 1
                signersSigned: 0
                requiredSignersExpected: 1
                requiredSignersSigned: 0
                allowRefusal: true
                advancedAuth: false
                refusedReason: null
                expiresAt: '2026-11-27T15:00:00Z'
                effectiveAt: null
                signedAt: null
                canceledAt: '2026-10-29T10:00:00Z'
                hasSignedFile: false
                tagValues:
                - tag: imovel
                  value: Villa Trancoso
                - tag: inicio
                  value: 21/11/2026
                signers:
                - id: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                  sequence: 1
                  role: payer
                  isRequired: true
                  name: Ana Souza
                  email: ana@exemplo.com
                  status: new
                  timesViewed: 0
                  lastViewedAt: null
                  signedAt: null
                  refusedAt: null
                  signUrl: null
                createdAt: '2026-10-28T15:00:00Z'
                updatedAt: '2026-10-28T15:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}/signed-file:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    get:
      operationId: getContractSignedFile
      tags:
      - Contratos
      summary: Baixar contrato assinado
      description: "Devolve uma URL temporária do PDF assinado.\n\n- Só existe depois de `signed`, quando o arquivo do provedor já foi guardado; antes → 409\n  `CONTRACT.SIGNED_FILE_NOT_AVAILABLE` (tente de novo em alguns minutos).\n- A URL vale até `expiresAt`; peça outra quando vencer.\n- Sem `contracts:contracts:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: URL temporária do PDF assinado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractSignedFile'
              example:
                url: https://kodano-contracts.s3.amazonaws.com/…
                expiresAt: '2026-10-29T10:10:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}/signers:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    post:
      operationId: addContractSigner
      tags:
      - Contratos
      summary: Adicionar signatário
      description: "Acrescenta um signatário a um contrato ainda não encerrado e devolve o signatário com `signUrl`.\n\n- No máximo 10 signatários; contrato encerrado → 409 `CONTRACT.NOT_EDITABLE`.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractSignerInput'
            example:
              name: Bruno Lima
              email: bruno@exemplo.com
              phone:
                areaCode: '71'
                number: '988880000'
              role: guarantor
      responses:
        '201':
          description: Signatário adicionado.
          headers:
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractSigner'
              example:
                id: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                sequence: 1
                role: payer
                isRequired: true
                name: Ana Souza
                email: ana@exemplo.com
                status: new
                timesViewed: 0
                lastViewedAt: null
                signedAt: null
                refusedAt: null
                signUrl: https://api.kodano.com.br/merchant/v1/public/contracts/r/q8Zt3mW1xY7pK2vB9nL4cR6hD0sF5gJ
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}/signers/{signerId}:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    - $ref: '#/components/parameters/SignerIdPath'
    patch:
      operationId: updateContractSigner
      tags:
      - Contratos
      summary: Alterar signatário
      description: "Altera nome, contato, CPF ou autenticação de um signatário que ainda não assinou.\n\n- Signatário que já assinou ou contrato encerrado → 409 `CONTRACT.NOT_EDITABLE`.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateContractSignerRequest'
            example:
              email: ana.souza@exemplo.com
      responses:
        '200':
          description: Signatário alterado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractSigner'
              example:
                id: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                sequence: 1
                role: payer
                isRequired: true
                name: Ana Souza
                email: ana@exemplo.com
                status: new
                timesViewed: 0
                lastViewedAt: null
                signedAt: null
                refusedAt: null
                signUrl: https://api.kodano.com.br/merchant/v1/public/contracts/r/q8Zt3mW1xY7pK2vB9nL4cR6hD0sF5gJ
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    delete:
      operationId: removeContractSigner
      tags:
      - Contratos
      summary: Remover signatário
      description: "Remove um signatário que ainda não assinou. O contrato precisa continuar com um `payer`.\n\n- Signatário que já assinou ou contrato encerrado → 409 `CONTRACT.NOT_EDITABLE`.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Signatário removido.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '422':
          $ref: '#/components/responses/ContractUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/contracts/{id}/signers/{signerId}/resend:
    parameters:
    - $ref: '#/components/parameters/ContractIdPath'
    - $ref: '#/components/parameters/SignerIdPath'
    post:
      operationId: resendContractSigner
      tags:
      - Contratos
      summary: Reenviar pedido de assinatura
      description: "Reenvia ao signatário o pedido de assinatura.\n\n- Um reenvio por signatário a cada 30 minutos; antes disso → 429 `CONTRACT.RESEND_RATE_LIMITED` com `Retry-After`.\n- Signatário que já assinou ou contrato encerrado → 409 `CONTRACT.NOT_EDITABLE`.\n- Sem `contracts:contracts:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - contracts:contracts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '202':
          description: Reenvio aceito.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractSignerResent'
              example:
                signerId: sgn_0192f3c1b2c34d5e6f7a8b9c0d1e2f30
                notifiedAt: '2026-10-28T16:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ContractNotFound'
        '409':
          $ref: '#/components/responses/ContractConflict'
        '429':
          description: Reenvio antes de 30 minutos ou limite de requisições.
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                CONTRACT.RESEND_RATE_LIMITED:
                  summary: CONTRACT.RESEND_RATE_LIMITED
                  value:
                    type: https://api.kodano.com.br/problems/contract-resend-rate-limited
                    title: Signer notified too recently
                    status: 429
                    detail: Wait before notifying this signer again
                    code: CONTRACT.RESEND_RATE_LIMITED
                    correlationId: conc-20261028-0042
                RATE_LIMIT.EXCEEDED:
                  $ref: '#/components/examples/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/deposits:
    post:
      operationId: createDeposit
      tags:
      - Caução
      summary: Abrir caução
      description: "Abre uma caução no cartão do pagador, amarrada a um contrato do merchant, com `Location: /v1/deposits/{id}`.\n\n- A caução nasce `AWAITING_AUTHORIZATION` com `checkoutUrl`: mande o pagador a esse endereço para autorizar o cartão\n  (com 3DS). Aprovada a autorização, vira `ACTIVE`.\n- `contractId` precisa ser um contrato (`ctr_`) do merchant que não esteja `refused`, `expired` nem `canceled`\n  (não precisa estar assinado) → senão 422 `DEPOSIT.CONTRACT_NOT_USABLE`.\n- Informe `customerId` (`cus_`) ou `payer`; com `customerId`, o cartão autorizado precisa ser desse cliente.\n- Modo e tarifa seguem a configuração do merchant na Kodano: `mode` e `feeAmount` voltam na caução.\n- Com `autoRenew`, a Kodano renova a autorização antes de ela vencer, até `coverageEndsAt`.\n- Merchant sem caução habilitada → 422 `DEPOSIT.NOT_ENABLED`.\n- Sem `payments:deposits:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDepositRequest'
            example:
              contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
              payer:
                name: Ana Souza
                document: '12345678909'
                email: ana@exemplo.com
              name: Caução Villa Trancoso
              amount: 500000
              currency: BRL
              coverageStartsAt: '2026-11-02T00:00:00Z'
              coverageEndsAt: '2026-12-05T00:00:00Z'
              autoRenew: true
      responses:
        '201':
          description: Caução aberta, aguardando a autorização do pagador.
          headers:
            Location:
              $ref: '#/components/headers/Location'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
              example:
                id: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
                status: AWAITING_AUTHORIZATION
                mode: PREAUTH_RENEWAL
                autoRenew: true
                contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                customerId: null
                name: Caução Villa Trancoso
                description: null
                amount: 500000
                currency: BRL
                coverageStartsAt: '2026-11-02T00:00:00Z'
                coverageEndsAt: '2026-12-05T00:00:00Z'
                guaranteeEndsAt: null
                checkoutUrl: https://pay.kodano.com.br/ord_0192f3c2d4e5f6a7b8c9d0e1f2a3b4c6
                card: null
                authorizationExpiresAt: null
                nextRenewalAttemptAt: null
                unsecuredReason: null
                feeAmount: null
                capturedAmount: null
                refundAmount: null
                damageDescription: null
                createdAt: '2026-10-28T15:05:00Z'
                updatedAt: '2026-10-28T15:05:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Mesma Idempotency-Key com outro corpo, ainda em execução, ou conflito de estado.
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                IDEMPOTENCY.CONFLICT:
                  $ref: '#/components/examples/IdempotencyConflict'
                IDEMPOTENCY.IN_PROGRESS:
                  $ref: '#/components/examples/IdempotencyInProgress'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/DepositUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    get:
      operationId: listDeposits
      tags:
      - Caução
      summary: Listar cauções
      description: "Cauções do merchant, da mais nova para a mais antiga, abertas pela API ou pelo portal.\n\n- Filtros: `status` e `contractId`. Valor fora do enum → 422 `VALIDATION.FAILED`.\n- Sem `payments:deposits:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:read
      parameters:
      - name: status
        in: query
        required: false
        schema:
          $ref: '#/components/schemas/DepositStatus'
      - name: contractId
        in: query
        required: false
        schema:
          type: string
        example: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Página de cauções.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepositList'
              example:
                data:
                - id: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
                  status: ACTIVE
                  mode: PREAUTH_RENEWAL
                  autoRenew: true
                  contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                  customerId: cus_0192f3c2aa11bb22cc33dd44ee55ff66
                  name: Caução Villa Trancoso
                  description: null
                  amount: 500000
                  currency: BRL
                  coverageStartsAt: '2026-11-02T00:00:00Z'
                  coverageEndsAt: '2026-12-05T00:00:00Z'
                  guaranteeEndsAt: '2026-11-07T15:20:00Z'
                  checkoutUrl: null
                  card:
                    brand: VISA
                    lastFour: '4242'
                  authorizationExpiresAt: '2026-11-07T15:20:00Z'
                  nextRenewalAttemptAt: '2026-11-07T10:20:00Z'
                  unsecuredReason: null
                  feeAmount: 20000
                  capturedAmount: null
                  refundAmount: null
                  damageDescription: null
                  createdAt: '2026-10-28T15:05:00Z'
                  updatedAt: '2026-10-28T15:05:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/deposits/{id}:
    parameters:
    - $ref: '#/components/parameters/DepositIdPath'
    get:
      operationId: getDeposit
      tags:
      - Caução
      summary: Consultar caução
      description: "Devolve a caução com a autorização vigente.\n\n- Inexistente, malformada ou de outro merchant → 404 `DEPOSIT.NOT_FOUND`.\n- Sem `payments:deposits:read` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Caução.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
              example:
                id: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
                status: ACTIVE
                mode: PREAUTH_RENEWAL
                autoRenew: true
                contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                customerId: cus_0192f3c2aa11bb22cc33dd44ee55ff66
                name: Caução Villa Trancoso
                description: null
                amount: 500000
                currency: BRL
                coverageStartsAt: '2026-11-02T00:00:00Z'
                coverageEndsAt: '2026-12-05T00:00:00Z'
                guaranteeEndsAt: '2026-11-07T15:20:00Z'
                checkoutUrl: null
                card:
                  brand: VISA
                  lastFour: '4242'
                authorizationExpiresAt: '2026-11-07T15:20:00Z'
                nextRenewalAttemptAt: '2026-11-07T10:20:00Z'
                unsecuredReason: null
                feeAmount: 20000
                capturedAmount: null
                refundAmount: null
                damageDescription: null
                createdAt: '2026-10-28T15:05:00Z'
                updatedAt: '2026-10-28T15:05:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/DepositNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/deposits/{id}/evidence-uploads:
    parameters:
    - $ref: '#/components/parameters/DepositIdPath'
    post:
      operationId: createDepositEvidenceUploads
      tags:
      - Caução
      summary: Preparar envio de evidências
      description: "Devolve URLs temporárias para enviar as fotos do dano por `PUT` antes da captura.\n\n- Até 20 arquivos por chamada, `image/png`, `image/jpeg`, `image/heic` ou `image/heif`.\n- Envie cada arquivo com `PUT` na `uploadUrl`, com o mesmo `Content-Type`, antes de `expiresAt`, e use os\n  `objectKey` em `POST /deposits/{id}/capture`.\n- Sem `payments:deposits:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepositEvidenceUploadsRequest'
            example:
              files:
              - fileName: sala.jpg
                contentType: image/jpeg
      responses:
        '200':
          description: URLs de envio.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepositEvidenceUploads'
              example:
                uploads:
                - fileName: sala.jpg
                  uploadUrl: https://kodano-deposits.s3.amazonaws.com/…
                  objectKey: deposits/dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a/evidence/7c1e-sala.jpg
                  expiresAt: '2026-12-05T13:15:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/DepositNotFound'
        '409':
          $ref: '#/components/responses/DepositConflict'
        '422':
          $ref: '#/components/responses/DepositUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/deposits/{id}/capture:
    parameters:
    - $ref: '#/components/parameters/DepositIdPath'
    post:
      operationId: captureDeposit
      tags:
      - Caução
      summary: Capturar caução
      description: "Cobra o dano do cartão do pagador e encerra a caução (`CAPTURED`).\n\n- Sem `amount`, captura o valor inteiro; com `amount` menor que o da caução, captura só esse valor e libera o resto.\n- `damageDescription` e ao menos uma evidência (`objectKey` de `evidence-uploads`) são obrigatórios.\n- Caução aguardando autorização → 409 `DEPOSIT.AWAITING_AUTHORIZATION`; sem autorização válida → 409\n  `DEPOSIT.NO_LIVE_AUTHORIZATION`; já encerrada → 409 `DEPOSIT.ALREADY_CLOSED`.\n- Repetir com a mesma chave devolve a caução capturada.\n- Sem `payments:deposits:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CaptureDepositRequest'
            example:
              amount: 120000
              damageDescription: Mesa de vidro da sala quebrada
              evidence:
              - objectKey: deposits/dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a/evidence/7c1e-sala.jpg
                fileName: sala.jpg
                contentType: image/jpeg
      responses:
        '200':
          description: Caução capturada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
              example:
                id: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
                status: CAPTURED
                mode: PREAUTH_RENEWAL
                autoRenew: true
                contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                customerId: cus_0192f3c2aa11bb22cc33dd44ee55ff66
                name: Caução Villa Trancoso
                description: null
                amount: 500000
                currency: BRL
                coverageStartsAt: '2026-11-02T00:00:00Z'
                coverageEndsAt: '2026-12-05T00:00:00Z'
                guaranteeEndsAt: '2026-11-07T15:20:00Z'
                checkoutUrl: null
                card:
                  brand: VISA
                  lastFour: '4242'
                authorizationExpiresAt: '2026-11-07T15:20:00Z'
                nextRenewalAttemptAt: null
                unsecuredReason: null
                feeAmount: 20000
                capturedAmount: 120000
                refundAmount: null
                damageDescription: Mesa de vidro da sala quebrada
                createdAt: '2026-10-28T15:05:00Z'
                updatedAt: '2026-10-28T15:05:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/DepositNotFound'
        '409':
          $ref: '#/components/responses/DepositConflict'
        '422':
          $ref: '#/components/responses/DepositUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/deposits/{id}/release:
    parameters:
    - $ref: '#/components/parameters/DepositIdPath'
    post:
      operationId: releaseDeposit
      tags:
      - Caução
      summary: Liberar caução
      description: "Libera a caução sem cobrança (`RELEASED`): a autorização no cartão é desfeita.\n\n- Já encerrada → 409 `DEPOSIT.ALREADY_CLOSED`.\n- Repetir com a mesma chave devolve a caução liberada.\n- Sem `payments:deposits:write` → 403 `AUTH.ENTITLEMENT_MISSING`."
      security:
      - kodanoOAuth:
        - payments:deposits:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Caução liberada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
              example:
                id: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
                status: RELEASED
                mode: PREAUTH_RENEWAL
                autoRenew: true
                contractId: ctr_0192f3c1a7b84e2d9f0a1b2c3d4e5f60
                customerId: cus_0192f3c2aa11bb22cc33dd44ee55ff66
                name: Caução Villa Trancoso
                description: null
                amount: 500000
                currency: BRL
                coverageStartsAt: '2026-11-02T00:00:00Z'
                coverageEndsAt: '2026-12-05T00:00:00Z'
                guaranteeEndsAt: '2026-11-07T15:20:00Z'
                checkoutUrl: null
                card:
                  brand: VISA
                  lastFour: '4242'
                authorizationExpiresAt: '2026-11-07T15:20:00Z'
                nextRenewalAttemptAt: null
                unsecuredReason: null
                feeAmount: 20000
                capturedAmount: null
                refundAmount: null
                damageDescription: null
                createdAt: '2026-10-28T15:05:00Z'
                updatedAt: '2026-10-28T15:05:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/DepositNotFound'
        '409':
          $ref: '#/components/responses/DepositConflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/quotes/preview:
    post:
      operationId: previewFxQuote
      tags:
      - Câmbio
      summary: Simular cotação
      description: 'Taxa **indicativa**, já com o spread da Kodano, e valores estimados, sem rail de recebimento e sem compromisso. Nada é gravado e não há `Idempotency-Key`.


        - Hoje: câmbio à vista (`SPOT`), de real para dólar (`BRL_TO_USD`).

        - `amountSide` diz de que lado está o `amount`: `PAYIN` (quanto o pagador paga em reais) ou `PAYOUT` (quanto chega em dólar).

        - Valor abaixo do mínimo → 422 `FX.AMOUNT_BELOW_MINIMUM`; acima do máximo → 422 `FX.AMOUNT_ABOVE_MAXIMUM`; acima do limite do merchant → 422 `FX.LIMIT_EXCEEDED` com `remaining`.

        - Merchant sem câmbio habilitado → 422 `FX.NOT_ENABLED`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:quotes:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxQuotePreviewRequest'
            example:
              product: SPOT
              direction: BRL_TO_USD
              amount:
                amount: 500000
                currency: BRL
              amountSide: PAYIN
      responses:
        '200':
          description: Simulação.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxQuotePreview'
              example:
                product: SPOT
                direction: BRL_TO_USD
                rate: '5.2392'
                payin:
                  amount: 500000
                  currency: BRL
                payoutEstimate:
                  amount: 95434
                  currency: USD
                indicative: true
                expiresAt: '2026-11-02T15:10:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/FxUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/quotes:
    post:
      operationId: createFxQuote
      tags:
      - Câmbio
      summary: Criar cotação
      description: 'Cotação com o rail de recebimento escolhido, a tarifa do rail e o total a pagar. É gravada e vale até `expiresAt`.


        - `indicative` é sempre `true` no câmbio à vista: o valor que vale é o da operação (`POST /v1/fx/operations`), que pode reprecificar. Não apresente a cotação como taxa garantida.

        - `payoutRail`: `ACH`, `WIRE`, `SWIFT` ou `SEPA`. Rail que a direção não aceita → 422 `FX.RAIL_NOT_SUPPORTED`.

        - A `rate` já inclui o spread da Kodano, definido nas taxas da loja ou, quando a loja não tem taxa própria, na taxa padrão da plataforma. Não há linha de spread à parte. A tarifa do rail (`COVER_FEE`) aparece em `fees`.

        - Merchant sem câmbio habilitado → 422 `FX.NOT_ENABLED`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:quotes:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFxQuoteRequest'
            example:
              product: SPOT
              direction: BRL_TO_USD
              amount:
                amount: 500000
                currency: BRL
              amountSide: PAYIN
              payoutRail: ACH
      responses:
        '201':
          description: Cotação criada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxQuote'
              example:
                id: fxq_0192f4a1c3d24e5f8a9b0c1d2e3f4a5b
                status: QUOTED
                product: SPOT
                direction: BRL_TO_USD
                payoutRail: ACH
                rate: '5.2392'
                payin:
                  amount: 500000
                  currency: BRL
                payout:
                  amount: 95434
                  currency: USD
                fees:
                - type: COVER_FEE
                  rail: ACH
                  amount:
                    amount: 262
                    currency: BRL
                  reference:
                    amount: 50
                    currency: USD
                totalPayin:
                  amount: 500262
                  currency: BRL
                indicative: true
                expiresAt: '2026-11-02T15:10:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/FxConflict'
        '422':
          $ref: '#/components/responses/FxUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/operations:
    post:
      operationId: createFxOperation
      tags:
      - Câmbio
      summary: Criar operação de câmbio
      description: 'Cria a operação a partir de uma cotação e de uma conta de destino, e devolve o Pix que o pagador deve pagar.


        - Os valores da resposta são os **vinculantes**: podem diferir da cotação (`repricedFromQuote: true`). Mostre-os ao pagador antes de ele pagar.

        - `paymentInstructions.pixCopyPaste` é o Pix copia e cola; gere o QR Code no seu lado. O pagamento vence em `paymentInstructions.expiresAt`.

        - `authorization.accepted` precisa ser `true` e `termsVersion` precisa ser a versão vigente dos termos do câmbio à vista (`global-spot-2026-10`). Sua aplicação responde por colher o aceite de quem autoriza a transferência.

        - Resultado ambíguo na Kodano → **202** com `status: UNKNOWN`. Consulte `GET /v1/fx/operations/{id}`; nunca repita o POST com outra `Idempotency-Key`.

        - Cotação vencida → 410 `FX.QUOTE_EXPIRED`; conta de destino inativa ou incompatível com o rail → 422 `FX.PAYOUT_ACCOUNT_INVALID`.

        - Repetir a mesma chave com o mesmo corpo devolve a resposta original (`X-Idempotent-Replay: true`).

        - Merchant sem câmbio habilitado → 422 `FX.NOT_ENABLED`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:operations:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFxOperationRequest'
            example:
              quoteId: fxq_0192f4a1c3d24e5f8a9b0c1d2e3f4a5b
              payoutAccountId: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
              customerReference: pedido-8841
              authorization:
                accepted: true
                termsVersion: global-spot-2026-10
      responses:
        '201':
          description: Operação criada, aguardando o pagamento do Pix.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxOperation'
              example:
                id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                type: SPOT
                status: AWAITING_PAYMENT
                direction: BRL_TO_USD
                rate: '5.2400'
                payin:
                  amount: 500260
                  currency: BRL
                payout:
                  amount: 95412
                  currency: USD
                fees:
                - type: COVER_FEE
                  rail: ACH
                  amount:
                    amount: 262
                    currency: BRL
                  reference:
                    amount: 50
                    currency: USD
                repricedFromQuote: true
                payoutAccount:
                  id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                  label: Fornecedor EUA · ACH · ••••0288
                  rail: ACH
                customerReference: pedido-8841
                paymentInstructions:
                  rail: PIX
                  pixCopyPaste: 00020126580014br.gov.bcb.pix0136...
                  amount:
                    amount: 500260
                    currency: BRL
                  expiresAt: '2026-11-02T15:05:30Z'
                timeline:
                - status: AWAITING_PAYMENT
                  at: '2026-11-02T15:00:30Z'
                supportReference: GA-7K2M9Q
                receiptAvailable: false
                createdAt: '2026-11-02T15:00:30Z'
                updatedAt: '2026-11-02T15:00:30Z'
        '202':
          description: A Kodano não confirmou a criação; consulte a operação antes de qualquer nova tentativa.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxOperation'
              example:
                id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                type: SPOT
                status: UNKNOWN
                direction: BRL_TO_USD
                rate: null
                payin:
                  amount: 500260
                  currency: BRL
                payout:
                  amount: 95412
                  currency: USD
                fees:
                - type: COVER_FEE
                  rail: ACH
                  amount:
                    amount: 262
                    currency: BRL
                  reference:
                    amount: 50
                    currency: USD
                repricedFromQuote: true
                payoutAccount: null
                customerReference: pedido-8841
                paymentInstructions: null
                timeline: []
                supportReference: GA-7K2M9Q
                receiptAvailable: false
                createdAt: '2026-11-02T15:00:30Z'
                updatedAt: '2026-11-02T15:00:30Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '409':
          $ref: '#/components/responses/FxConflict'
        '410':
          $ref: '#/components/responses/FxGone'
        '422':
          $ref: '#/components/responses/FxUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    get:
      operationId: listFxOperations
      tags:
      - Câmbio
      summary: Listar operações de câmbio
      description: 'Operações do merchant, da mais nova para a mais antiga.


        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:operations:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      - name: status
        in: query
        required: false
        description: Filtra por status; repita o parâmetro para mais de um.
        schema:
          type: array
          items:
            $ref: '#/components/schemas/FxOperationStatus'
        style: form
        explode: true
      - name: direction
        in: query
        required: false
        description: Filtra pela direção.
        schema:
          $ref: '#/components/schemas/FxDirection'
      - name: createdFrom
        in: query
        required: false
        description: Início inclusivo `[from, to)`.
        schema:
          type: string
          format: date-time
      - name: createdTo
        in: query
        required: false
        description: Fim exclusivo; `from > to` → `VALIDATION.RANGE`.
        schema:
          type: string
          format: date-time
      - name: customerReference
        in: query
        required: false
        description: Referência exata informada na criação.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: Página de operações.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxOperationList'
              example:
                data:
                - id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: SETTLED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  customerReference: pedido-8841
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/operations/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da operação, fxo_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 FX.OPERATION_NOT_FOUND.
      schema:
        type: string
      example: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
    get:
      operationId: getFxOperation
      tags:
      - Câmbio
      summary: Consultar operação de câmbio
      description: 'Detalhe, linha do tempo e instruções de pagamento ativas.


        - Acompanhe a operação por polling neste endpoint (a cada 5 a 10 s enquanto estiver em andamento): o desfecho final é `SETTLED`, `EXPIRED`, `FAILED` ou `REFUNDED`.

        - `ACTION_REQUIRED`: o pagamento foi recebido mas o envio não se completou; a Kodano abre a devolução e acompanha com você pelo `supportReference`.

        - `supportReference` é o código curto para falar com o suporte da Kodano.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:operations:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Operação.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxOperation'
              example:
                id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                type: SPOT
                status: SETTLED
                direction: BRL_TO_USD
                rate: '5.2400'
                payin:
                  amount: 500260
                  currency: BRL
                payout:
                  amount: 95412
                  currency: USD
                fees:
                - type: COVER_FEE
                  rail: ACH
                  amount:
                    amount: 262
                    currency: BRL
                  reference:
                    amount: 50
                    currency: USD
                repricedFromQuote: true
                payoutAccount:
                  id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                  label: Fornecedor EUA · ACH · ••••0288
                  rail: ACH
                customerReference: pedido-8841
                paymentInstructions: null
                timeline:
                - status: AWAITING_PAYMENT
                  at: '2026-11-02T15:00:30Z'
                - status: PAYMENT_RECEIVED
                  at: '2026-11-02T15:02:10Z'
                - status: CONVERTING
                  at: '2026-11-02T15:02:40Z'
                - status: PAYOUT_IN_PROGRESS
                  at: '2026-11-02T15:03:00Z'
                - status: SETTLED
                  at: '2026-11-03T14:00:00Z'
                supportReference: GA-7K2M9Q
                receiptAvailable: true
                createdAt: '2026-11-02T15:00:30Z'
                updatedAt: '2026-11-03T14:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/operations/{id}/receipt:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da operação, fxo_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 FX.OPERATION_NOT_FOUND.
      schema:
        type: string
      example: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
    get:
      operationId: getFxOperationReceipt
      tags:
      - Câmbio
      summary: Obter comprovante da operação
      description: 'URL assinada do comprovante em PDF, emitido pela Kodano. Disponível em `SETTLED` e `REFUNDED`; a URL vale por pouco tempo, peça outra quando precisar.


        - Antes disso → 409 `FX.RECEIPT_NOT_AVAILABLE`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:operations:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Comprovante.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxReceipt'
              example:
                url: https://receipts.kodano.com.br/fx/0192f4a2…pdf?X-Sig=…
                expiresAt: '2026-11-03T14:10:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '409':
          $ref: '#/components/responses/FxConflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/payout-accounts:
    post:
      operationId: createFxPayoutAccount
      tags:
      - Contas de câmbio
      summary: Cadastrar conta de destino
      description: 'Cadastra a conta que recebe o dólar. A Kodano valida os dados antes de enviar (ABA com 9 dígitos, BIC com 8 ou 11, IBAN com dígito verificador).


        - Informe os blocos do rail escolhido: `bank` (ACH e WIRE), `swift` com `beneficiaryAddress` e `bankAddress` (SWIFT) ou `sepa` (SEPA).

        - A conta nasce `ACTIVE`. SWIFT para terceiro nasce `PENDING_DOCUMENT` até você enviar o documento da operação em `POST /v1/fx/payout-accounts/{id}/documents`.

        - Dado recusado → 422 `FX.PAYOUT_ACCOUNT_VALIDATION` com `errors[]` por campo. Rail indisponível → 422 `FX.RAIL_NOT_AVAILABLE`.

        - A resposta devolve a conta mascarada: o número completo nunca volta.

        - Merchant sem câmbio habilitado → 422 `FX.NOT_ENABLED`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:payout-accounts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFxPayoutAccountRequest'
            example:
              rail: ACH
              displayName: Fornecedor EUA
              holder:
                name: Global Trade LLC
                accountClass: BUSINESS
                relationship: VENDOR_OR_SUPPLIER
                businessIndustryNaics: '522320'
              bank:
                name: Chase
                routingNumber: '021000021'
                accountNumber: 000123456789
                accountType: CHECKING
              address:
                line1: 100 Main St
                city: New York
                stateProvinceRegion: NY
                country: US
                postalCode: '10001'
      responses:
        '201':
          description: Conta cadastrada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
            X-Idempotent-Replay:
              $ref: '#/components/headers/X-Idempotent-Replay'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxPayoutAccount'
              example:
                id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                displayName: Fornecedor EUA
                rail: ACH
                currency: USD
                holderName: Global Trade LLC
                bankName: Chase
                maskedAccount: ••••0288
                label: Fornecedor EUA · ACH · ••••0288
                status: ACTIVE
                createdAt: '2026-11-02T14:50:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/FxConflict'
        '422':
          $ref: '#/components/responses/FxUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    get:
      operationId: listFxPayoutAccounts
      tags:
      - Contas de câmbio
      summary: Listar contas de destino
      description: 'Contas de destino do merchant. Sem `status`, só as `ACTIVE`.


        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:payout-accounts:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Cursor'
      - name: rail
        in: query
        required: false
        description: Filtra pelo rail.
        schema:
          $ref: '#/components/schemas/FxRail'
      - name: status
        in: query
        required: false
        description: Padrão `ACTIVE`.
        schema:
          $ref: '#/components/schemas/FxPayoutAccountStatus'
      responses:
        '200':
          description: Página de contas.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxPayoutAccountList'
              example:
                data:
                - id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                  displayName: Fornecedor EUA
                  rail: ACH
                  currency: USD
                  holderName: Global Trade LLC
                  bankName: Chase
                  maskedAccount: ••••0288
                  label: Fornecedor EUA · ACH · ••••0288
                  status: ACTIVE
                  createdAt: '2026-11-02T14:50:00Z'
                hasMore: false
                nextCursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/payout-accounts/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da conta de destino, fxa_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 FX.PAYOUT_ACCOUNT_NOT_FOUND.
      schema:
        type: string
      example: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
    get:
      operationId: getFxPayoutAccount
      tags:
      - Contas de câmbio
      summary: Consultar conta de destino
      description: 'Conta mascarada: do número da conta só os últimos 4 dígitos.


        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:payout-accounts:read
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Conta.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxPayoutAccount'
              example:
                id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                displayName: Fornecedor EUA
                rail: ACH
                currency: USD
                holderName: Global Trade LLC
                bankName: Chase
                maskedAccount: ••••0288
                label: Fornecedor EUA · ACH · ••••0288
                status: ACTIVE
                createdAt: '2026-11-02T14:50:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
    delete:
      operationId: deleteFxPayoutAccount
      tags:
      - Contas de câmbio
      summary: Desativar conta de destino
      description: 'Desativa a conta do lado da Kodano: ela some das listas e não serve para operação nova. Operações em andamento não são afetadas. Repetir a chamada em conta já desativada também responde 204.


        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:payout-accounts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      responses:
        '204':
          description: Conta desativada.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /v1/fx/payout-accounts/{id}/documents:
    parameters:
    - name: id
      in: path
      required: true
      description: Id da conta de destino, fxa_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 FX.PAYOUT_ACCOUNT_NOT_FOUND.
      schema:
        type: string
      example: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
    post:
      operationId: uploadFxPayoutAccountDocument
      tags:
      - Contas de câmbio
      summary: Enviar documento da conta de destino
      description: 'Anexa o documento da operação a uma conta `PENDING_DOCUMENT` (SWIFT para terceiro). Aceita PDF, JPEG ou PNG de até 8 MB; o corpo inteiro, com os campos, cabe no limite de 10 MB do gateway.


        - `type`: `INVOICE`, `PURCHASE_ORDER`, `DELIVERY_SLIP`, `CONTRACT`, `CUSTOMS_DECLARATION`, `BILL_OF_LADING` ou `OTHER`.

        - Aceito o documento, a conta passa a `ACTIVE`.

        - Merchant sem câmbio habilitado → 422 `FX.NOT_ENABLED`.

        - Sem o escopo exigido → 403 `AUTH.ENTITLEMENT_MISSING`.'
      security:
      - kodanoOAuth:
        - fx:payout-accounts:write
      parameters:
      - $ref: '#/components/parameters/MerchantIdRequired'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        description: Arquivo e tipo do documento.
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/FxPayoutAccountDocumentUpload'
      responses:
        '200':
          description: Documento anexado.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/X-Correlation-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FxPayoutAccount'
              example:
                id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                displayName: Fornecedor EUA
                rail: SWIFT
                currency: USD
                holderName: Global Trade LLC
                bankName: Chase
                maskedAccount: ••••3000
                label: Supplier GmbH · SWIFT · ••••3000
                status: ACTIVE
                createdAt: '2026-11-02T14:50:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/FxNotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '422':
          $ref: '#/components/responses/FxUnprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
webhooks:
  payment.succeeded:
    post:
      operationId: onPaymentSucceeded
      summary: Pagamento concluído
      description: |-
        PIX recebido, boleto liquidado, venda de cartão aprovada (`SUCCEEDED`) ou pré-autorização capturada (`CAPTURED`). Boleto só emite na liquidação, não no pagamento.

        Escopo de leitura exigido do cliente dono do endpoint: um de `payments:pix:read`, `payments:boleto:read`, `payments:cards:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentSucceededEvent'
            example:
              id: evt_NAReVGpteI9IpjpimY69TtHrjIyl5ykV
              type: payment.succeeded
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T13:02:11Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: pay_Nj7ZJCZ9jM7SGGmnjGl9UAXLW7BpYwCL
                  status: SUCCEEDED
                  method: PIX
                  amount: 15990
                  currency: BRL
                  description: Pedido 123
                  customer:
                    id: cus_Lbfbv3xA5PQh8H5jKlF7yXqRNJxlSZXf
                    name: Ana Souza
                    document: 123******09
                  pix:
                    qrCode: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                    copyPaste: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                    expiresAt: '2026-10-16T14:00:00Z'
                    endToEndId: E18236120202610161302s0a1b2c3d4e
                  failure: null
                  statusReason: null
                  refundedAmount: 0
                  refundedAt: null
                  paymentLinkId: null
                  metadata:
                    orderRef: '123'
                  createdAt: '2026-10-16T13:00:00Z'
                  updatedAt: '2026-10-16T13:02:10Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  payment.failed:
    post:
      operationId: onPaymentFailed
      summary: Pagamento falhou
      description: |-
        Pagamento vai a `FAILED`. Só `failure.code: PAYMENT.PROVIDER_ERROR` não é final: a reconciliação pode gerar `payment.succeeded` depois.

        Escopo de leitura exigido do cliente dono do endpoint: um de `payments:pix:read`, `payments:boleto:read`, `payments:cards:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentFailedEvent'
            example:
              id: evt_<id do evento>
              type: payment.failed
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T13:15:43Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: pay_98viJDJcYyoItylIdKY4Jxa8JNQtNSfa
                  status: FAILED
                  method: CARD
                  amount: 25000
                  currency: BRL
                  description: Assinatura anual
                  customer:
                    id: cus_Lbfbv3xA5PQh8H5jKlF7yXqRNJxlSZXf
                    name: Ana Souza
                    document: 123******09
                  card:
                    id: card_r2Cls2kEBglFQCeCxEZDpiQEyVpSBhad
                    brand: VISA
                    last4: '4242'
                    installments: 3
                    capturedAmount: null
                    capturedAt: null
                  failure:
                    code: PAYMENT.DECLINED
                    message: Payment declined by the issuer
                  statusReason: null
                  refundedAmount: 0
                  refundedAt: null
                  paymentLinkId: null
                  metadata: {}
                  createdAt: '2026-10-16T13:15:40Z'
                  updatedAt: '2026-10-16T13:15:42Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  payment.refunded:
    post:
      operationId: onPaymentRefunded
      summary: Estorno concluído
      description: |-
        Estorno concluído; `data.object` = pagamento, `data.refund` = estorno. O ajuste de captura parcial (`CAPTURE_ADJUSTMENT`) não gera o evento.

        Escopo de leitura exigido do cliente dono do endpoint: um de `payments:pix:read`, `payments:boleto:read`, `payments:cards:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentRefundedEvent'
            example:
              id: evt_eLAeXfIkpXkuN51gMG1J3aYxprFLfIAo
              type: payment.refunded
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T18:30:02Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: pay_Nj7ZJCZ9jM7SGGmnjGl9UAXLW7BpYwCL
                  status: PARTIALLY_REFUNDED
                  method: PIX
                  amount: 15990
                  currency: BRL
                  description: Pedido 123
                  customer:
                    id: cus_Lbfbv3xA5PQh8H5jKlF7yXqRNJxlSZXf
                    name: Ana Souza
                    document: 123******09
                  pix:
                    qrCode: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                    copyPaste: 00020101021226840014br.gov.bcb.pix2562pix.exemplo.com.br/qr/v2/cobv/5c1b2a3d4e5f60718293a4b5c6d7e8f95204000053039865802BR5911ACME LTDA6009SAO PAULO62070503***6304A1B2
                    expiresAt: '2026-10-16T14:00:00Z'
                    endToEndId: E18236120202610161302s0a1b2c3d4e
                  failure: null
                  statusReason: null
                  refundedAmount: 5000
                  refundedAt: '2026-10-16T18:30:00Z'
                  paymentLinkId: null
                  metadata:
                    orderRef: '123'
                  createdAt: '2026-10-16T13:00:00Z'
                  updatedAt: '2026-10-16T18:30:00Z'
                refund:
                  id: ref_EN9MqxQZIpd3nf12U7pIXifnAXOs7j69
                  amount: 5000
                  currency: BRL
                  status: SUCCEEDED
                  reason: null
                  failure: null
                  createdAt: '2026-10-16T18:29:48Z'
                  updatedAt: '2026-10-16T18:30:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  chargeback.received:
    post:
      operationId: onChargebackReceived
      summary: Chargeback recebido
      description: |-
        Chargeback recebido; `data.object` = Chargeback (schema próprio; não há `GET` de chargeback na v1). `reason` e `defenseDeadline` podem ser `null`.

        Escopo de leitura exigido do cliente dono do endpoint: um de `payments:pix:read`, `payments:boleto:read`, `payments:cards:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChargebackReceivedEvent'
            example:
              id: evt_<id do evento>
              type: chargeback.received
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T15:20:01Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: cbk_8a4b378aaabd0986b4ef3d6f3f04da15
                  paymentId: pay_K3vT9mQ2xL7wR4nB8cY1zD6fH0jS5pGa
                  amount: 25000
                  currency: BRL
                  reason: Transação não reconhecida pelo portador
                  status: RECEIVED
                  defenseDeadline: '2026-10-30T23:59:59Z'
                  receivedAt: '2026-10-16T15:20:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  withdrawal.completed:
    post:
      operationId: onWithdrawalCompleted
      summary: Saque concluído
      description: |-
        Saque concluído. Transições não monotônicas: pode chegar depois de `withdrawal.failed`.

        Escopo de leitura exigido do cliente dono do endpoint: `payouts:withdrawals:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WithdrawalCompletedEvent'
            example:
              id: evt_<id do evento>
              type: withdrawal.completed
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: wdr_c47e1472263776f52b6b5790fffea987
                  status: COMPLETED
                  amount: 150000
                  currency: BRL
                  fee: 150
                  totalAmount: 150150
                  netAmount: 150000
                  method: PIX_KEY
                  destination:
                    type: PIX_KEY
                    id: pxk_c8b1b513cb57da910b2a6787a2d5f8c3
                  failure: null
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  withdrawal.failed:
    post:
      operationId: onWithdrawalFailed
      summary: Saque falhou
      description: |-
        Saque falhou, foi rejeitado ou cancelado; `failure.code` `WITHDRAWAL.PAYOUT_FAILED`, `WITHDRAWAL.REJECTED` ou `WITHDRAWAL.CANCELED`.

        Escopo de leitura exigido do cliente dono do endpoint: `payouts:withdrawals:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WithdrawalFailedEvent'
            example:
              id: evt_<id do evento>
              type: withdrawal.failed
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: wdr_c47e1472263776f52b6b5790fffea987
                  status: FAILED
                  amount: 150000
                  currency: BRL
                  fee: 150
                  totalAmount: 150150
                  netAmount: 150000
                  method: PIX_KEY
                  destination:
                    type: PIX_KEY
                    id: pxk_c8b1b513cb57da910b2a6787a2d5f8c3
                  failure:
                    code: WITHDRAWAL.PAYOUT_FAILED
                    message: Payout rejected by the receiving institution
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  settlement.closed:
    post:
      operationId: onSettlementClosed
      summary: Liquidação fechada
      description: |-
        Lote do dia inteiro `SETTLED`, uma vez por lote; `data.object` = Settlement sem `items` (abre em `GET /v1/settlements/{id}`). Linha que chega depois do fechamento não gera novo evento.

        Escopo de leitura exigido do cliente dono do endpoint: `receivables:settlements:read`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SettlementClosedEvent'
            example:
              id: evt_<id do evento>
              type: settlement.closed
              apiVersion: '2026-10-01'
              createdAt: '2026-10-16T06:00:00Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: stl_2026101501a33e1fc42bb5898fe70dbb
                  status: SETTLED
                  settlementDate: '2026-10-15'
                  currency: BRL
                  transactionCount: 4
                  grossAmount: 99000
                  feesAmount: 2871
                  netAmount: 96129
                  settledAt: '2026-10-15T00:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  webhook_endpoint.disabled:
    post:
      operationId: onWebhookEndpointDisabled
      summary: Webhook endpoint auto-desativado
      description: |-
        Auto-desativação (`AUTO_FAILURES`); vai aos outros endpoints `ACTIVE` que assinam o tipo e alcançam o escopo do endpoint desativado. Desativação manual (PATCH) não gera o evento.

        Escopo de leitura exigido do cliente dono do endpoint: só `platform.core`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointDisabledEvent'
            example:
              id: evt_<id do evento>
              type: webhook_endpoint.disabled
              apiVersion: '2026-10-01'
              createdAt: '2026-10-17T14:10:32Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: whe_zQ7EUbzgd8SsjEVUqldLn3OYxujoQHxr
                  url: https://hooks.parceiro.com.br/kodano
                  description: ERP financeiro
                  events:
                  - payment.succeeded
                  - payment.failed
                  - payment.refunded
                  status: DISABLED
                  scope: MERCHANT
                  merchantId: mrc_e3cc4727054da558273162251e310d29
                  failureCount: 23
                  lastSuccessAt: '2026-10-16T13:02:12Z'
                  lastFailureAt: '2026-10-17T14:10:31Z'
                  disabledAt: '2026-10-17T14:10:31Z'
                  disabledReason: AUTO_FAILURES
                  previousSecretExpiresAt: null
                  createdAt: '2026-10-16T12:40:00Z'
                  updatedAt: '2026-10-17T14:10:31Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  ping:
    post:
      operationId: onPing
      summary: Ping de teste
      description: |-
        Só sai como entrega `TEST` de `POST /v1/webhook-endpoints/{id}/test`, assinado com o segredo atual; não é assinável. O `data` não está definido no TDD; o exemplo é o vetor de teste de assinatura do TDD (segredo `whsec_test_secret`, `t=1760486400` → `v1=677603e905a53a5f17864abe50e46533c38c24e08644c3d0fe78ec7bdad22700`).

        Escopo de leitura exigido do cliente dono do endpoint: só `platform.core`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PingEvent'
            example:
              id: evt_test
              type: ping
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  fx.operation.settled:
    post:
      operationId: onFxOperationSettled
      summary: Câmbio liquidado
      description: |-
        Operação liquidada (`SETTLED`): o dólar foi entregue na conta de destino e o comprovante está disponível.

        Escopo de leitura exigido do cliente dono do endpoint: `fx:operations:read`. `data.object` tem o formato do `GET /v1/fx/operations/{id}`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxOperationSettledEvent'
            example:
              id: evt_<id do evento>
              type: fx.operation.settled
              apiVersion: '2026-10-01'
              createdAt: '2026-11-03T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: SETTLED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  fees:
                  - type: COVER_FEE
                    rail: ACH
                    amount:
                      amount: 262
                      currency: BRL
                    reference:
                      amount: 50
                      currency: USD
                  repricedFromQuote: true
                  payoutAccount:
                    id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                    label: Fornecedor EUA · ACH · ••••0288
                    rail: ACH
                  customerReference: pedido-8841
                  paymentInstructions: null
                  timeline:
                  - status: AWAITING_PAYMENT
                    at: '2026-11-02T15:00:30Z'
                  - status: SETTLED
                    at: '2026-11-03T14:00:00Z'
                  supportReference: GA-7K2M9Q
                  receiptAvailable: true
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  fx.operation.expired:
    post:
      operationId: onFxOperationExpired
      summary: Câmbio expirou
      description: |-
        Operação expirada (`EXPIRED`): o Pix não foi pago no prazo. Crie outra operação a partir de uma cotação nova.

        Escopo de leitura exigido do cliente dono do endpoint: `fx:operations:read`. `data.object` tem o formato do `GET /v1/fx/operations/{id}`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxOperationExpiredEvent'
            example:
              id: evt_<id do evento>
              type: fx.operation.expired
              apiVersion: '2026-10-01'
              createdAt: '2026-11-03T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: EXPIRED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  fees:
                  - type: COVER_FEE
                    rail: ACH
                    amount:
                      amount: 262
                      currency: BRL
                    reference:
                      amount: 50
                      currency: USD
                  repricedFromQuote: true
                  payoutAccount:
                    id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                    label: Fornecedor EUA · ACH · ••••0288
                    rail: ACH
                  customerReference: pedido-8841
                  paymentInstructions: null
                  timeline:
                  - status: AWAITING_PAYMENT
                    at: '2026-11-02T15:00:30Z'
                  - status: EXPIRED
                    at: '2026-11-03T14:00:00Z'
                  supportReference: GA-7K2M9Q
                  receiptAvailable: false
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  fx.operation.failed:
    post:
      operationId: onFxOperationFailed
      summary: Câmbio falhou
      description: |-
        Operação falhou (`FAILED`) sem dinheiro recebido.

        Escopo de leitura exigido do cliente dono do endpoint: `fx:operations:read`. `data.object` tem o formato do `GET /v1/fx/operations/{id}`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxOperationFailedEvent'
            example:
              id: evt_<id do evento>
              type: fx.operation.failed
              apiVersion: '2026-10-01'
              createdAt: '2026-11-03T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: FAILED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  fees:
                  - type: COVER_FEE
                    rail: ACH
                    amount:
                      amount: 262
                      currency: BRL
                    reference:
                      amount: 50
                      currency: USD
                  repricedFromQuote: true
                  payoutAccount:
                    id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                    label: Fornecedor EUA · ACH · ••••0288
                    rail: ACH
                  customerReference: pedido-8841
                  paymentInstructions: null
                  timeline:
                  - status: AWAITING_PAYMENT
                    at: '2026-11-02T15:00:30Z'
                  - status: FAILED
                    at: '2026-11-03T14:00:00Z'
                  supportReference: GA-7K2M9Q
                  receiptAvailable: false
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  fx.operation.action_required:
    post:
      operationId: onFxOperationActionRequired
      summary: Câmbio requer ação
      description: |-
        Pix recebido sem entrega do dólar (`ACTION_REQUIRED`): a Kodano abre a devolução. Acompanhe `REFUND_PENDING` e `REFUNDED` por `fx.operation.refunded` ou pelo `GET`. Use o `supportReference` para falar com o suporte.

        Escopo de leitura exigido do cliente dono do endpoint: `fx:operations:read`. `data.object` tem o formato do `GET /v1/fx/operations/{id}`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxOperationActionRequiredEvent'
            example:
              id: evt_<id do evento>
              type: fx.operation.action_required
              apiVersion: '2026-10-01'
              createdAt: '2026-11-03T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: ACTION_REQUIRED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  fees:
                  - type: COVER_FEE
                    rail: ACH
                    amount:
                      amount: 262
                      currency: BRL
                    reference:
                      amount: 50
                      currency: USD
                  repricedFromQuote: true
                  payoutAccount:
                    id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                    label: Fornecedor EUA · ACH · ••••0288
                    rail: ACH
                  customerReference: pedido-8841
                  paymentInstructions: null
                  timeline:
                  - status: AWAITING_PAYMENT
                    at: '2026-11-02T15:00:30Z'
                  - status: ACTION_REQUIRED
                    at: '2026-11-03T14:00:00Z'
                  supportReference: GA-7K2M9Q
                  receiptAvailable: false
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
  fx.operation.refunded:
    post:
      operationId: onFxOperationRefunded
      summary: Câmbio devolvido
      description: |-
        Valor pago devolvido ao pagador (`REFUNDED`); o comprovante da devolução está disponível.

        Escopo de leitura exigido do cliente dono do endpoint: `fx:operations:read`. `data.object` tem o formato do `GET /v1/fx/operations/{id}`.

        Entrega: `POST` na `url` do endpoint com `Content-Type: application/json; charset=utf-8`, corpo de até 256 KB, TLS 1.2+, sem seguir redirect, IP validado contra SSRF a cada tentativa. Verificar a assinatura sobre os bytes crus (nunca sobre JSON re-serializado). Entrega duplicada é esperada e a ordem não é garantida: deduplicar por `id`.
      tags:
      - Webhooks
      security: []
      parameters:
      - name: X-Kodano-Signature
        in: header
        required: true
        schema:
          type: string
        description: '`t=<unix em segundos>,v1=<hex>`; durante a rotação, `t=<unix>,v1=<novo>,v1=<anterior>`. `v1` = hex minúsculo de HMAC-SHA256(chave = bytes UTF-8 do segredo inteiro, inclusive `whsec_`; mensagem = `"{t}."` + bytes crus do corpo). Rejeitar `|agora − t| > 300 s`, comparar em tempo constante e aceitar se qualquer `v1` conferir.'
        example: t=1792155732,v1=d3e134866c73f1ace702a6dab8e1d8225c471951a3f88f395c987bc441b22962
      - name: X-Kodano-Event-Id
        in: header
        required: true
        schema:
          type: string
          pattern: ^evt_
        description: Igual ao `id` do corpo; chave de deduplicação.
      - name: X-Kodano-Event-Type
        in: header
        required: true
        schema:
          $ref: '#/components/schemas/EventType'
        description: Igual ao `type` do corpo.
      - name: User-Agent
        in: header
        required: true
        schema:
          type: string
          const: Kodano-Webhooks/1.0
        description: '`Kodano-Webhooks/1.0`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FxOperationRefundedEvent'
            example:
              id: evt_<id do evento>
              type: fx.operation.refunded
              apiVersion: '2026-10-01'
              createdAt: '2026-11-03T14:00:05Z'
              environment: sandbox
              merchantId: mrc_e3cc4727054da558273162251e310d29
              data:
                object:
                  id: fxo_0192f4a2d4e35f6a9b0c1d2e3f4a5b6c
                  type: SPOT
                  status: REFUNDED
                  direction: BRL_TO_USD
                  rate: '5.2400'
                  payin:
                    amount: 500260
                    currency: BRL
                  payout:
                    amount: 95412
                    currency: USD
                  fees:
                  - type: COVER_FEE
                    rail: ACH
                    amount:
                      amount: 262
                      currency: BRL
                    reference:
                      amount: 50
                      currency: USD
                  repricedFromQuote: true
                  payoutAccount:
                    id: fxa_0192f4a0b2c14d3e7f8a9b0c1d2e3f4a
                    label: Fornecedor EUA · ACH · ••••0288
                    rail: ACH
                  customerReference: pedido-8841
                  paymentInstructions: null
                  timeline:
                  - status: AWAITING_PAYMENT
                    at: '2026-11-02T15:00:30Z'
                  - status: REFUNDED
                    at: '2026-11-03T14:00:00Z'
                  supportReference: GA-7K2M9Q
                  receiptAvailable: true
                  createdAt: '2026-11-02T15:00:30Z'
                  updatedAt: '2026-11-03T14:00:00Z'
      responses:
        2XX:
          description: Recebido. Sucesso é qualquer 2xx em até 10 s (DNS, TLS e resposta inclusos); o corpo da resposta é ignorado.
        default:
          description: 'Qualquer outra resposta, 3xx (redirect não é seguido), timeout ou erro de conexão conta como falha: retry exponencial com jitter por 24 h (8 tentativas), depois DLQ; com `failureCount` ≥ 20 e 24 h de falhas, auto-desativação e `webhook_endpoint.disabled`.'
components:
  securitySchemes:
    kodanoOAuth:
      type: oauth2
      description: |-
        Token obtido no Zitadel do ambiente (`POST /oauth/v2/token`), fora do host da API, e enviado em `Authorization: Bearer <access_token>`.

        - Sandbox: `https://kodano-uat-p55zt6.us1.zitadel.cloud/oauth/v2/token`
        - Produção: `https://kodano-prod-qe8hbt.us1.zitadel.cloud/oauth/v2/token`

        Use sempre o `tokenUrl` devolvido na emissão da credencial. Duas variantes: `private_key_jwt` (preferida; grant `urn:ietf:params:oauth:grant-type:jwt-bearer` com `assertion` RS256 assinada pela chave da credencial, `iss` = `sub` = `userId`, `aud` = authority do Zitadel, `kid` = `keyId`) e `client_secret` (grant `client_credentials`). O OpenAPI só modela a segunda.

        O parâmetro `scope` do token é OIDC, não os escopos da API: `openid urn:zitadel:iam:org:project:id:<projectId da API>:aud urn:zitadel:iam:user:resourceowner`. Sem o `:aud` a API responde 401 `AUTH.TOKEN_INVALID`; sem `resourceowner`, 403 `AUTH.TENANT_MISMATCH`.

        Os 28 escopos abaixo são os entitlements concedidos à credencial pelos produtos de integração (`produto:recurso:ação`; `write` não implica `read`), avaliados a cada requisição; não são pedidos no token. O tenant vem da organização do token; o merchant, de `X-Merchant-Id`. Revogar a credencial nega os tokens já emitidos em até 60 s.
      flows:
        clientCredentials:
          tokenUrl: https://kodano-prod-qe8hbt.us1.zitadel.cloud/oauth/v2/token
          scopes:
            events:events:read: Ler eventos (`GET /v1/events*`). Produto `platform.core`.
            webhooks:endpoints:read: Ler webhook endpoints e entregas. Produto `platform.core`.
            webhooks:endpoints:write: Criar, alterar, excluir, testar e rotacionar webhook endpoints; reentregar eventos. Produto `platform.core`.
            payments:pix:read: Ler pagamentos PIX. Produto `payments.pix`.
            payments:pix:write: Criar cobranças PIX; estornar PIX. Produto `payments.pix`.
            payments:boleto:read: Ler pagamentos de boleto. Produto `payments.boleto`.
            payments:boleto:write: Emitir boletos. Produto `payments.boleto`.
            payments:cards:read: Ler pagamentos de cartão. Produto `payments.cards`.
            payments:cards:write: Cobrar, capturar, estornar e cancelar cartão. Produto `payments.cards`.
            payments:vault:read: Ler cartões do cofre. Produto `payments.cards`.
            payments:vault:write: Salvar e inativar cartões no cofre; `card.store` na cobrança. Produto `payments.cards`.
            payments:links:read: Ler links de pagamento. Produto `payments.links`.
            payments:links:write: Criar e cancelar links de pagamento. Produto `payments.links`.
            payments:customers:read: Ler clientes. Produto `payments.customers`.
            payments:customers:write: Cadastrar e atualizar clientes. Produto `payments.customers`.
            receivables:balance:read: Ler saldo. Produto `receivables.balance`.
            receivables:statement:read: Ler extrato. Produto `receivables.balance`.
            receivables:settlements:read: Ler liquidações. Produto `receivables.balance`.
            payouts:withdrawals:read: Ler saques. Produto `payouts.withdrawals`.
            payouts:withdrawals:write: Solicitar saques. Produto `payouts.withdrawals`.
            payouts:destinations:read: Ler destinos de saque (contas e chaves PIX). Produto `payouts.withdrawals`.
            payouts:destinations:write: Cadastrar e remover destinos de saque. Produto `payouts.withdrawals`.
            contracts:contracts:read: Consultar contratos, signatários e PDFs
            contracts:contracts:write: Emitir, cancelar e gerenciar signatários de contratos e enviar PDFs
            contracts:templates:read: Consultar modelos de contrato
            contracts:templates:write: Enviar e analisar modelos de contrato
            payments:deposits:read: Consultar cauções
            payments:deposits:write: Abrir, liberar e capturar cauções
  parameters:
    MerchantIdRequired:
      name: X-Merchant-Id
      in: header
      required: true
      description: Merchant da operação (`mrc_` + Guid sem hífens, minúsculo). Obrigatório com credencial de organização (ausente → 400 `MERCHANT.HEADER_REQUIRED`); com credencial de merchant é opcional (ausente = o próprio merchant). Malformado, repetido, inexistente ou de outro tenant → 403 `MERCHANT.FORBIDDEN` (nunca 404). Vazio = ausente.
      schema:
        type: string
        pattern: ^mrc_[0-9a-f]{32}$
      example: mrc_0f8fad5bd9cb469fa16570867728950e
    MerchantIdOptional:
      name: X-Merchant-Id
      in: header
      required: false
      description: Opcional. Credencial de organização sem o header atua no nível da organização; com o header, no merchant indicado. Fora do tenant, inexistente ou diferente do merchant da credencial de merchant → 403 `MERCHANT.FORBIDDEN`.
      schema:
        type: string
        pattern: ^mrc_[0-9a-f]{32}$
      example: mrc_e3cc4727054da558273162251e310d29
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: 'UUID, retido por 24 h a partir da primeira chamada. Escopo: (tenant, merchant, rota-template, chave). Mesma chave e mesmo corpo → resposta original (2xx ou 4xx) com `X-Idempotent-Replay: true`; corpo ou path concreto diferente → 409 `IDEMPOTENCY.CONFLICT`; em execução ou desfecho desconhecido → 409 `IDEMPOTENCY.IN_PROGRESS` com `resourceId` e `Retry-After: 2`. Ausente ou não-UUID → 422 `IDEMPOTENCY.KEY_REQUIRED`.'
      schema:
        type: string
        format: uuid
      example: 4b2e8c1a-9d7f-4e3b-a6c5-0f1e2d3c4b5a
    CorrelationId:
      name: X-Correlation-Id
      in: header
      required: false
      description: Opcional, sempre ecoado na resposta e repetido em `correlationId` do Problem. Ausente ou inválido → o serviço gera UUID v7.
      schema:
        type: string
        pattern: ^[A-Za-z0-9._-]{1,64}$
      example: erp-7f3a-0010
    Limit:
      name: limit
      in: query
      required: false
      description: Tamanho da página, 1–100; fora → 422 `VALIDATION.FAILED`.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    Cursor:
      name: cursor
      in: query
      required: false
      description: '`nextCursor` da página anterior, com os mesmos filtros e o mesmo `limit`. Adulterado, de outra lista, de outro merchant ou com filtros diferentes → 422 `CURSOR.INVALID`.'
      schema:
        type: string
    ContractTemplateIdPath:
      name: id
      in: path
      required: true
      description: Id do modelo, ctt_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 CONTRACT_TEMPLATE.NOT_FOUND.
      schema:
        type: string
      example: ctt_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
    ContractIdPath:
      name: id
      in: path
      required: true
      description: Id do contrato, ctr_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 CONTRACT.NOT_FOUND.
      schema:
        type: string
      example: ctr_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
    SignerIdPath:
      name: signerId
      in: path
      required: true
      description: Id do signatário, sgn_ + 32 hexadecimais. Inexistente, malformado ou de outro merchant → 404 CONTRACT.NOT_FOUND.
      schema:
        type: string
      example: sgn_0192f3b0c4d17e8a9b2f5c6d7e8f9a0b
    DepositIdPath:
      name: id
      in: path
      required: true
      description: Id da caução, dpst_ + 27 letras e dígitos. Inexistente, malformado ou de outro merchant → 404 DEPOSIT.NOT_FOUND.
      schema:
        type: string
      example: dpst_7Hq2LmX9vB4nR8sT1wK6yZ3cP0a
  headers:
    X-Correlation-Id:
      description: Mesmo valor do request (ou gerado pelo serviço). Em erro da borda, só quando o cliente enviou.
      schema:
        type: string
    X-Idempotent-Replay:
      description: '`true` quando a resposta é o replay de uma `Idempotency-Key` já usada.'
      schema:
        type: string
        enum:
        - 'true'
    Retry-After:
      description: 'Segundos até tentar de novo: 429 `1` (rate) ou `3600` (quota do sandbox); 503 da borda `1`; 503 de provedor `30`; 409 em curso `2`.'
      schema:
        type: integer
    Location:
      description: URL do recurso criado; repetida no replay.
      schema:
        type: string
    WWW-Authenticate:
      description: '`Bearer`.'
      schema:
        type: string
        enum:
        - Bearer
    Cache-Control:
      description: '`no-store`.'
      schema:
        type: string
        enum:
        - no-store
  schemas:
    Problem:
      type: object
      description: Erro público em `application/problem+json`, textos em en-US, sem stack nem corpo de provedor.
      required:
      - type
      - title
      - status
      - code
      - correlationId
      properties:
        type:
          type: string
          format: uri
          description: '`https://api.kodano.com.br/problems/` + `code` em kebab-case.'
        title:
          type: string
          description: Resumo fixo por `code`, en-US.
        status:
          type: integer
          description: Igual ao status HTTP.
        detail:
          type: string
          description: Explicação da ocorrência, sem PII nem dado do provedor.
        code:
          type: string
          pattern: ^[A-Z_]+\.[A-Z_]+$
          description: Código do catálogo.
        correlationId:
          type: string
          description: 'Serviço: o `X-Correlation-Id` efetivo. Borda: `$context.requestId`.'
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ProblemFieldError'
          description: Erros por campo; a borda sempre envia `[]`.
        resourceId:
          type: string
          description: 'Só no 409 `IDEMPOTENCY.IN_PROGRESS`: id do recurso já reservado, quando conhecido.'
        existingCustomerId:
          type: string
          description: Só no 409 `CUSTOMER.DOCUMENT_CONFLICT`.
        availableRefundAmount:
          type: integer
          description: Só no 422 `REFUND.AMOUNT_EXCEEDS_REFUNDABLE`, em centavos.
        refundId:
          type: string
          description: Só no 422 `REFUND.PROVIDER_REJECTED`.
        reason:
          type: string
          enum:
          - METHOD_NOT_SUPPORTED
          - CAPTURED
          - FINAL_STATUS
          - MANAGED_INTERNALLY
          - PROVIDER_REJECTED
          description: Só no 422 `PAYMENT.NOT_CANCELABLE`.
        missing:
          description: 'Só no 422 `MERCHANT.NOT_CONFIGURED`: o que falta na configuração do merchant (formato não definido no TDD).'
        currentStatus:
          type: string
          enum:
          - ACTIVE
          - COMPLETED
          - EXPIRED
          - CANCELED
          description: Só no 409 `PAYMENT_LINK.NOT_CANCELABLE`.
        limitScope:
          type: string
          enum:
          - credential
          - merchant
          description: Só no 422 `WITHDRAWAL.DAILY_LIMIT_EXCEEDED`.
        remainingAmount:
          type: integer
          description: Só no 422 `WITHDRAWAL.DAILY_LIMIT_EXCEEDED`, em centavos.
        resetAt:
          type: string
          format: date-time
          description: Só no 422 `WITHDRAWAL.DAILY_LIMIT_EXCEEDED`.
    ProblemFieldError:
      type: object
      required:
      - field
      - code
      - message
      properties:
        field:
          type: string
          description: Caminho do campo em camelCase (`customer.document`).
        code:
          type: string
          description: '`VALIDATION.*` ou motivo específico (ex.: `WEBHOOK.URL_PRIVATE_ADDRESS`).'
        message:
          type: string
          description: Texto en-US.
    Currency:
      type: string
      enum:
      - BRL
      description: Único valor na v1.
    Money:
      type: object
      required:
      - amount
      - currency
      properties:
        amount:
          type: integer
          minimum: 0
          description: Centavos.
        currency:
          $ref: '#/components/schemas/Currency'
    Metadata:
      type: object
      description: Até 20 chaves `^[A-Za-z0-9_.-]{1,40}$`, valores string ≤ 500; devolvido tal qual.
      maxProperties: 20
      propertyNames:
        pattern: ^[A-Za-z0-9_.-]{1,40}$
      additionalProperties:
        type: string
        maxLength: 500
    Phone:
      type: object
      required:
      - areaCode
      - number
      properties:
        areaCode:
          type: string
          pattern: ^[0-9]{2}$
        number:
          type: string
          pattern: ^[0-9]{8,9}$
    Address:
      type: object
      required:
      - postalCode
      - publicArea
      - number
      - neighborhood
      - city
      - state
      properties:
        postalCode:
          type: string
          pattern: ^[0-9]{8}$
        publicArea:
          type: string
        number:
          type: string
        complement:
          type: string
        neighborhood:
          type: string
        city:
          type: string
        state:
          type: string
          pattern: ^[A-Z]{2}$
          description: UF.
    Document:
      type: string
      pattern: ^([0-9]{11}|[0-9A-Z]{12}[0-9]{2})$
      description: CPF com 11 dígitos (verificadores conferidos, sem sequência repetida) ou CNPJ com 14 caracteres `[0-9A-Z]{12}[0-9]{2}` (alfanumérico).
    CustomerRef:
      type: object
      required:
      - id
      properties:
        id:
          type: string
          pattern: ^cus_
          description: '`cus_` do cliente do merchant.'
    CustomerInline:
      type: object
      description: Cliente inline. Documento já cadastrado reaproveita a pessoa.
      required:
      - name
      - document
      - email
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 150
        document:
          $ref: '#/components/schemas/Document'
        email:
          type: string
          format: email
          maxLength: 100
        phone:
          $ref: '#/components/schemas/Phone'
        address:
          $ref: '#/components/schemas/Address'
    CustomerInput:
      oneOf:
      - $ref: '#/components/schemas/CustomerRef'
      - $ref: '#/components/schemas/CustomerInline'
      description: '`{ id }` ou cliente inline; os dois juntos → `VALIDATION.ONE_OF`.'
    CustomerSummary:
      type: object
      required:
      - id
      - name
      - document
      properties:
        id:
          type: string
          pattern: ^cus_
        name:
          type: string
        document:
          type: string
          description: CPF/CNPJ mascarado.
    PaymentStatus:
      type: string
      enum:
      - PENDING
      - AUTHORIZED
      - CAPTURED
      - SUCCEEDED
      - FAILED
      - CANCELED
      - REFUNDED
      - PARTIALLY_REFUNDED
      - CHARGEBACK
      description: 'Nove status mutuamente exclusivos. Precedência entre os derivados de sucesso: `CHARGEBACK` > `REFUNDED` > `PARTIALLY_REFUNDED` > `CAPTURED`/`SUCCEEDED` > `AUTHORIZED`.'
    PaymentMethod:
      type: string
      enum:
      - PIX
      - BOLETO
      - CARD
    PixBlock:
      type: object
      required:
      - qrCode
      - copyPaste
      - expiresAt
      - endToEndId
      properties:
        qrCode:
          type: string
          description: EMV (`emvqrcps`); igual a `copyPaste` na v1, sem imagem.
        copyPaste:
          type: string
          description: EMV copia-e-cola.
        expiresAt:
          type: string
          format: date-time
          description: '`createdAt + expiresIn`.'
        endToEndId:
          type:
          - string
          - 'null'
          description: Id E2E do Pix recebido; `null` antes do pagamento.
    BoletoBlock:
      type: object
      required:
      - barcode
      - digitableLine
      - pdfUrl
      - dueDate
      - paidAt
      properties:
        barcode:
          type: string
          description: Código de barras, 44 dígitos.
        digitableLine:
          type: string
        pdfUrl:
          type:
          - string
          - 'null'
          description: '`null` na v1 até decisão de Produto + Tech Lead.'
        dueDate:
          type: string
          format: date
        paidAt:
          type:
          - string
          - 'null'
          format: date-time
          description: Carimbo do webhook `charge-paid`.
    CardBlock:
      type: object
      required:
      - id
      - brand
      - last4
      - installments
      - capturedAmount
      - capturedAt
      properties:
        id:
          type:
          - string
          - 'null'
          description: '`card_` do cofre; `null` quando pago por token sem `store`.'
        brand:
          type: string
          description: Bandeira (`VISA`, `MASTERCARD`, …).
        last4:
          type: string
        installments:
          type: integer
          minimum: 1
          maximum: 12
        capturedAmount:
          type:
          - integer
          - 'null'
          description: Centavos capturados (em captura parcial, o valor pedido).
        capturedAt:
          type:
          - string
          - 'null'
          format: date-time
    NextAction:
      type: object
      required:
      - type
      - url
      description: Só em cartão `PENDING` com desafio 3DS.
      properties:
        type:
          type: string
          enum:
          - REDIRECT_3DS
        url:
          type: string
          format: uri
    PaymentFailure:
      type: object
      required:
      - code
      - message
      description: Só em `FAILED`. Só `PAYMENT.PROVIDER_ERROR` não é final (a reconciliação pode levar a `SUCCEEDED`).
      properties:
        code:
          type: string
          enum:
          - PAYMENT.DECLINED
          - PAYMENT.PROVIDER_REJECTED
          - PAYMENT.PROVIDER_ERROR
          - CARD.ANTIFRAUD_REJECTED
          - MERCHANT.NOT_CONFIGURED
        message:
          type: string
    Payment:
      type: object
      description: Mesmo objeto no 201 dos três POST de criação, no 200 do `cancel`, no detalhe, em cada item da lista e em `data.object` dos eventos de pagamento. O bloco do método vem preenchido; os dos outros métodos vêm `null`.
      required:
      - id
      - status
      - statusReason
      - method
      - amount
      - currency
      - description
      - customer
      - failure
      - refundedAmount
      - refundedAt
      - paymentLinkId
      - metadata
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^pay_
          description: '`pay_` + 28 alfanuméricos.'
        status:
          $ref: '#/components/schemas/PaymentStatus'
        statusReason:
          type:
          - string
          - 'null'
          enum:
          - PROVIDER_PENDING
          - EXPIRED
          - CAPTURE_FAILED
          - null
          description: '`PROVIDER_PENDING` (PENDING sem instrumento após timeout do provedor), `EXPIRED` (CANCELED por expiração), `CAPTURE_FAILED` (CANCELED porque a captura imediata falhou).'
        method:
          $ref: '#/components/schemas/PaymentMethod'
        amount:
          type: integer
          description: Centavos pedidos na criação.
        currency:
          $ref: '#/components/schemas/Currency'
        description:
          type:
          - string
          - 'null'
          maxLength: 140
        customer:
          $ref: '#/components/schemas/CustomerSummary'
        pix:
          anyOf:
          - $ref: '#/components/schemas/PixBlock'
          - type: 'null'
        boleto:
          anyOf:
          - $ref: '#/components/schemas/BoletoBlock'
          - type: 'null'
        card:
          anyOf:
          - $ref: '#/components/schemas/CardBlock'
          - type: 'null'
        nextAction:
          anyOf:
          - $ref: '#/components/schemas/NextAction'
          - type: 'null'
        failure:
          anyOf:
          - $ref: '#/components/schemas/PaymentFailure'
          - type: 'null'
        refundedAmount:
          type: integer
          description: Soma dos estornos `SUCCEEDED` (o ajuste de captura parcial não conta).
        refundedAt:
          type:
          - string
          - 'null'
          format: date-time
        paymentLinkId:
          type:
          - string
          - 'null'
          description: '`ord_` quando o pagamento veio de link.'
        metadata:
          $ref: '#/components/schemas/Metadata'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PaymentList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Payment'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreatePixPaymentRequest:
      type: object
      required:
      - amount
      - currency
      - customer
      properties:
        amount:
          type: integer
          minimum: 100
          maximum: 100000000
          description: Centavos.
        currency:
          $ref: '#/components/schemas/Currency'
        description:
          type: string
          maxLength: 140
        customer:
          $ref: '#/components/schemas/CustomerInput'
        expiresIn:
          type: integer
          minimum: 60
          maximum: 86400
          default: 86400
          description: Segundos.
        metadata:
          $ref: '#/components/schemas/Metadata'
    CreateBoletoPaymentRequest:
      type: object
      required:
      - amount
      - currency
      - dueDate
      - customer
      properties:
        amount:
          type: integer
          minimum: 100
          maximum: 100000000
        currency:
          $ref: '#/components/schemas/Currency'
        description:
          type: string
          maxLength: 140
        dueDate:
          type: string
          format: date
          description: D+1 a D+180 em UTC.
        customer:
          $ref: '#/components/schemas/CustomerInput'
        fine:
          type: object
          required:
          - percent
          properties:
            percent:
              type: number
              exclusiveMinimum: 0
              maximum: 100
              description: Multa após o vencimento, até 2 casas.
        interest:
          type: object
          required:
          - monthlyPercent
          properties:
            monthlyPercent:
              type: number
              exclusiveMinimum: 0
              maximum: 100
              description: Juros de mora ao mês, até 2 casas.
        discount:
          type: object
          required:
          - amount
          - limitDate
          properties:
            amount:
              type: integer
              description: Centavos, < `amount`; desconto fixo.
            limitDate:
              type: string
              format: date
              description: Entre hoje e `dueDate`.
        metadata:
          $ref: '#/components/schemas/Metadata'
    CardStoredInput:
      type: object
      required:
      - id
      properties:
        id:
          type: string
          pattern: ^card_
          description: '`card_` do cofre.'
    CardTokenInput:
      type: object
      required:
      - token
      - holderName
      - expiry
      - lastFour
      properties:
        token:
          type: string
          description: '`cryptData` do SDK de tokenização (uso único, 5 min).'
        holderName:
          type: string
        expiry:
          type: string
          pattern: ^[0-9]{2}/[0-9]{2}$
          description: '`MM/AA`.'
        lastFour:
          type: string
          pattern: ^[0-9]{4}$
        store:
          type: boolean
          description: Registra no cofre e devolve `card.id`; exige `payments:vault:write`.
    CreateCardPaymentRequest:
      type: object
      required:
      - amount
      - currency
      - card
      properties:
        amount:
          type: integer
          minimum: 100
          maximum: 100000000
        currency:
          $ref: '#/components/schemas/Currency'
        installments:
          type: integer
          minimum: 1
          maximum: 12
        capture:
          type: boolean
          default: true
        initiator:
          type: string
          enum:
          - CUSTOMER
          - MERCHANT
          description: '`CUSTOMER` (CIT) ou `MERCHANT` (MIT, só com `card.id`).'
        card:
          oneOf:
          - $ref: '#/components/schemas/CardStoredInput'
          - $ref: '#/components/schemas/CardTokenInput'
          description: Exatamente um de `{id}` e `{token, holderName, expiry, lastFour, store?}`. `number`, `pan`, `cvv` ou `cvc` → 422 `CARD.PAN_NOT_ALLOWED`.
        customer:
          $ref: '#/components/schemas/CustomerInput'
        browser:
          type: object
          properties:
            ip:
              type: string
              description: Obrigatório em CIT.
            userAgent:
              type: string
            device:
              type: object
              description: Conteúdo não definido no TDD.
        threeDs:
          type: object
          properties:
            returnUrl:
              type: string
              format: uri
        description:
          type: string
          maxLength: 140
        metadata:
          $ref: '#/components/schemas/Metadata'
    CaptureRequest:
      type: object
      properties:
        amount:
          type: integer
          minimum: 1
          description: Omitido = total autorizado com juros; menor = captura parcial.
    Capture:
      type: object
      required:
      - id
      - paymentId
      - status
      - amount
      - currency
      - authorizedAmount
      - adjustment
      properties:
        id:
          type: string
          pattern: ^cap_
        paymentId:
          type: string
          pattern: ^pay_
        status:
          type: string
          enum:
          - SUCCEEDED
          - FAILED
        amount:
          type: integer
          description: Valor capturado pedido.
        currency:
          $ref: '#/components/schemas/Currency'
        authorizedAmount:
          type: integer
          description: Total autorizado, com juros de parcelamento.
        adjustment:
          anyOf:
          - type: object
            required:
            - amount
            - status
            description: 'Só em captura parcial: estorno `CAPTURE_ADJUSTMENT` da diferença.'
            properties:
              amount:
                type: integer
              status:
                type: string
                description: Nasce `PENDING`.
          - type: 'null'
    RefundRequest:
      type: object
      properties:
        amount:
          type: integer
          minimum: 1
          description: Omitido = `availableRefundAmount`.
        reason:
          type: string
          enum:
          - REQUESTED_BY_CUSTOMER
          - DUPLICATE
          - FRAUDULENT
          - OTHER
        metadata:
          $ref: '#/components/schemas/Metadata'
    Refund:
      type: object
      required:
      - id
      - amount
      - currency
      - status
      - reason
      - failure
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^ref_
        paymentId:
          type: string
          pattern: ^pay_
        amount:
          type: integer
        currency:
          $ref: '#/components/schemas/Currency'
        status:
          type: string
          enum:
          - PENDING
          - SUCCEEDED
          - FAILED
        reason:
          type:
          - string
          - 'null'
          enum:
          - REQUESTED_BY_CUSTOMER
          - DUPLICATE
          - FRAUDULENT
          - OTHER
          - null
          description: '`null` em estorno do portal ou da caução.'
        metadata:
          $ref: '#/components/schemas/Metadata'
        failure:
          anyOf:
          - type: object
            required:
            - code
            properties:
              code:
                type: string
                enum:
                - REFUND.PROVIDER_REJECTED
                - REFUND.PROVIDER_ERROR
              message:
                type: string
          - type: 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    RefundList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Refund'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CancelPaymentRequest:
      type: object
      maxProperties: 0
      description: Sem corpo ou `{}`; qualquer campo → 422 `VALIDATION.FAILED`.
    TimelineEntry:
      type: object
      required:
      - type
      - occurredAt
      properties:
        type:
          type: string
          enum:
          - payment.created
          - payment.authorized
          - payment.captured
          - payment.succeeded
          - payment.failed
          - payment.canceled
          - payment.expired
          - payment.refunded
          - chargeback.received
        occurredAt:
          type: string
          format: date-time
        amount:
          type: integer
          description: Só em `payment.captured` e `payment.refunded`.
        resourceId:
          type: string
          description: '`cap_` ou `ref_`, só em `payment.captured` e `payment.refunded`.'
    TimelineList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TimelineEntry'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
      description: 'Sempre numa página (`hasMore: false`), em ordem cronológica.'
    PaymentFees:
      type: object
      required:
      - paymentId
      - currency
      - grossAmount
      - netAmount
      - status
      - fees
      properties:
        paymentId:
          type: string
          pattern: ^pay_
        currency:
          $ref: '#/components/schemas/Currency'
        grossAmount:
          type: integer
        netAmount:
          type: integer
        status:
          type: string
          enum:
          - PENDING
          - ESTIMATED
          - SETTLED
          - PARTIALLY_SETTLED
        fees:
          type: array
          items:
            type: object
            required:
            - type
            - amount
            properties:
              type:
                type: string
                enum:
                - KODANO_FEE
                - ANTICIPATION_FEE
              amount:
                type: integer
    Receipt:
      type: object
      required:
      - paymentId
      - status
      - paidAt
      - amount
      - currency
      - refundedAmount
      - method
      - payer
      - pdf
      properties:
        paymentId:
          type: string
          pattern: ^pay_
        status:
          $ref: '#/components/schemas/PaymentStatus'
        paidAt:
          type: string
          format: date-time
        amount:
          type: integer
        currency:
          $ref: '#/components/schemas/Currency'
        refundedAmount:
          type: integer
        method:
          $ref: '#/components/schemas/PaymentMethod'
        card:
          type: object
          properties:
            brand:
              type: string
            last4:
              type: string
          description: Só em cartão; bloco de PIX e boleto não definido no TDD.
        payer:
          type: object
          required:
          - name
          - document
          properties:
            name:
              type: string
            document:
              type: string
              description: Mascarado.
        pdf:
          anyOf:
          - type: object
            required:
            - url
            - expiresAt
            properties:
              url:
                type: string
                format: uri
                description: URL pré-assinada de 15 min.
              expiresAt:
                type: string
                format: date-time
          - type: 'null'
    Customer:
      type: object
      required:
      - id
      - type
      - name
      - document
      - email
      - phone
      - address
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^cus_
          description: '`cus_` + 16 caracteres.'
        type:
          type: string
          enum:
          - INDIVIDUAL
          - COMPANY
          description: Derivado do `document`, nunca enviado.
        name:
          type: string
          minLength: 1
          maxLength: 150
        document:
          type: string
          description: Completo só neste recurso; embutido em link ou pagamento sai mascarado.
        email:
          type: string
          maxLength: 100
        phone:
          anyOf:
          - $ref: '#/components/schemas/Phone'
          - type: 'null'
        address:
          anyOf:
          - $ref: '#/components/schemas/Address'
          - type: 'null'
        createdAt:
          type: string
          format: date-time
          description: Quando a pessoa virou cliente; base do filtro e do cursor.
        updatedAt:
          type: string
          format: date-time
    CustomerList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Customer'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreateCustomerRequest:
      type: object
      required:
      - name
      - document
      - email
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 150
        document:
          $ref: '#/components/schemas/Document'
        email:
          type: string
          format: email
          maxLength: 100
        phone:
          $ref: '#/components/schemas/Phone'
        address:
          $ref: '#/components/schemas/Address'
    UpdateCustomerRequest:
      type: object
      minProperties: 1
      description: 'Parcial: só `name`, `email`, `phone` e `address`; omitido não muda; `null` → 422; `document`/`type` → 422 `CUSTOMER.DOCUMENT_IMMUTABLE`.'
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 150
        email:
          type: string
          format: email
          maxLength: 100
        phone:
          $ref: '#/components/schemas/Phone'
        address:
          $ref: '#/components/schemas/Address'
    Card:
      type: object
      required:
      - id
      - customerId
      - brand
      - last4
      - expiry
      - holderName
      - status
      properties:
        id:
          type: string
          pattern: ^card_
          description: '`card_` + 27 caracteres.'
        customerId:
          type: string
          pattern: ^cus_
        brand:
          type: string
          description: Bandeira devolvida pelo provedor no registro.
        last4:
          type: string
        expiry:
          type: string
          description: '`MM/AA`.'
        holderName:
          type: string
        status:
          type: string
          enum:
          - ACTIVE
          - INACTIVE
    CardList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Card'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreateCardRequest:
      type: object
      required:
      - customerId
      - token
      - holderName
      - expiry
      - lastFour
      properties:
        customerId:
          type: string
          pattern: ^cus_
        token:
          type: string
          description: '`cryptData` do SDK de tokenização (uso único, 5 min).'
        holderName:
          type: string
        expiry:
          type: string
          pattern: ^[0-9]{2}/[0-9]{2}$
        lastFour:
          type: string
          pattern: ^[0-9]{4}$
    PaymentLinkStatus:
      type: string
      enum:
      - ACTIVE
      - COMPLETED
      - EXPIRED
      - CANCELED
    PaymentLink:
      type: object
      required:
      - id
      - url
      - status
      - amount
      - currency
      - name
      - description
      - methods
      - maxPayments
      - paymentsCount
      - maxInstallments
      - expiresAt
      - customer
      - externalReference
      - successUrl
      - failureUrl
      - metadata
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^ord_
          description: '`ord_` + 28 caracteres.'
        url:
          type: string
          format: uri
          description: Checkout hospedado e anônimo, fora do host da API.
        status:
          $ref: '#/components/schemas/PaymentLinkStatus'
        amount:
          type: integer
          minimum: 100
          maximum: 100000000
        currency:
          $ref: '#/components/schemas/Currency'
        name:
          type:
          - string
          - 'null'
          maxLength: 255
        description:
          type:
          - string
          - 'null'
          maxLength: 255
        methods:
          type: array
          minItems: 1
          maxItems: 3
          uniqueItems: true
          items:
            $ref: '#/components/schemas/PaymentMethod'
        maxPayments:
          type: integer
          minimum: 1
          maximum: 10000
          description: Sempre 1 com `customer`.
        paymentsCount:
          type: integer
          description: Pode passar de `maxPayments`.
        maxInstallments:
          type:
          - integer
          - 'null'
          minimum: 1
          maximum: 12
        expiresAt:
          type:
          - string
          - 'null'
          format: date-time
          description: '`null` = sem vencimento.'
        customer:
          anyOf:
          - $ref: '#/components/schemas/CustomerSummary'
          - type: 'null'
        externalReference:
          type: string
          maxLength: 100
          description: Única por merchant; gerada quando omitida.
        successUrl:
          type:
          - string
          - 'null'
          maxLength: 255
        failureUrl:
          type:
          - string
          - 'null'
          maxLength: 255
        metadata:
          anyOf:
          - $ref: '#/components/schemas/Metadata'
          - type: 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PaymentLinkList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PaymentLink'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreatePaymentLinkRequest:
      type: object
      required:
      - amount
      - currency
      - methods
      properties:
        amount:
          type: integer
          minimum: 100
          maximum: 100000000
        currency:
          $ref: '#/components/schemas/Currency'
        name:
          type: string
          maxLength: 255
        description:
          type: string
          maxLength: 255
        methods:
          type: array
          minItems: 1
          maxItems: 3
          uniqueItems: true
          items:
            $ref: '#/components/schemas/PaymentMethod'
        maxPayments:
          type: integer
          minimum: 1
          maximum: 10000
          default: 1
        maxInstallments:
          type: integer
          minimum: 1
          maximum: 12
          description: Só com `CARD` em `methods`.
        expiresAt:
          type: string
          format: date-time
          description: Entre agora + 5 min e agora + 180 dias; ausente = sem vencimento.
        customer:
          $ref: '#/components/schemas/CustomerInput'
        externalReference:
          type: string
          maxLength: 100
        successUrl:
          type: string
          format: uri
          maxLength: 255
        failureUrl:
          type: string
          format: uri
          maxLength: 255
        metadata:
          $ref: '#/components/schemas/Metadata'
    Balance:
      type: object
      required:
      - available
      - pending
      - blocked
      - blockedBreakdown
      - asOf
      properties:
        available:
          allOf:
          - $ref: '#/components/schemas/Money'
          description: Disponível para saque na carteira `default`.
        pending:
          allOf:
          - $ref: '#/components/schemas/Money'
          description: Recebíveis ainda não creditados.
        blocked:
          allOf:
          - $ref: '#/components/schemas/Money'
          description: 'Retido: reserva + escrow de chargeback.'
        blockedBreakdown:
          type: object
          required:
          - reserve
          - chargeback
          properties:
            reserve:
              $ref: '#/components/schemas/Money'
            chargeback:
              $ref: '#/components/schemas/Money'
        asOf:
          type: string
          format: date-time
          description: Instante do cálculo, vindo do treasury.
    StatementEntry:
      type: object
      required:
      - id
      - type
      - category
      - amount
      - currency
      - balanceAfter
      - referenceId
      - occurredAt
      properties:
        id:
          type: string
          pattern: ^ste_
          description: Estável; serve para deduplicar.
        type:
          type: string
          enum:
          - CREDIT
          - DEBIT
        category:
          type: string
          enum:
          - PAYMENT
          - FEE
          - WITHDRAWAL
          - REFUND
          - CHARGEBACK
          - ADJUSTMENT
        amount:
          type: integer
          description: Valor absoluto, em centavos.
        currency:
          $ref: '#/components/schemas/Currency'
        balanceAfter:
          type: integer
          description: Saldo `available` logo depois do lançamento.
        referenceId:
          type:
          - string
          - 'null'
          description: '`pay_`, `ref_` ou `wdr_` de origem.'
        occurredAt:
          type: string
          format: date-time
    StatementList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/StatementEntry'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    SettlementStatus:
      type: string
      enum:
      - SCHEDULED
      - PROCESSING
      - SETTLED
      - FAILED
      - REVERSED
    Settlement:
      type: object
      required:
      - id
      - status
      - settlementDate
      - currency
      - transactionCount
      - grossAmount
      - feesAmount
      - netAmount
      - settledAt
      properties:
        id:
          type: string
          pattern: ^stl_[0-9]{8}[0-9a-f]{24}$
          description: '`stl_` + `yyyyMMdd` + 24 hex.'
        status:
          $ref: '#/components/schemas/SettlementStatus'
        settlementDate:
          type: string
          format: date
        currency:
          $ref: '#/components/schemas/Currency'
        transactionCount:
          type: integer
        grossAmount:
          type: integer
        feesAmount:
          type: integer
          description: '`grossAmount − netAmount`.'
        netAmount:
          type: integer
        settledAt:
          type:
          - string
          - 'null'
          format: date-time
          description: Só em `SETTLED`.
    SettlementList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Settlement'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    SettlementItem:
      type: object
      required:
      - paymentId
      - method
      - installment
      - status
      - anticipated
      - grossAmount
      - feesAmount
      - netAmount
      properties:
        paymentId:
          type:
          - string
          - 'null'
          description: '`pay_` do pagamento de origem.'
        method:
          type: string
          enum:
          - CREDIT_CARD
          - DEBIT_CARD
          - PIX
          - BOLETO
        installment:
          type: integer
        status:
          $ref: '#/components/schemas/SettlementStatus'
        anticipated:
          type: boolean
        grossAmount:
          type: integer
        feesAmount:
          type: integer
        netAmount:
          type: integer
    SettlementDetail:
      allOf:
      - $ref: '#/components/schemas/Settlement'
      - type: object
        required:
        - items
        properties:
          items:
            type: object
            required:
            - data
            - hasMore
            - nextCursor
            properties:
              data:
                type: array
                items:
                  $ref: '#/components/schemas/SettlementItem'
              hasMore:
                type: boolean
                description: Há página seguinte.
              nextCursor:
                type:
                - string
                - 'null'
                description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    WithdrawalFailure:
      type: object
      required:
      - code
      properties:
        code:
          type: string
          enum:
          - WITHDRAWAL.PAYOUT_FAILED
          - WITHDRAWAL.REJECTED
          - WITHDRAWAL.CANCELED
        message:
          type: string
          description: Aparece só no exemplo de `withdrawal.failed`.
    Withdrawal:
      type: object
      required:
      - id
      - status
      - amount
      - currency
      - fee
      - totalAmount
      - netAmount
      - method
      - destination
      - failure
      properties:
        id:
          type: string
          pattern: ^wdr_
        status:
          type: string
          enum:
          - REQUESTED
          - PROCESSING
          - COMPLETED
          - FAILED
          description: 'Transições não monotônicas: `FAILED` pode virar `COMPLETED` ou voltar a `REQUESTED`.'
        amount:
          type: integer
        currency:
          $ref: '#/components/schemas/Currency'
        fee:
          type: integer
          description: Cobrada por cima; `0` em saque legado.
        totalAmount:
          type: integer
          description: '`amount + fee`: o que sai do saldo.'
        netAmount:
          type: integer
          description: O que chega ao destino (= `amount`).
        method:
          type: string
          enum:
          - PIX_KEY
          - BANK_ACCOUNT
          - TED
          description: '`TED` só leitura (saques antigos do portal).'
        destination:
          type: object
          required:
          - type
          - id
          properties:
            type:
              type: string
              enum:
              - BANK_ACCOUNT
              - PIX_KEY
            id:
              type:
              - string
              - 'null'
              description: '`bka_` ou `pxk_`; `null` em saque do portal com destino ad hoc.'
        failure:
          anyOf:
          - $ref: '#/components/schemas/WithdrawalFailure'
          - type: 'null'
        createdVia:
          type: string
          enum:
          - API
          - PORTAL
          - BACKOFFICE
          - ONBOARDING
    WithdrawalList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Withdrawal'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreateWithdrawalRequest:
      type: object
      required:
      - amount
      - currency
      - destination
      properties:
        amount:
          type: integer
          exclusiveMinimum: 0
          description: Centavos.
        currency:
          $ref: '#/components/schemas/Currency'
        destination:
          oneOf:
          - type: object
            required:
            - bankAccountId
            properties:
              bankAccountId:
                type: string
                pattern: ^bka_
            additionalProperties: false
          - type: object
            required:
            - pixKeyId
            properties:
              pixKeyId:
                type: string
                pattern: ^pxk_
            additionalProperties: false
          description: Só um de `bankAccountId` e `pixKeyId`; dados bancários no corpo → 422.
        description:
          type: string
          maxLength: 140
    BankAccount:
      type: object
      required:
      - id
      - status
      - bankCode
      - branch
      - accountLast4
      - type
      - holderName
      - holderMatchesMerchant
      - createdVia
      - createdAt
      - verifiedAt
      properties:
        id:
          type: string
          pattern: ^bka_
        status:
          type: string
          enum:
          - PENDING_VERIFICATION
          - VERIFIED
        bankCode:
          type: string
          description: Código COMPE.
        branch:
          type: string
          pattern: ^[0-9]{1,5}(-[0-9A-Za-z])?$
        accountLast4:
          type: string
        type:
          type: string
          enum:
          - CHECKING
          - SAVINGS
          - PAYMENT
        holderName:
          type: string
        holderMatchesMerchant:
          type: boolean
        createdVia:
          type: string
          enum:
          - API
          - PORTAL
          - BACKOFFICE
          - ONBOARDING
        createdAt:
          type: string
          format: date-time
        verifiedAt:
          type:
          - string
          - 'null'
          format: date-time
    BankAccountList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/BankAccount'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreateBankAccountRequest:
      type: object
      required:
      - bankCode
      - branch
      - account
      - type
      - holderName
      - holderTaxId
      properties:
        bankCode:
          type: string
        branch:
          type: string
          pattern: ^[0-9]{1,5}(-[0-9A-Za-z])?$
        account:
          type: string
          description: '`número-dígito`; o trecho depois do último `-` é o dígito.'
        type:
          type: string
          enum:
          - CHECKING
          - SAVINGS
          - PAYMENT
        holderName:
          type: string
        holderTaxId:
          type: string
          pattern: ^[0-9]+$
          description: CPF ou CNPJ, só dígitos; nunca volta na resposta.
    PixKeyType:
      type: string
      enum:
      - CPF
      - CNPJ
      - PHONE
      - EMAIL
      - EVP
    PixKey:
      type: object
      required:
      - id
      - status
      - type
      - holderName
      - institutionIspb
      - holderMatchesMerchant
      - createdVia
      - createdAt
      - verifiedAt
      properties:
        id:
          type: string
          pattern: ^pxk_
        status:
          type: string
          enum:
          - PENDING_VERIFICATION
          - VERIFIED
        type:
          $ref: '#/components/schemas/PixKeyType'
        holderName:
          type: string
          description: Titular devolvido pelo DICT.
        institutionIspb:
          type: string
        holderMatchesMerchant:
          type: boolean
        createdVia:
          type: string
          enum:
          - API
          - PORTAL
          - BACKOFFICE
          - ONBOARDING
        createdAt:
          type: string
          format: date-time
        verifiedAt:
          type:
          - string
          - 'null'
          format: date-time
    PixKeyList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PixKey'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    CreatePixKeyRequest:
      type: object
      required:
      - type
      - key
      properties:
        type:
          $ref: '#/components/schemas/PixKeyType'
        key:
          type: string
          minLength: 1
          description: Normalizado antes da consulta ao DICT.
    EventType:
      type: string
      enum:
      - payment.succeeded
      - payment.failed
      - payment.refunded
      - chargeback.received
      - withdrawal.completed
      - withdrawal.failed
      - fx.operation.settled
      - fx.operation.expired
      - fx.operation.failed
      - fx.operation.action_required
      - fx.operation.refunded
      - settlement.closed
      - webhook_endpoint.disabled
      - ping
    SubscribableEventType:
      type: string
      description: Catálogo v1 sem `ping`.
      enum:
      - payment.succeeded
      - payment.failed
      - payment.refunded
      - chargeback.received
      - withdrawal.completed
      - withdrawal.failed
      - fx.operation.settled
      - fx.operation.expired
      - fx.operation.failed
      - fx.operation.action_required
      - fx.operation.refunded
      - settlement.closed
      - webhook_endpoint.disabled
    WebhookEndpoint:
      type: object
      required:
      - id
      - url
      - description
      - events
      - status
      - scope
      - merchantId
      - failureCount
      - lastSuccessAt
      - lastFailureAt
      - disabledAt
      - disabledReason
      - previousSecretExpiresAt
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^whe_
        url:
          type: string
          format: uri
          maxLength: 500
          description: '`https`, porta 443 ou 8443, sem fragmento nem userinfo.'
        description:
          type:
          - string
          - 'null'
          maxLength: 255
        events:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items:
            $ref: '#/components/schemas/SubscribableEventType'
        status:
          type: string
          enum:
          - ACTIVE
          - DISABLED
        scope:
          type: string
          enum:
          - ORGANIZATION
          - MERCHANT
          description: Imutável.
        merchantId:
          type:
          - string
          - 'null'
          description: '`mrc_…` no escopo `MERCHANT`; `null` no `ORGANIZATION`; imutável.'
        failureCount:
          type: integer
          description: Falhas de entrega desde o último 2xx (exclui `TEST`).
        lastSuccessAt:
          type:
          - string
          - 'null'
          format: date-time
        lastFailureAt:
          type:
          - string
          - 'null'
          format: date-time
        disabledAt:
          type:
          - string
          - 'null'
          format: date-time
        disabledReason:
          type:
          - string
          - 'null'
          enum:
          - MANUAL
          - AUTO_FAILURES
          - null
        previousSecretExpiresAt:
          type:
          - string
          - 'null'
          format: date-time
          description: Fim da janela em que o segredo anterior ainda assina.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    WebhookEndpointList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEndpoint'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    WebhookEndpointWithSecret:
      allOf:
      - $ref: '#/components/schemas/WebhookEndpoint'
      - type: object
        required:
        - secret
        properties:
          secret:
            type:
            - string
            - 'null'
            pattern: ^whsec_[A-Za-z0-9_-]{43}$
            description: Exibido uma única vez; `null` no replay de idempotência.
    CreateWebhookEndpointRequest:
      type: object
      required:
      - url
      - events
      properties:
        url:
          type: string
          format: uri
          maxLength: 500
        events:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items:
            $ref: '#/components/schemas/SubscribableEventType'
        description:
          type: string
          maxLength: 255
    UpdateWebhookEndpointRequest:
      type: object
      minProperties: 1
      description: Ao menos um de `url`, `events`, `description`, `status`; `scope`/`merchantId` → 422.
      properties:
        url:
          type: string
          format: uri
          maxLength: 500
        events:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items:
            $ref: '#/components/schemas/SubscribableEventType'
        description:
          type: string
          maxLength: 255
        status:
          type: string
          enum:
          - ACTIVE
          - DISABLED
    RotateSecretRequest:
      type: object
      properties:
        overlapHours:
          type: integer
          minimum: 0
          maximum: 72
          default: 24
    RotateSecretResponse:
      type: object
      required:
      - id
      - secret
      - previousSecretExpiresAt
      properties:
        id:
          type: string
          pattern: ^whe_
        secret:
          type:
          - string
          - 'null'
          description: Novo segredo, exibido uma vez; `null` no replay.
        previousSecretExpiresAt:
          type:
          - string
          - 'null'
          format: date-time
    WebhookTestResponse:
      type: object
      required:
      - deliveryId
      - eventId
      - status
      properties:
        deliveryId:
          type: string
          pattern: ^dlv_
        eventId:
          type: string
          pattern: ^evt_
        status:
          type: string
          enum:
          - PENDING
    WebhookDelivery:
      type: object
      required:
      - id
      - eventId
      - eventType
      - kind
      - status
      - attempt
      - httpStatus
      - latencyMs
      - at
      - nextAttemptAt
      - error
      properties:
        id:
          type: string
          pattern: ^dlv_
        eventId:
          type: string
          pattern: ^evt_
        eventType:
          $ref: '#/components/schemas/EventType'
        kind:
          type: string
          enum:
          - EVENT
          - REDELIVER
          - TEST
        status:
          type: string
          enum:
          - PENDING
          - RETRYING
          - SUCCEEDED
          - FAILED
          - CANCELED
        attempt:
          type: integer
          minimum: 0
          maximum: 8
        httpStatus:
          type:
          - integer
          - 'null'
        latencyMs:
          type:
          - integer
          - 'null'
        at:
          type:
          - string
          - 'null'
          format: date-time
        nextAttemptAt:
          type:
          - string
          - 'null'
          format: date-time
        error:
          type:
          - string
          - 'null'
          enum:
          - TIMEOUT
          - DNS_FAILURE
          - CONNECTION_FAILED
          - TLS_ERROR
          - SSRF_BLOCKED
          - REDIRECT_NOT_FOLLOWED
          - HTTP_4XX
          - HTTP_5XX
          - ENDPOINT_DISABLED
          - CLIENT_NOT_ENTITLED
          - null
    WebhookDeliveryList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    Event:
      type: object
      description: 'Envelope: corpo assinado do webhook, item de `GET /v1/events` e resposta de `GET /v1/events/{id}`.'
      required:
      - id
      - type
      - apiVersion
      - createdAt
      - environment
      - merchantId
      - data
      properties:
        id:
          type: string
          pattern: ^evt_
          description: Igual ao header `X-Kodano-Event-Id`; chave de deduplicação.
        type:
          $ref: '#/components/schemas/EventType'
        apiVersion:
          type: string
          description: '`2026-10-01`.'
        createdAt:
          type: string
          format: date-time
        environment:
          type: string
          enum:
          - sandbox
          - production
        merchantId:
          type:
          - string
          - 'null'
          description: '`mrc_…`; `null` em evento de organização.'
        data:
          type: object
          required:
          - object
          properties:
            object:
              type: object
              description: Recurso no instante do fato, com o shape do `GET` do recurso.
            refund:
              allOf:
              - $ref: '#/components/schemas/Refund'
              description: Só em `payment.refunded`.
    EventList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Event'
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco e assinado da página seguinte; `null` quando `hasMore` é `false`.
    Chargeback:
      type: object
      description: Schema próprio do evento `chargeback.received`; não há `GET` de chargeback na v1.
      required:
      - id
      - paymentId
      - amount
      - currency
      - reason
      - status
      - defenseDeadline
      - receivedAt
      properties:
        id:
          type: string
          pattern: ^cbk_
        paymentId:
          type: string
          pattern: ^pay_
        amount:
          type: integer
        currency:
          $ref: '#/components/schemas/Currency'
        reason:
          type:
          - string
          - 'null'
        status:
          type: string
        defenseDeadline:
          type:
          - string
          - 'null'
          format: date-time
        receivedAt:
          type: string
          format: date-time
    RedeliverRequest:
      type: object
      properties:
        endpointId:
          type: string
          pattern: ^whe_
          description: Sem ele, vai a todos os endpoints `ACTIVE` do escopo que assinam o tipo.
    RedeliverResponse:
      type: object
      required:
      - eventId
      - deliveries
      properties:
        eventId:
          type: string
          pattern: ^evt_
        deliveries:
          type: array
          items:
            type: object
            required:
            - id
            - endpointId
            - status
            properties:
              id:
                type: string
                pattern: ^dlv_
              endpointId:
                type: string
                pattern: ^whe_
              status:
                type: string
                enum:
                - PENDING
    PaymentSucceededEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: payment.succeeded
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Payment'
    PaymentFailedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: payment.failed
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Payment'
    PaymentRefundedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: payment.refunded
          data:
            type: object
            required:
            - object
            - refund
            properties:
              object:
                $ref: '#/components/schemas/Payment'
              refund:
                $ref: '#/components/schemas/Refund'
    ChargebackReceivedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: chargeback.received
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Chargeback'
    WithdrawalCompletedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: withdrawal.completed
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Withdrawal'
    WithdrawalFailedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: withdrawal.failed
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Withdrawal'
    SettlementClosedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: settlement.closed
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/Settlement'
    WebhookEndpointDisabledEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: webhook_endpoint.disabled
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/WebhookEndpoint'
    PingEvent:
      type: object
      description: Envelope do `ping`; o `data` do `ping` não está definido no TDD (pergunta em aberto).
      required:
      - id
      - type
      properties:
        id:
          type: string
          pattern: ^evt_
        type:
          const: ping
        apiVersion:
          type: string
        createdAt:
          type: string
          format: date-time
        environment:
          type: string
          enum:
          - sandbox
          - production
        merchantId:
          type:
          - string
          - 'null'
        data:
          type: object
    ContractTemplateList:
      allOf:
      - $ref: '#/components/schemas/ListEnvelope'
      - type: object
        properties:
          data:
            type: array
            items:
              $ref: '#/components/schemas/ContractTemplate'
    ListEnvelope:
      type: object
      description: Envelope de toda lista. Cada domínio fixa o tipo dos itens de data.
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          description: Itens no mesmo formato do GET do recurso.
          items: {}
        hasMore:
          type: boolean
          description: Há página seguinte.
        nextCursor:
          type:
          - string
          - 'null'
          description: Cursor opaco da página seguinte; null quando hasMore é false.
      additionalProperties: false
    ContractTemplate:
      type: object
      required:
      - id
      - name
      - description
      - isReadyToIssue
      - tags
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: ctt_ + 32 hexadecimais.
        name:
          type: string
        description:
          type:
          - string
          - 'null'
        isReadyToIssue:
          type: boolean
        tags:
          type: array
          items:
            $ref: '#/components/schemas/ContractTemplateTag'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      additionalProperties: false
    ContractTemplateTag:
      type: object
      required:
      - tag
      - label
      - inputType
      - requiredForSigner
      properties:
        tag:
          type: string
        label:
          type:
          - string
          - 'null'
        inputType:
          type:
          - string
          - 'null'
        requiredForSigner:
          type: boolean
      additionalProperties: false
    ContractTemplateUpload:
      type: object
      required:
      - file
      properties:
        file:
          type: string
          format: binary
          description: .docx com tags {{tag}}.
        name:
          type: string
          maxLength: 255
        description:
          type: string
          maxLength: 1000
    ContractTemplateAnalysis:
      type: object
      required:
      - fileName
      - sizeBytes
      - sha256
      - tags
      - existingTemplateId
      properties:
        fileName:
          type: string
        sizeBytes:
          type: integer
        sha256:
          type: string
        tags:
          type: array
          items:
            type: string
        existingTemplateId:
          type:
          - string
          - 'null'
      additionalProperties: false
    ContractFileList:
      allOf:
      - $ref: '#/components/schemas/ListEnvelope'
      - type: object
        properties:
          data:
            type: array
            items:
              $ref: '#/components/schemas/ContractFile'
    ContractFile:
      type: object
      required:
      - id
      - name
      - description
      - fileName
      - sizeBytes
      - sha256
      - createdAt
      properties:
        id:
          type: string
          description: cfl_ + 32 hexadecimais.
        name:
          type: string
        description:
          type:
          - string
          - 'null'
        fileName:
          type: string
        sizeBytes:
          type: integer
        sha256:
          type: string
        createdAt:
          type: string
          format: date-time
      additionalProperties: false
    ContractFileUpload:
      type: object
      required:
      - file
      properties:
        file:
          type: string
          format: binary
          description: PDF sem senha.
        name:
          type: string
          maxLength: 255
        description:
          type: string
          maxLength: 1000
    CreateContractRequest:
      type: object
      required:
      - signers
      description: Exatamente um de contractTemplateId e contractFileId.
      oneOf:
      - required:
        - contractTemplateId
      - required:
        - contractFileId
      properties:
        contractTemplateId:
          type: string
          description: ctt_ de um modelo pronto.
        contractFileId:
          type: string
          description: cfl_ de um PDF da biblioteca.
        name:
          type: string
          maxLength: 255
        description:
          type: string
          maxLength: 1000
        signers:
          type: array
          minItems: 1
          maxItems: 10
          items:
            $ref: '#/components/schemas/ContractSignerInput'
        tagValues:
          type: array
          description: Só com contractTemplateId.
          items:
            $ref: '#/components/schemas/ContractTagValue'
        expiresAt:
          type: string
          format: date-time
          description: 'Padrão: 30 dias.'
        allowRefusal:
          type: boolean
          default: false
        advancedAuth:
          type: boolean
          default: false
      additionalProperties: false
    ContractSignerInput:
      type: object
      required:
      - name
      - email
      - phone
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        email:
          type: string
          format: email
          maxLength: 255
        phone:
          $ref: '#/components/schemas/Phone'
        cpf:
          type: string
          pattern: ^[0-9]{11}$
          description: Obrigatório com authentication.advancedCpf.
        role:
          type: string
          enum:
          - payer
          - merchant
          - witness
          - guarantor
          - other
        authentication:
          $ref: '#/components/schemas/ContractSignerAuthentication'
      additionalProperties: false
    ContractSignerAuthentication:
      type: object
      description: Exigências de autenticação na assinatura. Sem o objeto, vale a assinatura na tela.
      properties:
        screenSignature:
          type: boolean
        emailToken:
          type: boolean
        selfie:
          type: boolean
        documentPhoto:
          type: boolean
        simpleCpf:
          type: boolean
          description: Pede o CPF.
        advancedCpf:
          type: boolean
          description: Confere o CPF na Receita; exige cpf.
      additionalProperties: false
    ContractTagValue:
      type: object
      required:
      - tag
      - value
      properties:
        tag:
          type: string
        value:
          type:
          - string
          - 'null'
      additionalProperties: false
    Contract:
      type: object
      required:
      - id
      - status
      - name
      - description
      - contractTemplateId
      - contractFileId
      - signersExpected
      - signersSigned
      - requiredSignersExpected
      - requiredSignersSigned
      - allowRefusal
      - advancedAuth
      - refusedReason
      - expiresAt
      - effectiveAt
      - signedAt
      - canceledAt
      - hasSignedFile
      - tagValues
      - signers
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: ctr_ + 32 hexadecimais.
        status:
          $ref: '#/components/schemas/ContractStatus'
        name:
          type: string
        description:
          type:
          - string
          - 'null'
        contractTemplateId:
          type:
          - string
          - 'null'
        contractFileId:
          type:
          - string
          - 'null'
        signersExpected:
          type: integer
        signersSigned:
          type: integer
        requiredSignersExpected:
          type: integer
        requiredSignersSigned:
          type: integer
        allowRefusal:
          type: boolean
        advancedAuth:
          type: boolean
        refusedReason:
          type:
          - string
          - 'null'
        expiresAt:
          type:
          - string
          - 'null'
          format: date-time
        effectiveAt:
          type:
          - string
          - 'null'
          format: date-time
        signedAt:
          type:
          - string
          - 'null'
          format: date-time
        canceledAt:
          type:
          - string
          - 'null'
          format: date-time
        hasSignedFile:
          type: boolean
        tagValues:
          type: array
          items:
            $ref: '#/components/schemas/ContractTagValue'
        signers:
          type: array
          items:
            $ref: '#/components/schemas/ContractSigner'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      additionalProperties: false
    ContractStatus:
      type: string
      description: draft só existe durante a emissão; signed, refused, expired e canceled são finais.
      enum:
      - draft
      - pending_signature
      - viewed
      - partially_signed
      - signed
      - refused
      - expired
      - canceled
    ContractSigner:
      type: object
      required:
      - id
      - sequence
      - role
      - isRequired
      - name
      - email
      - status
      - timesViewed
      - lastViewedAt
      - signedAt
      - refusedAt
      - signUrl
      properties:
        id:
          type: string
        sequence:
          type: integer
        role:
          $ref: '#/components/schemas/ContractSignerRole'
        isRequired:
          type: boolean
        name:
          type: string
        email:
          type: string
        status:
          type: string
          enum:
          - new
          - viewed
          - signed
          - refused
        timesViewed:
          type: integer
        lastViewedAt:
          type:
          - string
          - 'null'
          format: date-time
        signedAt:
          type:
          - string
          - 'null'
          format: date-time
        refusedAt:
          type:
          - string
          - 'null'
          format: date-time
        signUrl:
          type:
          - string
          - 'null'
          format: uri
          description: Link da Kodano para assinar; null depois de assinar ou recusar e com o contrato encerrado.
      additionalProperties: false
    ContractSignerRole:
      type: string
      enum:
      - payer
      - merchant
      - witness
      - guarantor
      - other
    ContractList:
      allOf:
      - $ref: '#/components/schemas/ListEnvelope'
      - type: object
        properties:
          data:
            type: array
            items:
              $ref: '#/components/schemas/ContractSummary'
    ContractSummary:
      type: object
      required:
      - id
      - status
      - name
      - description
      - contractTemplateId
      - signersExpected
      - signersSigned
      - expiresAt
      - effectiveAt
      - signedAt
      - hasSignedFile
      - createdAt
      properties:
        id:
          type: string
        status:
          $ref: '#/components/schemas/ContractStatus'
        name:
          type: string
        description:
          type:
          - string
          - 'null'
        contractTemplateId:
          type:
          - string
          - 'null'
        signersExpected:
          type: integer
        signersSigned:
          type: integer
        expiresAt:
          type:
          - string
          - 'null'
          format: date-time
        effectiveAt:
          type:
          - string
          - 'null'
          format: date-time
        signedAt:
          type:
          - string
          - 'null'
          format: date-time
        hasSignedFile:
          type: boolean
        createdAt:
          type: string
          format: date-time
      additionalProperties: false
    CancelContractRequest:
      type: object
      properties:
        reason:
          type: string
          maxLength: 500
      additionalProperties: false
    ContractSignedFile:
      type: object
      required:
      - url
      - expiresAt
      properties:
        url:
          type: string
          format: uri
        expiresAt:
          type: string
          format: date-time
      additionalProperties: false
    UpdateContractSignerRequest:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        email:
          type: string
          format: email
          maxLength: 255
        phone:
          $ref: '#/components/schemas/Phone'
        cpf:
          type: string
          pattern: ^[0-9]{11}$
        authentication:
          $ref: '#/components/schemas/ContractSignerAuthentication'
      additionalProperties: false
    ContractSignerResent:
      type: object
      required:
      - signerId
      - notifiedAt
      properties:
        signerId:
          type: string
        notifiedAt:
          type: string
          format: date-time
      additionalProperties: false
    CreateDepositRequest:
      type: object
      required:
      - contractId
      - amount
      - currency
      - coverageStartsAt
      - coverageEndsAt
      properties:
        contractId:
          type: string
          description: ctr_ do contrato do merchant.
        customerId:
          type: string
          description: cus_; sem ele, payer é obrigatório.
        payer:
          $ref: '#/components/schemas/DepositPayer'
        name:
          type: string
          maxLength: 255
        description:
          type: string
          maxLength: 255
        amount:
          type: integer
          format: int64
          minimum: 1
          description: Centavos.
        currency:
          $ref: '#/components/schemas/Currency'
        coverageStartsAt:
          type: string
          format: date-time
        coverageEndsAt:
          type: string
          format: date-time
          description: Depois de coverageStartsAt.
        autoRenew:
          type: boolean
          default: false
      additionalProperties: false
    DepositPayer:
      type: object
      required:
      - name
      - document
      - email
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        document:
          $ref: '#/components/schemas/Document'
        email:
          type: string
          format: email
          maxLength: 255
        phone:
          $ref: '#/components/schemas/Phone'
      additionalProperties: false
    Deposit:
      type: object
      required:
      - id
      - status
      - mode
      - autoRenew
      - contractId
      - customerId
      - name
      - description
      - amount
      - currency
      - coverageStartsAt
      - coverageEndsAt
      - guaranteeEndsAt
      - checkoutUrl
      - card
      - authorizationExpiresAt
      - nextRenewalAttemptAt
      - unsecuredReason
      - feeAmount
      - capturedAmount
      - refundAmount
      - damageDescription
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: dpst_ + 27 letras e dígitos.
        status:
          $ref: '#/components/schemas/DepositStatus'
        mode:
          $ref: '#/components/schemas/DepositMode'
        autoRenew:
          type: boolean
        contractId:
          type: string
        customerId:
          type:
          - string
          - 'null'
          description: null até a autorização, quando a caução foi aberta com payer.
        name:
          type:
          - string
          - 'null'
        description:
          type:
          - string
          - 'null'
        amount:
          type: integer
          format: int64
        currency:
          $ref: '#/components/schemas/Currency'
        coverageStartsAt:
          type: string
          format: date-time
        coverageEndsAt:
          type: string
          format: date-time
        guaranteeEndsAt:
          type:
          - string
          - 'null'
          format: date-time
          description: Até quando a autorização vigente cobre a caução.
        checkoutUrl:
          type:
          - string
          - 'null'
          format: uri
          description: Só em AWAITING_AUTHORIZATION.
        card:
          $ref: '#/components/schemas/DepositCard'
        authorizationExpiresAt:
          type:
          - string
          - 'null'
          format: date-time
        nextRenewalAttemptAt:
          type:
          - string
          - 'null'
          format: date-time
        unsecuredReason:
          type:
          - string
          - 'null'
          enum:
          - RENEWAL_DENIED
          - EXPIRED_AS_PLANNED
          - null
        feeAmount:
          type:
          - integer
          - 'null'
          format: int64
        capturedAmount:
          type:
          - integer
          - 'null'
          format: int64
        refundAmount:
          type:
          - integer
          - 'null'
          format: int64
        damageDescription:
          type:
          - string
          - 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      additionalProperties: false
    DepositStatus:
      type: string
      enum:
      - AWAITING_AUTHORIZATION
      - ACTIVE
      - RENEWAL_AT_RISK
      - UNSECURED
      - RELEASED
      - CAPTURED
      - REFUNDED
      description: "AWAITING_AUTHORIZATION: aguardando o pagador autorizar o cartão no checkoutUrl.\nACTIVE: valor reservado no cartão. RENEWAL_AT_RISK: a renovação automática falhou e será tentada de novo.\nUNSECURED: sem autorização válida (renovação negada ou cobertura encerrada sem renovar). RELEASED, CAPTURED e\nREFUNDED são finais."
    DepositMode:
      type: string
      enum:
      - PREAUTH_RENEWAL
      - PREAUTH_FEE_UPFRONT
      - IMMEDIATE_CHARGE
      description: "PREAUTH_RENEWAL: reserva renovada no cartão, tarifa depois. PREAUTH_FEE_UPFRONT: reserva\nrenovada, tarifa cobrada na abertura. IMMEDIATE_CHARGE: o valor é cobrado na abertura e devolvido no encerramento."
    DepositCard:
      type:
      - object
      - 'null'
      required:
      - brand
      - lastFour
      properties:
        brand:
          type: string
        lastFour:
          type: string
          pattern: ^[0-9]{4}$
      additionalProperties: false
    DepositList:
      allOf:
      - $ref: '#/components/schemas/ListEnvelope'
      - type: object
        properties:
          data:
            type: array
            items:
              $ref: '#/components/schemas/Deposit'
    DepositEvidenceUploadsRequest:
      type: object
      required:
      - files
      properties:
        files:
          type: array
          minItems: 1
          maxItems: 20
          items:
            type: object
            required:
            - fileName
            - contentType
            properties:
              fileName:
                type: string
                maxLength: 255
              contentType:
                type: string
                enum:
                - image/png
                - image/jpeg
                - image/heic
                - image/heif
            additionalProperties: false
      additionalProperties: false
    DepositEvidenceUploads:
      type: object
      required:
      - uploads
      properties:
        uploads:
          type: array
          items:
            type: object
            required:
            - fileName
            - uploadUrl
            - objectKey
            - expiresAt
            properties:
              fileName:
                type: string
              uploadUrl:
                type: string
                format: uri
              objectKey:
                type: string
              expiresAt:
                type: string
                format: date-time
            additionalProperties: false
      additionalProperties: false
    CaptureDepositRequest:
      type: object
      required:
      - damageDescription
      - evidence
      properties:
        amount:
          type: integer
          format: int64
          minimum: 1
          description: Centavos a cobrar; ausente = o valor inteiro.
        damageDescription:
          type: string
          minLength: 1
          maxLength: 2000
        evidence:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/DepositEvidence'
      additionalProperties: false
    DepositEvidence:
      type: object
      required:
      - objectKey
      - fileName
      - contentType
      properties:
        objectKey:
          type: string
        fileName:
          type: string
          maxLength: 255
        contentType:
          type: string
          enum:
          - image/png
          - image/jpeg
          - image/heic
          - image/heif
      additionalProperties: false
    FxMoney:
      type: object
      required:
      - amount
      - currency
      properties:
        amount:
          type: integer
          minimum: 0
          description: 'Menor unidade da moeda: centavos de real (`BRL`) ou de dólar (`USD`).'
        currency:
          type: string
          enum:
          - BRL
          - USD
    FxProduct:
      type: string
      enum:
      - SPOT
      description: Câmbio à vista. Único valor hoje.
    FxDirection:
      type: string
      enum:
      - BRL_TO_USD
      description: Do real para o dólar. Única direção hoje.
    FxRail:
      type: string
      enum:
      - ACH
      - WIRE
      - SWIFT
      - SEPA
      description: Meio de entrega do dólar na conta de destino.
    FxPayoutAccountStatus:
      type: string
      enum:
      - ACTIVE
      - PENDING_DOCUMENT
      - INACTIVE
    FxOperationStatus:
      type: string
      enum:
      - UNKNOWN
      - AWAITING_PAYMENT
      - PAYMENT_RECEIVED
      - CONVERTING
      - PAYOUT_IN_PROGRESS
      - SETTLED
      - EXPIRED
      - FAILED
      - ACTION_REQUIRED
      - REFUND_PENDING
      - REFUNDED
      - REFUND_FAILED
      description: '`UNKNOWN`: a Kodano ainda não confirmou a criação. `AWAITING_PAYMENT`: Pix à espera do pagamento. `PAYMENT_RECEIVED`, `CONVERTING`, `PAYOUT_IN_PROGRESS`: em andamento. `SETTLED`: dólar entregue (terminal). `EXPIRED`: Pix não pago no prazo (terminal). `FAILED`: falha sem dinheiro recebido (terminal). `ACTION_REQUIRED`: pagamento recebido sem entrega, a Kodano abre a devolução. `REFUND_PENDING`, `REFUNDED` (terminal), `REFUND_FAILED`: devolução.'
    FxFee:
      type: object
      required:
      - type
      - rail
      - amount
      - reference
      properties:
        type:
          type: string
          enum:
          - COVER_FEE
          description: Tarifa do rail de entrega.
        rail:
          $ref: '#/components/schemas/FxRail'
        amount:
          $ref: '#/components/schemas/FxMoney'
        reference:
          oneOf:
          - $ref: '#/components/schemas/FxMoney'
          - type: 'null'
          description: Tarifa na moeda em que o rail a cobra, quando difere da moeda paga.
    FxQuotePreviewRequest:
      type: object
      required:
      - product
      - direction
      - amount
      - amountSide
      properties:
        product:
          $ref: '#/components/schemas/FxProduct'
        direction:
          $ref: '#/components/schemas/FxDirection'
        amount:
          $ref: '#/components/schemas/FxMoney'
        amountSide:
          type: string
          enum:
          - PAYIN
          - PAYOUT
          description: '`PAYIN`: `amount` é o que o pagador paga em reais. `PAYOUT`: `amount` é o que chega em dólar.'
    FxQuotePreview:
      type: object
      required:
      - product
      - direction
      - rate
      - payin
      - payoutEstimate
      - indicative
      - expiresAt
      properties:
        product:
          $ref: '#/components/schemas/FxProduct'
        direction:
          $ref: '#/components/schemas/FxDirection'
        rate:
          type: string
          description: Reais por dólar, decimal em string, já com o spread da Kodano.
          example: '5.2392'
        payin:
          $ref: '#/components/schemas/FxMoney'
        payoutEstimate:
          $ref: '#/components/schemas/FxMoney'
        indicative:
          type: boolean
          enum:
          - true
        expiresAt:
          type: string
          format: date-time
    CreateFxQuoteRequest:
      type: object
      required:
      - product
      - direction
      - amount
      - amountSide
      - payoutRail
      properties:
        product:
          $ref: '#/components/schemas/FxProduct'
        direction:
          $ref: '#/components/schemas/FxDirection'
        amount:
          $ref: '#/components/schemas/FxMoney'
        amountSide:
          type: string
          enum:
          - PAYIN
          - PAYOUT
        payoutRail:
          $ref: '#/components/schemas/FxRail'
    FxQuote:
      type: object
      required:
      - id
      - status
      - product
      - direction
      - payoutRail
      - rate
      - payin
      - payout
      - fees
      - totalPayin
      - indicative
      - expiresAt
      properties:
        id:
          type: string
          pattern: ^fxq_[0-9a-f]{32}$
        status:
          type: string
          enum:
          - QUOTED
        product:
          $ref: '#/components/schemas/FxProduct'
        direction:
          $ref: '#/components/schemas/FxDirection'
        payoutRail:
          $ref: '#/components/schemas/FxRail'
        rate:
          type: string
          description: Reais por dólar, decimal em string.
        payin:
          $ref: '#/components/schemas/FxMoney'
        payout:
          $ref: '#/components/schemas/FxMoney'
        fees:
          type: array
          items:
            $ref: '#/components/schemas/FxFee'
        totalPayin:
          $ref: '#/components/schemas/FxMoney'
          description: '`payin` mais as tarifas em reais: o que o pagador paga.'
        indicative:
          type: boolean
          enum:
          - true
        expiresAt:
          type: string
          format: date-time
    FxPayoutAccountRef:
      type: object
      required:
      - id
      - label
      - rail
      properties:
        id:
          type: string
          pattern: ^fxa_[0-9a-f]{32}$
        label:
          type: string
        rail:
          $ref: '#/components/schemas/FxRail'
    CreateFxOperationRequest:
      type: object
      required:
      - quoteId
      - payoutAccountId
      - authorization
      properties:
        quoteId:
          type: string
          pattern: ^fxq_[0-9a-f]{32}$
        payoutAccountId:
          type: string
          pattern: ^fxa_[0-9a-f]{32}$
        customerReference:
          oneOf:
          - type: string
            maxLength: 64
          - type: 'null'
          description: Rótulo livre seu, até 64 caracteres, devolvido tal qual e aceito como filtro da lista. Não coloque dado pessoal.
        authorization:
          type: object
          required:
          - accepted
          - termsVersion
          properties:
            accepted:
              type: boolean
              description: Precisa ser `true`; senão 422 `FX.AUTHORIZATION_REQUIRED`.
            termsVersion:
              type: string
              example: global-spot-2026-10
              description: Versão vigente dos termos do câmbio à vista.
    FxPaymentInstructions:
      type: object
      required:
      - rail
      - pixCopyPaste
      - amount
      - expiresAt
      properties:
        rail:
          type: string
          enum:
          - PIX
        pixCopyPaste:
          type: string
          description: Pix copia e cola.
        amount:
          $ref: '#/components/schemas/FxMoney'
        expiresAt:
          oneOf:
          - type: string
            format: date-time
          - type: 'null'
    FxTimelineItem:
      type: object
      required:
      - status
      - at
      properties:
        status:
          $ref: '#/components/schemas/FxOperationStatus'
        at:
          type: string
          format: date-time
    FxOperation:
      type: object
      required:
      - id
      - type
      - status
      - direction
      - rate
      - payin
      - payout
      - fees
      - repricedFromQuote
      - payoutAccount
      - customerReference
      - paymentInstructions
      - timeline
      - supportReference
      - receiptAvailable
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^fxo_[0-9a-f]{32}$
        type:
          $ref: '#/components/schemas/FxProduct'
        status:
          $ref: '#/components/schemas/FxOperationStatus'
        direction:
          $ref: '#/components/schemas/FxDirection'
        rate:
          oneOf:
          - type: string
          - type: 'null'
          description: Reais por dólar da operação; nulo em `UNKNOWN`.
        payin:
          oneOf:
          - $ref: '#/components/schemas/FxMoney'
          - type: 'null'
        payout:
          oneOf:
          - $ref: '#/components/schemas/FxMoney'
          - type: 'null'
        fees:
          type: array
          items:
            $ref: '#/components/schemas/FxFee'
        repricedFromQuote:
          type: boolean
          description: '`true` quando os valores da operação diferem da cotação.'
        payoutAccount:
          oneOf:
          - $ref: '#/components/schemas/FxPayoutAccountRef'
          - type: 'null'
        customerReference:
          oneOf:
          - type: string
          - type: 'null'
        paymentInstructions:
          oneOf:
          - $ref: '#/components/schemas/FxPaymentInstructions'
          - type: 'null'
          description: Presente enquanto a operação espera o pagamento (`AWAITING_PAYMENT`); nulo depois.
        timeline:
          type: array
          items:
            $ref: '#/components/schemas/FxTimelineItem'
        supportReference:
          type: string
          description: Código curto para falar com o suporte da Kodano.
        receiptAvailable:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    FxOperationSummary:
      type: object
      required:
      - id
      - type
      - status
      - direction
      - rate
      - payin
      - payout
      - customerReference
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          pattern: ^fxo_[0-9a-f]{32}$
        type:
          $ref: '#/components/schemas/FxProduct'
        status:
          $ref: '#/components/schemas/FxOperationStatus'
        direction:
          $ref: '#/components/schemas/FxDirection'
        rate:
          oneOf:
          - type: string
          - type: 'null'
        payin:
          oneOf:
          - $ref: '#/components/schemas/FxMoney'
          - type: 'null'
        payout:
          oneOf:
          - $ref: '#/components/schemas/FxMoney'
          - type: 'null'
        customerReference:
          oneOf:
          - type: string
          - type: 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    FxOperationList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/FxOperationSummary'
        hasMore:
          type: boolean
        nextCursor:
          oneOf:
          - type: string
          - type: 'null'
    FxReceipt:
      type: object
      required:
      - url
      - expiresAt
      properties:
        url:
          type: string
          format: uri
        expiresAt:
          type: string
          format: date-time
    FxAddress:
      type: object
      required:
      - line1
      - city
      - country
      - postalCode
      properties:
        line1:
          type: string
        line2:
          oneOf:
          - type: string
          - type: 'null'
        city:
          type: string
        stateProvinceRegion:
          oneOf:
          - type: string
          - type: 'null'
        country:
          type: string
          description: ISO 3166-1 alfa-2.
          example: US
        postalCode:
          type: string
    CreateFxPayoutAccountRequest:
      type: object
      required:
      - rail
      - displayName
      - holder
      properties:
        rail:
          $ref: '#/components/schemas/FxRail'
        displayName:
          type: string
          maxLength: 80
        holder:
          type: object
          required:
          - name
          - accountClass
          properties:
            name:
              type: string
            accountClass:
              type: string
              enum:
              - BUSINESS
              - INDIVIDUAL
            relationship:
              type: string
              description: 'Relação com o titular, ex.: `VENDOR_OR_SUPPLIER`.'
            businessIndustryNaics:
              type: string
              description: Código NAICS, para titular empresa.
        bank:
          type: object
          description: ACH e WIRE.
          required:
          - name
          - routingNumber
          - accountNumber
          - accountType
          properties:
            name:
              type: string
            routingNumber:
              type: string
              pattern: ^[0-9]{9}$
            accountNumber:
              type: string
            accountType:
              type: string
              enum:
              - CHECKING
              - SAVINGS
        swift:
          type: object
          description: SWIFT.
          required:
          - bic
          - accountNumberOrIban
          properties:
            bic:
              type: string
              pattern: ^[A-Z0-9]{8}([A-Z0-9]{3})?$
            accountNumberOrIban:
              type: string
            paymentCode:
              oneOf:
              - type: string
              - type: 'null'
        sepa:
          type: object
          description: SEPA.
          required:
          - iban
          properties:
            iban:
              type: string
            bic:
              oneOf:
              - type: string
              - type: 'null'
        address:
          $ref: '#/components/schemas/FxAddress'
          description: Endereço do titular (ACH, WIRE e SEPA).
        beneficiaryAddress:
          $ref: '#/components/schemas/FxAddress'
          description: Endereço do beneficiário (SWIFT).
        bankAddress:
          allOf:
          - $ref: '#/components/schemas/FxAddress'
          - type: object
            properties:
              name:
                type: string
          description: Endereço do banco (SWIFT).
    FxPayoutAccount:
      type: object
      required:
      - id
      - displayName
      - rail
      - currency
      - holderName
      - bankName
      - maskedAccount
      - label
      - status
      - createdAt
      properties:
        id:
          type: string
          pattern: ^fxa_[0-9a-f]{32}$
        displayName:
          type: string
        rail:
          $ref: '#/components/schemas/FxRail'
        currency:
          type: string
          enum:
          - USD
        holderName:
          type: string
        bankName:
          oneOf:
          - type: string
          - type: 'null'
        maskedAccount:
          type: string
          description: Só os últimos 4 dígitos.
        label:
          type: string
        status:
          $ref: '#/components/schemas/FxPayoutAccountStatus'
        createdAt:
          type: string
          format: date-time
    FxPayoutAccountList:
      type: object
      required:
      - data
      - hasMore
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/FxPayoutAccount'
        hasMore:
          type: boolean
        nextCursor:
          oneOf:
          - type: string
          - type: 'null'
    FxPayoutAccountDocumentUpload:
      type: object
      required:
      - type
      - file
      properties:
        type:
          type: string
          enum:
          - INVOICE
          - PURCHASE_ORDER
          - DELIVERY_SLIP
          - CONTRACT
          - CUSTOMS_DECLARATION
          - BILL_OF_LADING
          - OTHER
        file:
          type: string
          format: binary
          description: PDF, JPEG ou PNG, até 8 MB.
    FxOperationSettledEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: fx.operation.settled
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/FxOperation'
    FxOperationExpiredEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: fx.operation.expired
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/FxOperation'
    FxOperationFailedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: fx.operation.failed
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/FxOperation'
    FxOperationActionRequiredEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: fx.operation.action_required
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/FxOperation'
    FxOperationRefundedEvent:
      allOf:
      - $ref: '#/components/schemas/Event'
      - type: object
        properties:
          type:
            const: fx.operation.refunded
          data:
            type: object
            required:
            - object
            properties:
              object:
                $ref: '#/components/schemas/FxOperation'
  examples:
    AUTH.TOKEN_INVALID:
      summary: AUTH.TOKEN_INVALID
      value:
        type: https://api.kodano.com.br/problems/auth-token-invalid
        title: Invalid or missing access token
        status: 401
        detail: The access token is missing, invalid or expired
        code: AUTH.TOKEN_INVALID
        correlationId: 5b0e7c1a-3d2f-4e8b-9a6c-1f2e3d4c5b6a
        errors: []
    AUTH.TENANT_MISMATCH:
      summary: AUTH.TENANT_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/auth-tenant-mismatch
        title: Tenant mismatch
        status: 403
        detail: The token organization does not match the credential
        code: AUTH.TENANT_MISMATCH
        correlationId: erp-7f3a-0002
    AUTH.ENVIRONMENT_MISMATCH:
      summary: AUTH.ENVIRONMENT_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/auth-environment-mismatch
        title: Environment mismatch
        status: 403
        detail: The credential belongs to another environment
        code: AUTH.ENVIRONMENT_MISMATCH
        correlationId: erp-7f3a-0002
    AUTH.ENTITLEMENT_MISSING:
      summary: AUTH.ENTITLEMENT_MISSING
      value:
        type: https://api.kodano.com.br/problems/auth-entitlement-missing
        title: Product not enabled for this credential
        status: 403
        detail: Scope payments:boleto:write is not granted
        code: AUTH.ENTITLEMENT_MISSING
        correlationId: erp-7f3a-0002
    AUTH.CONTEXT_MISSING:
      summary: AUTH.CONTEXT_MISSING
      value:
        type: https://api.kodano.com.br/problems/auth-context-missing
        title: Authorization context missing
        status: 403
        detail: The application authorization context is missing or incomplete
        code: AUTH.CONTEXT_MISSING
        correlationId: erp-7f3a-0002
    AUTH.TEMPORARILY_UNAVAILABLE:
      summary: AUTH.TEMPORARILY_UNAVAILABLE
      value:
        type: https://api.kodano.com.br/problems/auth-temporarily-unavailable
        title: Authorization temporarily unavailable
        status: 503
        detail: The authorization service is temporarily unavailable
        code: AUTH.TEMPORARILY_UNAVAILABLE
        correlationId: erp-7f3a-0002
    MERCHANT.HEADER_REQUIRED:
      summary: MERCHANT.HEADER_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/merchant-header-required
        title: X-Merchant-Id header is required
        status: 400
        detail: This route requires X-Merchant-Id for organization credentials
        code: MERCHANT.HEADER_REQUIRED
        correlationId: erp-7f3a-0002
    MERCHANT.FORBIDDEN:
      summary: MERCHANT.FORBIDDEN
      value:
        type: https://api.kodano.com.br/problems/merchant-forbidden
        title: Merchant not accessible
        status: 403
        detail: X-Merchant-Id is not accessible to this credential
        code: MERCHANT.FORBIDDEN
        correlationId: erp-7f3a-0002
    ROUTE.NOT_FOUND:
      summary: ROUTE.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/route-not-found
        title: Route not found
        status: 404
        detail: The requested route does not exist
        code: ROUTE.NOT_FOUND
        correlationId: erp-7f3a-0002
    RATE_LIMIT.EXCEEDED:
      summary: RATE_LIMIT.EXCEEDED
      value:
        type: https://api.kodano.com.br/problems/rate-limit-exceeded
        title: Too many requests
        status: 429
        detail: Request rate limit exceeded for this credential
        code: RATE_LIMIT.EXCEEDED
        correlationId: 9d4c2b1a-7e6f-4a5b-8c3d-2e1f0a9b8c7d
        errors: []
    INTERNAL.ERROR:
      summary: INTERNAL.ERROR
      value:
        type: https://api.kodano.com.br/problems/internal-error
        title: Internal error
        status: 500
        detail: An unexpected error occurred
        code: INTERNAL.ERROR
        correlationId: erp-7f3a-0002
    IDEMPOTENCY.KEY_REQUIRED:
      summary: IDEMPOTENCY.KEY_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/idempotency-key-required
        title: Idempotency-Key header is required
        status: 422
        detail: Idempotency-Key must be a UUID
        code: IDEMPOTENCY.KEY_REQUIRED
        correlationId: erp-7f3a-0014
    IDEMPOTENCY.CONFLICT:
      summary: IDEMPOTENCY.CONFLICT
      value:
        type: https://api.kodano.com.br/problems/idempotency-conflict
        title: Idempotency key reused with a different request
        status: 409
        detail: Idempotency-Key 4b2e8c1a-9d7f-4e3b-a6c5-0f1e2d3c4b5a was used with a different body or path
        code: IDEMPOTENCY.CONFLICT
        correlationId: erp-7f3a-0012
    IDEMPOTENCY.IN_PROGRESS:
      summary: IDEMPOTENCY.IN_PROGRESS
      value:
        type: https://api.kodano.com.br/problems/idempotency-in-progress
        title: Request in progress
        status: 409
        detail: A request with this Idempotency-Key is still being processed
        code: IDEMPOTENCY.IN_PROGRESS
        correlationId: erp-7f3a-0013
        resourceId: pay_7Hq2LmX9vB4nR8sT1wK6yZ3cP0aD
    REQUEST.TOO_LARGE:
      summary: REQUEST.TOO_LARGE
      value:
        type: https://api.kodano.com.br/problems/request-too-large
        title: Request too large
        status: 413
        detail: The request body exceeds 1 MB
        code: REQUEST.TOO_LARGE
        correlationId: erp-7f3a-0002
    VALIDATION.FAILED:
      summary: VALIDATION.FAILED
      value:
        type: https://api.kodano.com.br/problems/validation-failed
        title: Request validation failed
        status: 422
        detail: One or more fields are invalid
        code: VALIDATION.FAILED
        correlationId: erp-7f3a-0001
        errors:
        - field: amount
          code: VALIDATION.MIN
          message: must be >= 100
    CURSOR.INVALID:
      summary: CURSOR.INVALID
      value:
        type: https://api.kodano.com.br/problems/cursor-invalid
        title: Invalid cursor
        status: 422
        detail: The cursor is invalid or was issued for different filters
        code: CURSOR.INVALID
        correlationId: erp-7f3a-0002
    MERCHANT.NOT_CONFIGURED:
      summary: MERCHANT.NOT_CONFIGURED
      value:
        type: https://api.kodano.com.br/problems/merchant-not-configured
        title: Merchant not configured
        status: 422
        detail: The merchant is not configured for this payment method
        code: MERCHANT.NOT_CONFIGURED
        correlationId: erp-7f3a-0002
    CUSTOMER.NOT_FOUND:
      summary: CUSTOMER.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/customer-not-found
        title: Customer not found
        status: 422
        detail: customer.id does not exist for this merchant
        code: CUSTOMER.NOT_FOUND
        correlationId: erp-7f3a-0002
    CUSTOMER.ADDRESS_REQUIRED:
      summary: CUSTOMER.ADDRESS_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/customer-address-required
        title: Customer address required
        status: 422
        detail: The customer has no address registered
        code: CUSTOMER.ADDRESS_REQUIRED
        correlationId: erp-7f3a-0002
    CUSTOMER.PHONE_REQUIRED:
      summary: CUSTOMER.PHONE_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/customer-phone-required
        title: Customer phone required
        status: 422
        detail: The customer has no phone registered
        code: CUSTOMER.PHONE_REQUIRED
        correlationId: erp-7f3a-0002
    CUSTOMER.DOCUMENT_CONFLICT:
      summary: CUSTOMER.DOCUMENT_CONFLICT
      value:
        type: https://api.kodano.com.br/problems/customer-document-conflict
        title: Customer already exists
        status: 409
        detail: A customer with this document already exists for this merchant
        code: CUSTOMER.DOCUMENT_CONFLICT
        correlationId: 0192a8f4-6c1e-7b3a-9f2d-5e8c1a7b4d20
        existingCustomerId: cus_8kQ2mZ7xW1pL4nR0
    CUSTOMER.DOCUMENT_IMMUTABLE:
      summary: CUSTOMER.DOCUMENT_IMMUTABLE
      value:
        type: https://api.kodano.com.br/problems/customer-document-immutable
        title: Customer document is immutable
        status: 422
        detail: document and type cannot be changed
        code: CUSTOMER.DOCUMENT_IMMUTABLE
        correlationId: erp-7f3a-0002
    PAYMENT.PROVIDER_UNAVAILABLE:
      summary: PAYMENT.PROVIDER_UNAVAILABLE
      value:
        type: https://api.kodano.com.br/problems/payment-provider-unavailable
        title: Upstream dependency unavailable
        status: 503
        detail: The ledger service did not answer in time. Retry after the indicated delay.
        code: PAYMENT.PROVIDER_UNAVAILABLE
        correlationId: conc-20261016-0001
    RESOURCE.NOT_FOUND:
      summary: RESOURCE.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/resource-not-found
        title: Resource not found
        status: 404
        detail: The resource does not exist
        code: RESOURCE.NOT_FOUND
        correlationId: erp-7f3a-0002
    RESOURCE.CONFLICT:
      summary: RESOURCE.CONFLICT
      value:
        type: https://api.kodano.com.br/problems/resource-conflict
        title: Resource conflict
        status: 409
        detail: The resource was changed concurrently
        code: RESOURCE.CONFLICT
        correlationId: erp-7f3a-0002
    CARD.PAN_NOT_ALLOWED:
      summary: CARD.PAN_NOT_ALLOWED
      value:
        type: https://api.kodano.com.br/problems/card-pan-not-allowed
        title: Card number not allowed
        status: 422
        detail: PAN and CVV must never be sent to the API
        code: CARD.PAN_NOT_ALLOWED
        correlationId: erp-7f3a-0002
    CARD.TOKEN_FIELDS_REQUIRED:
      summary: CARD.TOKEN_FIELDS_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/card-token-fields-required
        title: Card token fields required
        status: 422
        detail: holderName, expiry and lastFour are required with card.token
        code: CARD.TOKEN_FIELDS_REQUIRED
        correlationId: erp-7f3a-0002
    CARD.PAYER_IP_REQUIRED:
      summary: CARD.PAYER_IP_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/card-payer-ip-required
        title: Payer IP required
        status: 422
        detail: browser.ip is required for customer-initiated payments
        code: CARD.PAYER_IP_REQUIRED
        correlationId: erp-7f3a-0002
    CARD.TOKEN_INVALID:
      summary: CARD.TOKEN_INVALID
      value:
        type: https://api.kodano.com.br/problems/card-token-invalid
        title: Invalid card token
        status: 422
        detail: The card token is expired, reused or invalid
        code: CARD.TOKEN_INVALID
        correlationId: erp-7f3a-0002
    CARD.INACTIVE:
      summary: CARD.INACTIVE
      value:
        type: https://api.kodano.com.br/problems/card-inactive
        title: Card inactive
        status: 422
        detail: The stored card is inactive
        code: CARD.INACTIVE
        correlationId: erp-7f3a-0002
    CARD.CUSTOMER_MISMATCH:
      summary: CARD.CUSTOMER_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/card-customer-mismatch
        title: Card customer mismatch
        status: 422
        detail: The card belongs to another customer
        code: CARD.CUSTOMER_MISMATCH
        correlationId: erp-7f3a-0002
    CARD.INSTALLMENTS_NOT_AVAILABLE:
      summary: CARD.INSTALLMENTS_NOT_AVAILABLE
      value:
        type: https://api.kodano.com.br/problems/card-installments-not-available
        title: Installments not available
        status: 422
        detail: The card brand has no rate for this number of installments
        code: CARD.INSTALLMENTS_NOT_AVAILABLE
        correlationId: erp-7f3a-0002
    CARD.PREAUTH_NOT_ENABLED:
      summary: CARD.PREAUTH_NOT_ENABLED
      value:
        type: https://api.kodano.com.br/problems/card-preauth-not-enabled
        title: Pre-authorization not enabled
        status: 422
        detail: capture=false requires pre-authorization enabled for the merchant
        code: CARD.PREAUTH_NOT_ENABLED
        correlationId: erp-7f3a-0002
    CARD.BRAND_UNSUPPORTED:
      summary: CARD.BRAND_UNSUPPORTED
      value:
        type: https://api.kodano.com.br/problems/card-brand-unsupported
        title: Card brand not supported
        status: 422
        detail: The card brand is not supported
        code: CARD.BRAND_UNSUPPORTED
        correlationId: erp-7f3a-0002
    CARD.IN_USE:
      summary: CARD.IN_USE
      value:
        type: https://api.kodano.com.br/problems/card-in-use
        title: Card in use
        status: 409
        detail: The card is used by an open deposit
        code: CARD.IN_USE
        correlationId: erp-7f3a-0002
    PAYMENT.NOT_FOUND:
      summary: PAYMENT.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/payment-not-found
        title: Payment not found
        status: 404
        detail: The payment does not exist
        code: PAYMENT.NOT_FOUND
        correlationId: erp-7f3a-0002
    PAYMENT.OPERATION_IN_PROGRESS:
      summary: PAYMENT.OPERATION_IN_PROGRESS
      value:
        type: https://api.kodano.com.br/problems/payment-operation-in-progress
        title: Payment operation in progress
        status: 409
        detail: Another capture, refund or cancellation is running for this payment
        code: PAYMENT.OPERATION_IN_PROGRESS
        correlationId: erp-7f3a-0002
    PAYMENT.NOT_CANCELABLE:
      summary: PAYMENT.NOT_CANCELABLE
      value:
        type: https://api.kodano.com.br/problems/payment-not-cancelable
        title: Payment cannot be canceled
        status: 422
        detail: Captured payments must be refunded with POST /v1/payments/{id}/refunds
        code: PAYMENT.NOT_CANCELABLE
        correlationId: erp-7f3a-0002
        reason: CAPTURED
    CAPTURE.NOT_AUTHORIZED:
      summary: CAPTURE.NOT_AUTHORIZED
      value:
        type: https://api.kodano.com.br/problems/capture-not-authorized
        title: No open authorization
        status: 409
        detail: The payment has no open authorization to capture
        code: CAPTURE.NOT_AUTHORIZED
        correlationId: erp-7f3a-0002
    CAPTURE.AUTHORIZATION_EXPIRED:
      summary: CAPTURE.AUTHORIZATION_EXPIRED
      value:
        type: https://api.kodano.com.br/problems/capture-authorization-expired
        title: Authorization expired
        status: 409
        detail: The authorization has expired
        code: CAPTURE.AUTHORIZATION_EXPIRED
        correlationId: erp-7f3a-0002
    CAPTURE.METHOD_NOT_SUPPORTED:
      summary: CAPTURE.METHOD_NOT_SUPPORTED
      value:
        type: https://api.kodano.com.br/problems/capture-method-not-supported
        title: Capture not supported
        status: 422
        detail: Only card payments can be captured
        code: CAPTURE.METHOD_NOT_SUPPORTED
        correlationId: erp-7f3a-0002
    REFUND.IN_PROGRESS:
      summary: REFUND.IN_PROGRESS
      value:
        type: https://api.kodano.com.br/problems/refund-in-progress
        title: Refund in progress
        status: 409
        detail: There is already a refund in flight for this payment
        code: REFUND.IN_PROGRESS
        correlationId: erp-7f3a-0002
    REFUND.NOT_SUPPORTED:
      summary: REFUND.NOT_SUPPORTED
      value:
        type: https://api.kodano.com.br/problems/refund-not-supported
        title: Refund not supported
        status: 422
        detail: Boleto payments cannot be refunded
        code: REFUND.NOT_SUPPORTED
        correlationId: erp-7f3a-0002
    REFUND.PAYMENT_NOT_REFUNDABLE:
      summary: REFUND.PAYMENT_NOT_REFUNDABLE
      value:
        type: https://api.kodano.com.br/problems/refund-payment-not-refundable
        title: Payment not refundable
        status: 422
        detail: The payment status does not allow refunds
        code: REFUND.PAYMENT_NOT_REFUNDABLE
        correlationId: erp-7f3a-0002
    REFUND.PAYMENT_NOT_CAPTURED:
      summary: REFUND.PAYMENT_NOT_CAPTURED
      value:
        type: https://api.kodano.com.br/problems/refund-payment-not-captured
        title: Payment not captured
        status: 422
        detail: Authorized payments must be canceled, not refunded
        code: REFUND.PAYMENT_NOT_CAPTURED
        correlationId: erp-7f3a-0002
    REFUND.PAYMENT_FULLY_REFUNDED:
      summary: REFUND.PAYMENT_FULLY_REFUNDED
      value:
        type: https://api.kodano.com.br/problems/refund-payment-fully-refunded
        title: Payment fully refunded
        status: 422
        detail: The payment is already fully refunded
        code: REFUND.PAYMENT_FULLY_REFUNDED
        correlationId: erp-7f3a-0002
    REFUND.AMOUNT_EXCEEDS_REFUNDABLE:
      summary: REFUND.AMOUNT_EXCEEDS_REFUNDABLE
      value:
        type: https://api.kodano.com.br/problems/refund-amount-exceeds-refundable
        title: Refund amount exceeds refundable
        status: 422
        detail: amount is greater than the available refund amount
        code: REFUND.AMOUNT_EXCEEDS_REFUNDABLE
        correlationId: erp-7f3a-0002
        availableRefundAmount: 15000
    REFUND.WINDOW_EXPIRED:
      summary: REFUND.WINDOW_EXPIRED
      value:
        type: https://api.kodano.com.br/problems/refund-window-expired
        title: Refund window expired
        status: 422
        detail: PIX refunds are allowed up to 90 days after payment
        code: REFUND.WINDOW_EXPIRED
        correlationId: erp-7f3a-0002
    REFUND.CHARGEBACK_OPEN:
      summary: REFUND.CHARGEBACK_OPEN
      value:
        type: https://api.kodano.com.br/problems/refund-chargeback-open
        title: Chargeback open
        status: 422
        detail: The payment has an open chargeback
        code: REFUND.CHARGEBACK_OPEN
        correlationId: erp-7f3a-0002
    REFUND.PAYMENT_MANAGED_INTERNALLY:
      summary: REFUND.PAYMENT_MANAGED_INTERNALLY
      value:
        type: https://api.kodano.com.br/problems/refund-payment-managed-internally
        title: Payment managed internally
        status: 422
        detail: Deposit payments cannot be refunded through the API
        code: REFUND.PAYMENT_MANAGED_INTERNALLY
        correlationId: erp-7f3a-0002
    REFUND.PROVIDER_REJECTED:
      summary: REFUND.PROVIDER_REJECTED
      value:
        type: https://api.kodano.com.br/problems/refund-provider-rejected
        title: Refund rejected by provider
        status: 422
        detail: The provider rejected the refund
        code: REFUND.PROVIDER_REJECTED
        correlationId: erp-7f3a-0002
        refundId: ref_Xsf2o3gyrDO1xkxwnQrS7RPeMOkI
    REFUND.PROVIDER_ERROR:
      summary: REFUND.PROVIDER_ERROR
      value:
        type: https://api.kodano.com.br/problems/refund-provider-error
        title: Refund provider error
        status: 502
        detail: The refund ended with a provider error
        code: REFUND.PROVIDER_ERROR
        correlationId: erp-7f3a-0002
    FEES.NOT_AVAILABLE:
      summary: FEES.NOT_AVAILABLE
      value:
        type: https://api.kodano.com.br/problems/fees-not-available
        title: Fees not available
        status: 422
        detail: Fees are available only for paid payments
        code: FEES.NOT_AVAILABLE
        correlationId: erp-7f3a-0002
    ACCEPT.NOT_SUPPORTED:
      summary: ACCEPT.NOT_SUPPORTED
      value:
        type: https://api.kodano.com.br/problems/accept-not-supported
        title: Accept not supported
        status: 406
        detail: Accept must be application/json or application/pdf
        code: ACCEPT.NOT_SUPPORTED
        correlationId: erp-7f3a-0002
    RECEIPT.NOT_AVAILABLE:
      summary: RECEIPT.NOT_AVAILABLE
      value:
        type: https://api.kodano.com.br/problems/receipt-not-available
        title: Receipt not available
        status: 422
        detail: Receipts are available only for paid payments
        code: RECEIPT.NOT_AVAILABLE
        correlationId: erp-7f3a-0002
    PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE:
      summary: PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE
      value:
        type: https://api.kodano.com.br/problems/payment-link-customer-requires-single-use
        title: Customer requires single use link
        status: 422
        detail: A link with customer must have maxPayments = 1
        code: PAYMENT_LINK.CUSTOMER_REQUIRES_SINGLE_USE
        correlationId: erp-7f3a-0002
    PAYMENT_LINK.DUPLICATE_REFERENCE:
      summary: PAYMENT_LINK.DUPLICATE_REFERENCE
      value:
        type: https://api.kodano.com.br/problems/payment-link-duplicate-reference
        title: Duplicate external reference
        status: 409
        detail: externalReference is already used by this merchant
        code: PAYMENT_LINK.DUPLICATE_REFERENCE
        correlationId: erp-7f3a-0002
    PAYMENT_LINK.NOT_CANCELABLE:
      summary: PAYMENT_LINK.NOT_CANCELABLE
      value:
        type: https://api.kodano.com.br/problems/payment-link-not-cancelable
        title: Payment link cannot be canceled
        status: 409
        detail: The payment link is not active
        code: PAYMENT_LINK.NOT_CANCELABLE
        correlationId: erp-7f3a-0002
        currentStatus: CANCELED
    SETTLEMENT.NOT_FOUND:
      summary: SETTLEMENT.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/settlement-not-found
        title: Settlement not found
        status: 404
        detail: The settlement does not exist
        code: SETTLEMENT.NOT_FOUND
        correlationId: erp-7f3a-0002
    WITHDRAWAL.NOT_FOUND:
      summary: WITHDRAWAL.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/withdrawal-not-found
        title: Withdrawal not found
        status: 404
        detail: The withdrawal does not exist
        code: WITHDRAWAL.NOT_FOUND
        correlationId: erp-7f3a-0002
    WITHDRAWAL.DESTINATION_NOT_FOUND:
      summary: WITHDRAWAL.DESTINATION_NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/withdrawal-destination-not-found
        title: Withdrawal destination not found
        status: 422
        detail: The destination does not exist for this merchant
        code: WITHDRAWAL.DESTINATION_NOT_FOUND
        correlationId: erp-7f3a-0002
    WITHDRAWAL.DESTINATION_NOT_VERIFIED:
      summary: WITHDRAWAL.DESTINATION_NOT_VERIFIED
      value:
        type: https://api.kodano.com.br/problems/withdrawal-destination-not-verified
        title: Withdrawal destination not verified
        status: 422
        detail: The destination must be VERIFIED
        code: WITHDRAWAL.DESTINATION_NOT_VERIFIED
        correlationId: erp-7f3a-0002
    WITHDRAWAL.METHOD_NOT_ALLOWED:
      summary: WITHDRAWAL.METHOD_NOT_ALLOWED
      value:
        type: https://api.kodano.com.br/problems/withdrawal-method-not-allowed
        title: Withdrawal method not allowed
        status: 422
        detail: The merchant cannot withdraw through this rail
        code: WITHDRAWAL.METHOD_NOT_ALLOWED
        correlationId: erp-7f3a-0002
    WITHDRAWAL.AMOUNT_OUT_OF_RANGE:
      summary: WITHDRAWAL.AMOUNT_OUT_OF_RANGE
      value:
        type: https://api.kodano.com.br/problems/withdrawal-amount-out-of-range
        title: Withdrawal amount out of range
        status: 422
        detail: amount is outside the allowed range
        code: WITHDRAWAL.AMOUNT_OUT_OF_RANGE
        correlationId: erp-7f3a-0002
    WITHDRAWAL.INSUFFICIENT_BALANCE:
      summary: WITHDRAWAL.INSUFFICIENT_BALANCE
      value:
        type: https://api.kodano.com.br/problems/withdrawal-insufficient-balance
        title: Insufficient balance
        status: 422
        detail: The available balance is lower than amount + fee
        code: WITHDRAWAL.INSUFFICIENT_BALANCE
        correlationId: erp-7f3a-0002
    WITHDRAWAL.DAILY_LIMIT_EXCEEDED:
      summary: WITHDRAWAL.DAILY_LIMIT_EXCEEDED
      value:
        type: https://api.kodano.com.br/problems/withdrawal-daily-limit-exceeded
        title: Daily withdrawal limit exceeded
        status: 422
        detail: The amount exceeds the remaining daily limit of this credential
        code: WITHDRAWAL.DAILY_LIMIT_EXCEEDED
        correlationId: conc-20261016-0042
        limitScope: credential
        remainingAmount: 120000
        resetAt: '2026-10-17T03:00:00Z'
    BANK_ACCOUNT.TYPE_NOT_ALLOWED:
      summary: BANK_ACCOUNT.TYPE_NOT_ALLOWED
      value:
        type: https://api.kodano.com.br/problems/bank-account-type-not-allowed
        title: Bank account type not allowed
        status: 422
        detail: This account type is not accepted by the API
        code: BANK_ACCOUNT.TYPE_NOT_ALLOWED
        correlationId: erp-7f3a-0002
    BANK_ACCOUNT.ALREADY_REGISTERED:
      summary: BANK_ACCOUNT.ALREADY_REGISTERED
      value:
        type: https://api.kodano.com.br/problems/bank-account-already-registered
        title: Bank account already registered
        status: 409
        detail: The bank account is already registered
        code: BANK_ACCOUNT.ALREADY_REGISTERED
        correlationId: erp-7f3a-0002
    BANK_ACCOUNT.NOT_FOUND:
      summary: BANK_ACCOUNT.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/bank-account-not-found
        title: Bank account not found
        status: 404
        detail: The bank account does not exist
        code: BANK_ACCOUNT.NOT_FOUND
        correlationId: erp-7f3a-0002
    BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN:
      summary: BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN
      value:
        type: https://api.kodano.com.br/problems/bank-account-primary-delete-forbidden
        title: Primary bank account cannot be deleted
        status: 409
        detail: The primary account cannot be deleted while other accounts exist
        code: BANK_ACCOUNT.PRIMARY_DELETE_FORBIDDEN
        correlationId: erp-7f3a-0002
    DESTINATION.PENDING_LIMIT_REACHED:
      summary: DESTINATION.PENDING_LIMIT_REACHED
      value:
        type: https://api.kodano.com.br/problems/destination-pending-limit-reached
        title: Pending destinations limit reached
        status: 422
        detail: The merchant already has 20 pending destinations
        code: DESTINATION.PENDING_LIMIT_REACHED
        correlationId: erp-7f3a-0002
    PIX_KEY.NOT_FOUND_IN_DICT:
      summary: PIX_KEY.NOT_FOUND_IN_DICT
      value:
        type: https://api.kodano.com.br/problems/pix-key-not-found-in-dict
        title: PIX key not found in DICT
        status: 422
        detail: The PIX key is not registered in DICT
        code: PIX_KEY.NOT_FOUND_IN_DICT
        correlationId: erp-7f3a-0002
    PIX_KEY.TYPE_MISMATCH:
      summary: PIX_KEY.TYPE_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/pix-key-type-mismatch
        title: PIX key type mismatch
        status: 422
        detail: type does not match the key type returned by DICT
        code: PIX_KEY.TYPE_MISMATCH
        correlationId: erp-7f3a-0002
    PIX_KEY.NO_ACTIVE_ACCOUNT:
      summary: PIX_KEY.NO_ACTIVE_ACCOUNT
      value:
        type: https://api.kodano.com.br/problems/pix-key-no-active-account
        title: No active account
        status: 422
        detail: The merchant has no active digital account to query DICT
        code: PIX_KEY.NO_ACTIVE_ACCOUNT
        correlationId: erp-7f3a-0002
    PIX_KEY.ALREADY_REGISTERED:
      summary: PIX_KEY.ALREADY_REGISTERED
      value:
        type: https://api.kodano.com.br/problems/pix-key-already-registered
        title: PIX key already registered
        status: 409
        detail: The PIX key is already registered
        code: PIX_KEY.ALREADY_REGISTERED
        correlationId: erp-7f3a-0002
    PIX_KEY.NOT_FOUND:
      summary: PIX_KEY.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/pix-key-not-found
        title: PIX key not found
        status: 404
        detail: The PIX key does not exist
        code: PIX_KEY.NOT_FOUND
        correlationId: erp-7f3a-0002
    WEBHOOK.URL_INVALID:
      summary: WEBHOOK.URL_INVALID
      value:
        type: https://api.kodano.com.br/problems/webhook-url-invalid
        title: Invalid webhook URL
        status: 422
        detail: The webhook URL is not allowed
        code: WEBHOOK.URL_INVALID
        correlationId: erp-req-0001
        errors:
        - field: url
          code: WEBHOOK.URL_PRIVATE_ADDRESS
          message: hooks.parceiro.com.br resolves to 10.0.0.5, which is not allowed
    WEBHOOK.EVENT_UNKNOWN:
      summary: WEBHOOK.EVENT_UNKNOWN
      value:
        type: https://api.kodano.com.br/problems/webhook-event-unknown
        title: Unknown event type
        status: 422
        detail: The event type is not in the v1 catalog or cannot be subscribed
        code: WEBHOOK.EVENT_UNKNOWN
        correlationId: erp-7f3a-0002
    WEBHOOK.LIMIT_EXCEEDED:
      summary: WEBHOOK.LIMIT_EXCEEDED
      value:
        type: https://api.kodano.com.br/problems/webhook-limit-exceeded
        title: Webhook endpoint limit exceeded
        status: 422
        detail: There are already 10 endpoints in this scope and environment
        code: WEBHOOK.LIMIT_EXCEEDED
        correlationId: erp-7f3a-0002
    WEBHOOK.NOT_FOUND:
      summary: WEBHOOK.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/webhook-not-found
        title: Webhook endpoint not found
        status: 404
        detail: The webhook endpoint does not exist
        code: WEBHOOK.NOT_FOUND
        correlationId: erp-7f3a-0002
    WEBHOOK.TEST_RATE_LIMITED:
      summary: WEBHOOK.TEST_RATE_LIMITED
      value:
        type: https://api.kodano.com.br/problems/webhook-test-rate-limited
        title: Webhook test rate limited
        status: 429
        detail: More than 10 tests per minute for this endpoint
        code: WEBHOOK.TEST_RATE_LIMITED
        correlationId: erp-7f3a-0002
    WEBHOOK.ENDPOINT_DISABLED:
      summary: WEBHOOK.ENDPOINT_DISABLED
      value:
        type: https://api.kodano.com.br/problems/webhook-endpoint-disabled
        title: Webhook endpoint disabled
        status: 409
        detail: The webhook endpoint is DISABLED
        code: WEBHOOK.ENDPOINT_DISABLED
        correlationId: erp-7f3a-0002
    WEBHOOK.EVENT_NOT_SUBSCRIBED:
      summary: WEBHOOK.EVENT_NOT_SUBSCRIBED
      value:
        type: https://api.kodano.com.br/problems/webhook-event-not-subscribed
        title: Event not subscribed
        status: 422
        detail: The endpoint does not subscribe to this event type
        code: WEBHOOK.EVENT_NOT_SUBSCRIBED
        correlationId: erp-7f3a-0002
    WEBHOOK.NO_MATCHING_ENDPOINT:
      summary: WEBHOOK.NO_MATCHING_ENDPOINT
      value:
        type: https://api.kodano.com.br/problems/webhook-no-matching-endpoint
        title: No matching endpoint
        status: 422
        detail: No ACTIVE endpoint in scope subscribes to this event type
        code: WEBHOOK.NO_MATCHING_ENDPOINT
        correlationId: erp-7f3a-0002
    WEBHOOK.REDELIVER_RATE_LIMITED:
      summary: WEBHOOK.REDELIVER_RATE_LIMITED
      value:
        type: https://api.kodano.com.br/problems/webhook-redeliver-rate-limited
        title: Redeliver rate limited
        status: 429
        detail: All target endpoints reached 5 redeliveries of this event in the last hour
        code: WEBHOOK.REDELIVER_RATE_LIMITED
        correlationId: erp-7f3a-0002
    EVENT.TYPE_UNKNOWN:
      summary: EVENT.TYPE_UNKNOWN
      value:
        type: https://api.kodano.com.br/problems/event-type-unknown
        title: Unknown event type
        status: 422
        detail: type is not in the v1 catalog
        code: EVENT.TYPE_UNKNOWN
        correlationId: erp-7f3a-0002
    EVENT.RANGE_OUT_OF_RETENTION:
      summary: EVENT.RANGE_OUT_OF_RETENTION
      value:
        type: https://api.kodano.com.br/problems/event-range-out-of-retention
        title: Range out of retention
        status: 422
        detail: createdFrom is older than the 30-day retention
        code: EVENT.RANGE_OUT_OF_RETENTION
        correlationId: erp-7f3a-0002
    EVENT.NOT_FOUND:
      summary: EVENT.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/event-not-found
        title: Event not found
        status: 404
        detail: The event does not exist
        code: EVENT.NOT_FOUND
        correlationId: erp-7f3a-0002
    MerchantHeaderRequired:
      summary: MERCHANT.HEADER_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/merchant-header-required
        title: X-Merchant-Id header is required
        status: 400
        detail: This route requires X-Merchant-Id for organization credentials
        code: MERCHANT.HEADER_REQUIRED
        correlationId: erp-7f3a-0006
    AuthTokenInvalid:
      summary: AUTH.TOKEN_INVALID
      value:
        type: https://api.kodano.com.br/problems/auth-token-invalid
        title: Invalid or missing access token
        status: 401
        detail: The access token is missing, invalid or expired
        code: AUTH.TOKEN_INVALID
        correlationId: 5b0e7c1a-3d2f-4e8b-9a6c-1f2e3d4c5b6a
        errors: []
    AuthTenantMismatch:
      summary: AUTH.TENANT_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/auth-tenant-mismatch
        title: Organization mismatch
        status: 403
        detail: The token organization does not match this credential
        code: AUTH.TENANT_MISMATCH
        correlationId: 6c1f8d2b-4e3a-4f9c-8b7d-2a3b4c5d6e7f
        errors: []
    AuthEnvironmentMismatch:
      summary: AUTH.ENVIRONMENT_MISMATCH
      value:
        type: https://api.kodano.com.br/problems/auth-environment-mismatch
        title: Environment mismatch
        status: 403
        detail: This credential does not belong to this environment
        code: AUTH.ENVIRONMENT_MISMATCH
        correlationId: 7d2a9e3c-5f4b-4a0d-9c8e-3b4c5d6e7f80
        errors: []
    AuthEntitlementMissing:
      summary: AUTH.ENTITLEMENT_MISSING
      value:
        type: https://api.kodano.com.br/problems/auth-entitlement-missing
        title: Product not enabled for this credential
        status: 403
        detail: Scope payments:boleto:write is not granted
        code: AUTH.ENTITLEMENT_MISSING
        correlationId: erp-7f3a-0002
    AuthContextMissing:
      summary: AUTH.CONTEXT_MISSING
      value:
        type: https://api.kodano.com.br/problems/auth-context-missing
        title: Caller context missing
        status: 403
        detail: The caller context could not be proven
        code: AUTH.CONTEXT_MISSING
        correlationId: erp-7f3a-0005
    AuthForbidden:
      summary: AUTH.FORBIDDEN
      value:
        type: https://api.kodano.com.br/problems/auth-forbidden
        title: Access denied
        status: 403
        detail: This credential cannot access this route
        code: AUTH.FORBIDDEN
        correlationId: 8e3b0f4d-6a5c-4b1e-8d9f-4c5d6e7f8091
        errors: []
    MerchantForbidden:
      summary: MERCHANT.FORBIDDEN
      value:
        type: https://api.kodano.com.br/problems/merchant-forbidden
        title: Merchant not accessible
        status: 403
        detail: X-Merchant-Id is not accessible to this credential
        code: MERCHANT.FORBIDDEN
        correlationId: a05d2b6f-8c7e-4d3a-8f1b-6e7f8091a2b3
        errors: []
    RequestBlocked:
      summary: REQUEST.BLOCKED
      value:
        type: https://api.kodano.com.br/problems/request-blocked
        title: Request blocked
        status: 403
        detail: The request was blocked by the edge firewall
        code: REQUEST.BLOCKED
        correlationId: c27f4d8b-0e9a-4f5c-8b3d-8091a2b3c4d5
        errors: []
    RouteNotFound:
      summary: ROUTE.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/route-not-found
        title: Route not found
        status: 404
        detail: No route matches this method and path
        code: ROUTE.NOT_FOUND
        correlationId: b16e3c7a-9d8f-4e4b-9a2c-7f8091a2b3c4
        errors: []
    ResourceNotFound:
      summary: RESOURCE.NOT_FOUND
      value:
        type: https://api.kodano.com.br/problems/resource-not-found
        title: Resource not found
        status: 404
        detail: The requested resource was not found
        code: RESOURCE.NOT_FOUND
        correlationId: erp-7f3a-0018
    RateLimitExceeded:
      summary: RATE_LIMIT.EXCEEDED
      value:
        type: https://api.kodano.com.br/problems/rate-limit-exceeded
        title: Too many requests
        status: 429
        detail: Request rate limit exceeded for this credential
        code: RATE_LIMIT.EXCEEDED
        correlationId: 9d4c2b1a-7e6f-4a5b-8c3d-2e1f0a9b8c7d
        errors: []
    InternalError:
      summary: INTERNAL.ERROR
      value:
        type: https://api.kodano.com.br/problems/internal-error
        title: Internal error
        status: 500
        detail: An unexpected error occurred
        code: INTERNAL.ERROR
        correlationId: erp-7f3a-0021
    UpstreamUnavailable:
      summary: UPSTREAM.UNAVAILABLE
      value:
        type: https://api.kodano.com.br/problems/upstream-unavailable
        title: Service unavailable
        status: 502
        detail: The service is temporarily unavailable, retry later
        code: UPSTREAM.UNAVAILABLE
        correlationId: erp-7f3a-0022
    AuthTemporarilyUnavailable:
      summary: AUTH.TEMPORARILY_UNAVAILABLE
      value:
        type: https://api.kodano.com.br/problems/auth-temporarily-unavailable
        title: Authentication temporarily unavailable
        status: 503
        detail: Authentication could not be completed, retry shortly
        code: AUTH.TEMPORARILY_UNAVAILABLE
        correlationId: 9f4c1a5e-7b6d-4c2f-9e0a-5d6e7f8091a2
        errors: []
    UpstreamTimeout:
      summary: UPSTREAM.TIMEOUT
      value:
        type: https://api.kodano.com.br/problems/upstream-timeout
        title: Service timeout
        status: 504
        detail: The service did not respond in time
        code: UPSTREAM.TIMEOUT
        correlationId: erp-7f3a-0023
    IdempotencyConflict:
      summary: IDEMPOTENCY.CONFLICT
      value:
        type: https://api.kodano.com.br/problems/idempotency-conflict
        title: Idempotency key reused with a different request
        status: 409
        detail: Idempotency-Key 4b2e8c1a-9d7f-4e3b-a6c5-0f1e2d3c4b5a was used with a different body or path
        code: IDEMPOTENCY.CONFLICT
        correlationId: erp-7f3a-0012
    IdempotencyInProgress:
      summary: IDEMPOTENCY.IN_PROGRESS
      description: Enviado com Retry-After 2. resourceId aparece quando o recurso já existe.
      value:
        type: https://api.kodano.com.br/problems/idempotency-in-progress
        title: Request in progress
        status: 409
        detail: A request with this Idempotency-Key is still being processed
        code: IDEMPOTENCY.IN_PROGRESS
        correlationId: erp-7f3a-0013
        resourceId: pay_7Hq2LmX9vB4nR8sT1wK6yZ3cP0aD
    RequestTooLarge:
      summary: REQUEST.TOO_LARGE
      value:
        type: https://api.kodano.com.br/problems/request-too-large
        title: Request body too large
        status: 413
        detail: The request body exceeds the allowed size
        code: REQUEST.TOO_LARGE
        correlationId: erp-7f3a-0015
    ValidationFailed:
      summary: VALIDATION.FAILED
      value:
        type: https://api.kodano.com.br/problems/validation-failed
        title: Request validation failed
        status: 422
        detail: One or more fields are invalid
        code: VALIDATION.FAILED
        correlationId: erp-7f3a-0001
        errors:
        - field: amount
          code: VALIDATION.MIN
          message: must be >= 100
    IdempotencyKeyRequired:
      summary: IDEMPOTENCY.KEY_REQUIRED
      value:
        type: https://api.kodano.com.br/problems/idempotency-key-required
        title: Idempotency-Key header is required
        status: 422
        detail: Idempotency-Key must be a UUID
        code: IDEMPOTENCY.KEY_REQUIRED
        correlationId: erp-7f3a-0014
    CursorInvalid:
      summary: CURSOR.INVALID
      value:
        type: https://api.kodano.com.br/problems/cursor-invalid
        title: Invalid cursor
        status: 422
        detail: The cursor does not match this list, its filters or limit
        code: CURSOR.INVALID
        correlationId: erp-7f3a-0017
    RequestUnprocessable:
      summary: REQUEST.UNPROCESSABLE
      value:
        type: https://api.kodano.com.br/problems/request-unprocessable
        title: Request could not be processed
        status: 422
        detail: The request cannot be processed in the current state of the resource
        code: REQUEST.UNPROCESSABLE
        correlationId: erp-7f3a-0016
  responses:
    BadRequest:
      description: Credencial de organização sem X-Merchant-Id em rota de merchant.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            MERCHANT.HEADER_REQUIRED:
              $ref: '#/components/examples/MerchantHeaderRequired'
    Unauthorized:
      description: Token ausente, inválido, expirado ou de credencial inativa.
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WWW-Authenticate'
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            AUTH.TOKEN_INVALID:
              $ref: '#/components/examples/AuthTokenInvalid'
    Forbidden:
      description: Acesso negado à rota ou ao merchant.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            AUTH.TENANT_MISMATCH:
              $ref: '#/components/examples/AuthTenantMismatch'
            AUTH.ENVIRONMENT_MISMATCH:
              $ref: '#/components/examples/AuthEnvironmentMismatch'
            AUTH.ENTITLEMENT_MISSING:
              $ref: '#/components/examples/AuthEntitlementMissing'
            AUTH.CONTEXT_MISSING:
              $ref: '#/components/examples/AuthContextMissing'
            AUTH.FORBIDDEN:
              $ref: '#/components/examples/AuthForbidden'
            MERCHANT.FORBIDDEN:
              $ref: '#/components/examples/MerchantForbidden'
            REQUEST.BLOCKED:
              $ref: '#/components/examples/RequestBlocked'
    NotFound:
      description: Rota ou recurso não encontrado.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            ROUTE.NOT_FOUND:
              $ref: '#/components/examples/RouteNotFound'
            RESOURCE.NOT_FOUND:
              $ref: '#/components/examples/ResourceNotFound'
    TooManyRequests:
      description: Limite de requisições da credencial excedido.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            RATE_LIMIT.EXCEEDED:
              $ref: '#/components/examples/RateLimitExceeded'
    InternalError:
      description: Erro inesperado.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            INTERNAL.ERROR:
              $ref: '#/components/examples/InternalError'
    BadGateway:
      description: Serviço temporariamente indisponível.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            UPSTREAM.UNAVAILABLE:
              $ref: '#/components/examples/UpstreamUnavailable'
    ServiceUnavailable:
      description: Autenticação ou dependência indisponível; repita depois do Retry-After.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            AUTH.TEMPORARILY_UNAVAILABLE:
              $ref: '#/components/examples/AuthTemporarilyUnavailable'
    GatewayTimeout:
      description: 'Serviço sem resposta no prazo. Em rota idempotente, repita com a mesma Idempotency-Key: enquanto o desfecho não estiver resolvido, a repetição recebe 409 IDEMPOTENCY.IN_PROGRESS.'
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            UPSTREAM.TIMEOUT:
              $ref: '#/components/examples/UpstreamTimeout'
    ContentTooLarge:
      description: Corpo acima do limite.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            REQUEST.TOO_LARGE:
              $ref: '#/components/examples/RequestTooLarge'
    ContractUnprocessable:
      description: Corpo inválido ou regra de contrato não atendida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            VALIDATION.FAILED:
              $ref: '#/components/examples/ValidationFailed'
            IDEMPOTENCY.KEY_REQUIRED:
              $ref: '#/components/examples/IdempotencyKeyRequired'
            CONTRACT.NOT_ENABLED:
              summary: CONTRACT.NOT_ENABLED
              value:
                type: https://api.kodano.com.br/problems/contract-not-enabled
                title: Contracts not enabled
                status: 422
                detail: Electronic contracts are not enabled for this merchant
                code: CONTRACT.NOT_ENABLED
                correlationId: conc-20261028-0042
            CONTRACT.SOURCE_INVALID:
              summary: CONTRACT.SOURCE_INVALID
              value:
                type: https://api.kodano.com.br/problems/contract-source-invalid
                title: Contract source invalid
                status: 422
                detail: The template or file does not exist, is inactive or is not ready
                code: CONTRACT.SOURCE_INVALID
                correlationId: conc-20261028-0042
            CONTRACT.TEMPLATE_NOT_READY:
              summary: CONTRACT.TEMPLATE_NOT_READY
              value:
                type: https://api.kodano.com.br/problems/contract-template-not-ready
                title: Contract template not ready
                status: 422
                detail: The template cannot issue contracts yet
                code: CONTRACT.TEMPLATE_NOT_READY
                correlationId: conc-20261028-0042
            CONTRACT.TAG_MISSING:
              summary: CONTRACT.TAG_MISSING
              value:
                type: https://api.kodano.com.br/problems/contract-tag-missing
                title: Contract tags invalid
                status: 422
                detail: A required tag is missing or a tag is unknown to the template
                code: CONTRACT.TAG_MISSING
                correlationId: conc-20261028-0042
            CONTRACT.SIGNERS_INVALID:
              summary: CONTRACT.SIGNERS_INVALID
              value:
                type: https://api.kodano.com.br/problems/contract-signers-invalid
                title: Contract signers invalid
                status: 422
                detail: Signer count, roles or authentication are invalid
                code: CONTRACT.SIGNERS_INVALID
                correlationId: conc-20261028-0042
            CONTRACT.FILE_INVALID:
              summary: CONTRACT.FILE_INVALID
              value:
                type: https://api.kodano.com.br/problems/contract-file-invalid
                title: Contract file invalid
                status: 422
                detail: The file format, size or content is not accepted
                code: CONTRACT.FILE_INVALID
                correlationId: conc-20261028-0042
    ContractTemplateNotFound:
      description: Modelo inexistente, malformado ou de outro merchant; ou rota desconhecida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            CONTRACT_TEMPLATE.NOT_FOUND:
              summary: CONTRACT_TEMPLATE.NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/contract-template-not-found
                title: Contract template not found
                status: 404
                detail: No contract template with this id is accessible to this credential
                code: CONTRACT_TEMPLATE.NOT_FOUND
                correlationId: conc-20261028-0042
            ROUTE.NOT_FOUND:
              $ref: '#/components/examples/RouteNotFound'
    UnprocessableContent:
      description: Request bem formado que não pôde ser aceito.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            VALIDATION.FAILED:
              $ref: '#/components/examples/ValidationFailed'
            IDEMPOTENCY.KEY_REQUIRED:
              $ref: '#/components/examples/IdempotencyKeyRequired'
            CURSOR.INVALID:
              $ref: '#/components/examples/CursorInvalid'
            REQUEST.UNPROCESSABLE:
              $ref: '#/components/examples/RequestUnprocessable'
    ContractNotFound:
      description: Contrato inexistente, malformado ou de outro merchant; ou rota desconhecida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            CONTRACT.NOT_FOUND:
              summary: CONTRACT.NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/contract-not-found
                title: Contract not found
                status: 404
                detail: No contract with this id is accessible to this credential
                code: CONTRACT.NOT_FOUND
                correlationId: conc-20261028-0042
            ROUTE.NOT_FOUND:
              $ref: '#/components/examples/RouteNotFound'
    ContractConflict:
      description: Contrato ou signatário fora do estado exigido, ou PDF assinado ainda não disponível.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            CONTRACT.NOT_EDITABLE:
              summary: CONTRACT.NOT_EDITABLE
              value:
                type: https://api.kodano.com.br/problems/contract-not-editable
                title: Contract cannot be changed
                status: 409
                detail: The contract is closed or the signer has already signed
                code: CONTRACT.NOT_EDITABLE
                correlationId: conc-20261028-0042
            CONTRACT.SIGNED_FILE_NOT_AVAILABLE:
              summary: CONTRACT.SIGNED_FILE_NOT_AVAILABLE
              value:
                type: https://api.kodano.com.br/problems/contract-signed-file-not-available
                title: Signed file not available
                status: 409
                detail: The signed file is not available yet
                code: CONTRACT.SIGNED_FILE_NOT_AVAILABLE
                correlationId: conc-20261028-0042
            IDEMPOTENCY.CONFLICT:
              $ref: '#/components/examples/IdempotencyConflict'
    DepositUnprocessable:
      description: Corpo inválido ou regra de caução não atendida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            VALIDATION.FAILED:
              $ref: '#/components/examples/ValidationFailed'
            IDEMPOTENCY.KEY_REQUIRED:
              $ref: '#/components/examples/IdempotencyKeyRequired'
            DEPOSIT.NOT_ENABLED:
              summary: DEPOSIT.NOT_ENABLED
              value:
                type: https://api.kodano.com.br/problems/deposit-not-enabled
                title: Deposits not enabled
                status: 422
                detail: Deposits are not enabled for this merchant
                code: DEPOSIT.NOT_ENABLED
                correlationId: conc-20261028-0042
            DEPOSIT.CONTRACT_NOT_USABLE:
              summary: DEPOSIT.CONTRACT_NOT_USABLE
              value:
                type: https://api.kodano.com.br/problems/deposit-contract-not-usable
                title: Contract not usable
                status: 422
                detail: The contract does not exist for this merchant or is refused, expired or canceled
                code: DEPOSIT.CONTRACT_NOT_USABLE
                correlationId: conc-20261028-0042
            DEPOSIT.MODE_NOT_AVAILABLE:
              summary: DEPOSIT.MODE_NOT_AVAILABLE
              value:
                type: https://api.kodano.com.br/problems/deposit-mode-not-available
                title: Deposit mode not available
                status: 422
                detail: The deposit mode configured for this merchant cannot be used
                code: DEPOSIT.MODE_NOT_AVAILABLE
                correlationId: conc-20261028-0042
            DEPOSIT.CAPTURE_AMOUNT_NOT_ALLOWED:
              summary: DEPOSIT.CAPTURE_AMOUNT_NOT_ALLOWED
              value:
                type: https://api.kodano.com.br/problems/deposit-capture-amount-not-allowed
                title: Capture amount not allowed
                status: 422
                detail: The capture amount is not allowed for this deposit
                code: DEPOSIT.CAPTURE_AMOUNT_NOT_ALLOWED
                correlationId: conc-20261028-0042
    DepositNotFound:
      description: Caução inexistente, malformada ou de outro merchant; ou rota desconhecida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            DEPOSIT.NOT_FOUND:
              summary: DEPOSIT.NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/deposit-not-found
                title: Deposit not found
                status: 404
                detail: No deposit with this id is accessible to this credential
                code: DEPOSIT.NOT_FOUND
                correlationId: conc-20261028-0042
            ROUTE.NOT_FOUND:
              $ref: '#/components/examples/RouteNotFound'
    DepositConflict:
      description: Caução fora do estado exigido.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            DEPOSIT.AWAITING_AUTHORIZATION:
              summary: DEPOSIT.AWAITING_AUTHORIZATION
              value:
                type: https://api.kodano.com.br/problems/deposit-awaiting-authorization
                title: Deposit awaiting authorization
                status: 409
                detail: The payer has not authorized the card yet
                code: DEPOSIT.AWAITING_AUTHORIZATION
                correlationId: conc-20261028-0042
            DEPOSIT.NO_LIVE_AUTHORIZATION:
              summary: DEPOSIT.NO_LIVE_AUTHORIZATION
              value:
                type: https://api.kodano.com.br/problems/deposit-no-live-authorization
                title: Deposit without live authorization
                status: 409
                detail: The deposit has no valid card authorization to capture
                code: DEPOSIT.NO_LIVE_AUTHORIZATION
                correlationId: conc-20261028-0042
            DEPOSIT.ALREADY_CLOSED:
              summary: DEPOSIT.ALREADY_CLOSED
              value:
                type: https://api.kodano.com.br/problems/deposit-already-closed
                title: Deposit already closed
                status: 409
                detail: The deposit was already released, captured or refunded
                code: DEPOSIT.ALREADY_CLOSED
                correlationId: conc-20261028-0042
            IDEMPOTENCY.CONFLICT:
              $ref: '#/components/examples/IdempotencyConflict'
    FxNotFound:
      description: Cotação, operação ou conta de destino inexistente, malformada ou de outro merchant.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            FX.QUOTE_NOT_FOUND:
              summary: FX.QUOTE_NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/fx-quote-not-found
                title: Quote not found
                status: 404
                detail: No quote with this id is accessible to this credential
                code: FX.QUOTE_NOT_FOUND
                correlationId: conc-20261102-0042
            FX.OPERATION_NOT_FOUND:
              summary: FX.OPERATION_NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/fx-operation-not-found
                title: Operation not found
                status: 404
                detail: No operation with this id is accessible to this credential
                code: FX.OPERATION_NOT_FOUND
                correlationId: conc-20261102-0042
            FX.PAYOUT_ACCOUNT_NOT_FOUND:
              summary: FX.PAYOUT_ACCOUNT_NOT_FOUND
              value:
                type: https://api.kodano.com.br/problems/fx-payout-account-not-found
                title: Payout account not found
                status: 404
                detail: No payout account with this id is accessible to this credential
                code: FX.PAYOUT_ACCOUNT_NOT_FOUND
                correlationId: conc-20261102-0042
            ROUTE.NOT_FOUND:
              $ref: '#/components/examples/RouteNotFound'
    FxGone:
      description: Cotação vencida.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            FX.QUOTE_EXPIRED:
              summary: FX.QUOTE_EXPIRED
              value:
                type: https://api.kodano.com.br/problems/fx-quote-expired
                title: Quote expired
                status: 410
                detail: The quote expired; create a new one
                code: FX.QUOTE_EXPIRED
                correlationId: conc-20261102-0042
    FxConflict:
      description: Conflito de estado ou de idempotência.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            FX.ACCOUNT_NOT_READY:
              summary: FX.ACCOUNT_NOT_READY
              value:
                type: https://api.kodano.com.br/problems/fx-account-not-ready
                title: Account not ready
                status: 409
                detail: The merchant is not yet ready to operate foreign exchange
                code: FX.ACCOUNT_NOT_READY
                correlationId: conc-20261102-0042
            FX.RECEIPT_NOT_AVAILABLE:
              summary: FX.RECEIPT_NOT_AVAILABLE
              value:
                type: https://api.kodano.com.br/problems/fx-receipt-not-available
                title: Receipt not available
                status: 409
                detail: The receipt is available once the operation is settled or refunded
                code: FX.RECEIPT_NOT_AVAILABLE
                correlationId: conc-20261102-0042
            IDEMPOTENCY.CONFLICT:
              $ref: '#/components/examples/IdempotencyConflict'
    FxUnprocessable:
      description: Regra do câmbio recusou o pedido.
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/X-Correlation-Id'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            FX.NOT_ENABLED:
              summary: FX.NOT_ENABLED
              value:
                type: https://api.kodano.com.br/problems/fx-not-enabled
                title: Foreign exchange not enabled
                status: 422
                detail: Foreign exchange is not enabled for this merchant
                code: FX.NOT_ENABLED
                correlationId: conc-20261102-0042
            FX.AMOUNT_BELOW_MINIMUM:
              summary: FX.AMOUNT_BELOW_MINIMUM
              value:
                type: https://api.kodano.com.br/problems/fx-amount-below-minimum
                title: Amount below minimum
                status: 422
                detail: The amount is below the minimum for this corridor
                code: FX.AMOUNT_BELOW_MINIMUM
                correlationId: conc-20261102-0042
            FX.AMOUNT_ABOVE_MAXIMUM:
              summary: FX.AMOUNT_ABOVE_MAXIMUM
              value:
                type: https://api.kodano.com.br/problems/fx-amount-above-maximum
                title: Amount above maximum
                status: 422
                detail: The amount is above the maximum for this corridor
                code: FX.AMOUNT_ABOVE_MAXIMUM
                correlationId: conc-20261102-0042
            FX.LIMIT_EXCEEDED:
              summary: FX.LIMIT_EXCEEDED
              value:
                type: https://api.kodano.com.br/problems/fx-limit-exceeded
                title: Limit exceeded
                status: 422
                detail: The amount exceeds the available limit
                code: FX.LIMIT_EXCEEDED
                remaining:
                  amount: 120000
                  currency: BRL
                correlationId: conc-20261102-0042
            FX.CORRIDOR_NOT_AVAILABLE:
              summary: FX.CORRIDOR_NOT_AVAILABLE
              value:
                type: https://api.kodano.com.br/problems/fx-corridor-not-available
                title: Corridor not available
                status: 422
                detail: This product and direction are not available
                code: FX.CORRIDOR_NOT_AVAILABLE
                correlationId: conc-20261102-0042
            FX.RAIL_NOT_SUPPORTED:
              summary: FX.RAIL_NOT_SUPPORTED
              value:
                type: https://api.kodano.com.br/problems/fx-rail-not-supported
                title: Rail not supported
                status: 422
                detail: This rail does not support the direction
                code: FX.RAIL_NOT_SUPPORTED
                correlationId: conc-20261102-0042
            FX.RAIL_NOT_AVAILABLE:
              summary: FX.RAIL_NOT_AVAILABLE
              value:
                type: https://api.kodano.com.br/problems/fx-rail-not-available
                title: Rail not available
                status: 422
                detail: This rail is not enabled
                code: FX.RAIL_NOT_AVAILABLE
                correlationId: conc-20261102-0042
            FX.PAYOUT_ACCOUNT_INVALID:
              summary: FX.PAYOUT_ACCOUNT_INVALID
              value:
                type: https://api.kodano.com.br/problems/fx-payout-account-invalid
                title: Payout account invalid
                status: 422
                detail: The payout account is inactive or does not match the quote rail
                code: FX.PAYOUT_ACCOUNT_INVALID
                correlationId: conc-20261102-0042
            FX.PAYOUT_ACCOUNT_VALIDATION:
              summary: FX.PAYOUT_ACCOUNT_VALIDATION
              value:
                type: https://api.kodano.com.br/problems/fx-payout-account-validation
                title: Payout account validation failed
                status: 422
                detail: One or more fields are invalid
                code: FX.PAYOUT_ACCOUNT_VALIDATION
                correlationId: conc-20261102-0042
                errors:
                - field: bank.routingNumber
                  code: INVALID_ROUTING_NUMBER
                  message: The routing number must have 9 digits
            FX.AUTHORIZATION_REQUIRED:
              summary: FX.AUTHORIZATION_REQUIRED
              value:
                type: https://api.kodano.com.br/problems/fx-authorization-required
                title: Authorization required
                status: 422
                detail: authorization.accepted must be true with the current terms version
                code: FX.AUTHORIZATION_REQUIRED
                correlationId: conc-20261102-0042
            VALIDATION.FAILED:
              $ref: '#/components/examples/ValidationFailed'
