{
  "openapi": "3.1.0",
  "info": {
    "title": "Lume — API bancária",
    "version": "1.2.0",
    "summary": "Conta, PIX e extrato para integrações servidor-a-servidor.",
    "description": "API do banco digital Lume. Abre conta de pagamento, consulta saldo e\nextrato, cadastra chaves PIX, transfere por PIX e gera cobrança por BR Code.\n\n## Em quinze minutos\n\n1. Peça uma chave de sandbox em `POST /v1/sandbox/keys` — sem cadastro e sem\n   aprovação. A resposta traz a chave, uma conta de testes isolada com\n   R$ 5.000,00 de saldo fictício e as credenciais para abrir a mesma conta\n   no internet banking.\n2. Chame `GET /v1/accounts/balance` com o cabeçalho `X-API-Key`.\n3. Transfira com `POST /v1/pix/transfers`, sempre com `Idempotency-Key`.\n\n## Três regras que a integração precisa respeitar\n\n- **`Idempotency-Key` é obrigatório em toda chamada que move dinheiro.** A\n  rede não colabora: o cliente envia, perde o sinal antes da resposta e\n  tenta de novo. Sem a chave, ele paga duas vezes.\n- **Valores são inteiros em centavos.** `amountCents: 5000` são R$ 50,00.\n  Não existe campo em reais, e ponto flutuante não entra em lugar nenhum.\n- **O ambiente vive na chave.** Uma chave de sandbox não funciona em\n  produção nem o contrário, mesmo com a URL base certa.\n\n## Ambientes\n\n| Ambiente | URL base | Prefixo da chave |\n| --- | --- | --- |\n| Sandbox | `https://sandbox.api.lumecoding.com` | `lume_sk_sandbox_` |\n| Produção | `https://api.lumecoding.com` | `lume_sk_live_` |\n\nO sandbox roda o mesmo código de produção, com banco isolado, dados\nfictícios e um provedor de pagamentos simulado. As respostas têm a mesma\nforma nos dois ambientes: o que você construir contra o sandbox funciona em\nprodução trocando a URL base e a chave.\n",
    "contact": {
      "name": "Suporte a desenvolvedores",
      "url": "https://docbank.lumecoding.com",
      "email": "contato@lumecoding.com"
    },
    "license": {
      "name": "Proprietária"
    }
  },
  "servers": [
    {
      "url": "https://sandbox.api.lumecoding.com",
      "description": "Sandbox — dados fictícios, dinheiro nenhum"
    },
    {
      "url": "https://api.lumecoding.com",
      "description": "Produção"
    },
    {
      "url": "http://localhost:8080",
      "description": "Servidor local (`go run ./cmd/api`)"
    }
  ],
  "tags": [
    {
      "name": "Primeiros passos",
      "description": "A chave de sandbox e a verificação de que o serviço está no ar."
    },
    {
      "name": "Autenticação",
      "description": "Duas credenciais diferentes, para dois usos diferentes.\n\n**Chave de API** (`X-API-Key`) é a credencial de integração\nservidor-a-servidor. Não expira por tempo, expira por 30 dias de\ninatividade, e é revogável a qualquer momento. Com a conta bloqueada\nou encerrada, toda chamada com a chave responde `403` com\n`code: FORBIDDEN`; reativada a conta, a chave volta a valer.\n\nCada chave carrega **escopos**, e uma chave só alcança o que os seus\nescopos permitem:\n\n| Escopo | O que abre |\n| --- | --- |\n| `accounts:read` | Perfil, saldo, extrato, transação, limites, beneficiários, trilha de segurança e tabela e cotação de tarifas |\n| `pix:read` | Listar chaves, consultar o DICT, ler um BR Code, listar cobranças por QR Code |\n| `pix:write` | Transferir, emitir BR Code e gerar ou excluir cobranças por QR Code |\n| `pix:keys:write` | Cadastrar chave PIX (excluir é só de sessão) |\n| `beneficiaries:write` | Cadastrar e excluir beneficiário |\n\nA chave de sandbox nasce com todos: o ambiente existe para ser\nexercitado inteiro. A chave de produção nasce **só com\n`accounts:read`** — quem precisa mover dinheiro pede na emissão.\n\nAlgumas rotas não são alcançáveis por chave de API, com escopo nenhum, e\nno contrato declaram `security` só de sessão: `POST /v1/auth/step-up`,\nporque o segundo fator é um ato humano; `POST /v1/transactions/{id}/review`,\nporque decidir sobre a própria transação retida é do titular da conta;\n`POST /v1/transactions/{id}/refund`, porque devolver Pix recebido é\ndinheiro saindo da conta, com segundo fator;\n`POST /v1/ted/transfers`; a cobrança por boleto (`/v1/billets`) e o\npagamento de contas (`/v1/billets/lookup` e `/v1/billets/pay`), que só\nexistem para a sessão de quem está na tela. Chave de API nelas, ou\nfalta de escopo nas demais, devolve `403` com `code: FORBIDDEN`.\n\n**Sessão** (`Authorization: Bearer`) é a credencial de quem está numa\ntela: documento e senha, depois código do aplicativo autenticador. O\ntoken de acesso vale 15 minutos e o de renovação, 30 dias, com rotação a\ncada uso.\n\nAlém das duas, o **step-up** (`X-Step-Up`) é uma prova de segundo fator\namarrada a *uma* operação: aprovar uma transferência de R$ 50 não\nautoriza a próxima de R$ 50 mil.\n"
    },
    {
      "name": "Abertura de conta",
      "description": "Abrir conta e acompanhar a abertura. O modo da instalação\n(`accountOpening.mode` em `GET /v1/features`) decide quem conduz o\ncadastro: o Lume (`own`) ou o provedor financeiro, na tela dele\n(`hosted`).\n"
    },
    {
      "name": "Conta",
      "description": "Saldo, extrato e dados do titular."
    },
    {
      "name": "PIX",
      "description": "Chaves, consulta no DICT, transferência e BR Code."
    },
    {
      "name": "TED",
      "description": "Transferência para conta de outra instituição, com os dados do favorecido\ndigitados. **O nome do favorecido é o que o cliente digitou**: não há DICT\npara conta bancária, e a tela não pode apresentá-lo como confirmado.\n"
    },
    {
      "name": "Beneficiários",
      "description": "Contatos salvos. O nome vem sempre do DICT, nunca do que foi digitado."
    },
    {
      "name": "Cobrança",
      "description": "O boleto que o cliente **emite** para receber de alguem - o inverso do\npagamento de um titulo de terceiro.\n\nDuas coisas mudam em relacao a tudo o mais nesta API. A primeira e que\nemitir **nao move saldo**: o titulo e uma promessa de recebimento, nao um\nlancamento, e nada aparece no extrato ate que alguem pague. A segunda e\nque o credito nasce de um evento vindo de fora - a liquidacao -, e por\nisso chega como `BILLING_RECEIVED` no extrato, com o identificador que o\nboleto passa a apontar em `transactionId`.\n\nEntre a emissao e a liquidacao o titulo vive em `REGISTERING` ou\n`REGISTERED`. A diferenca importa: **um titulo em `REGISTERING` ainda nao\ne pagavel** e nao tem linha digitavel - entrega-la ao pagador nessa altura\ne entregar um numero que o caixa recusa.\n\nA cobranca por boleto e **so de sessao**: uma chave de API, com qualquer\nescopo, recebe `403 FORBIDDEN` nestas rotas.\n"
    },
    {
      "name": "Pagamento de contas",
      "description": "Pagar, com o saldo da conta, um boleto (ficha de compensação) ou uma conta\nde concessionária ou tributo (guia de arrecadação) de terceiro: o inverso da\ncobrança. Duas etapas, e a primeira só informa: a consulta diz quanto custa\npagar **agora**, e o pagamento a refaz antes de debitar.\n\nSó de sessão: uma chave de API recebe `403 FORBIDDEN` nestas rotas. O\npagamento sai no máximo uma vez, e o desfecho pode chegar depois da resposta\n(`PROCESSING`).\n"
    },
    {
      "name": "Agendamentos",
      "description": "Agendar um PIX ou o pagamento de um boleto para uma data futura. Quem paga\nna data depende da instalação: o Lume (que refaz as conferências na data) ou\no banco parceiro (que paga com o valor já reservado na conta). Só de sessão:\numa chave de API recebe `403 FORBIDDEN` nestas rotas.\n"
    },
    {
      "name": "Segurança",
      "description": "Limites em vigor e histórico de eventos da conta."
    },
    {
      "name": "Cartões",
      "description": "Os cartões da conta. A instalação oferece um produto (`model` em\n`GET /v1/cards`): `CREDIT`, o cartão de crédito com fatura, ou\n`PREPAID`, o pré-pago de um emissor parceiro, em que a compra debita o\nsaldo da conta e aparece no extrato (`CARD_PURCHASE`). No pré-pago o\ncliente aceita o termo, passa pela análise do emissor e pede o cartão\nvirtual; número, validade e código de segurança abrem só numa página\nsegura do emissor (`secure-view`). Só com sessão de cliente: nenhuma\nrota de cartão aceita chave de API.\n"
    },
    {
      "name": "Notificações",
      "description": "O endereço que o provedor financeiro chama. Não é chamado pela sua\nintegração — está aqui porque quem instala o Lume precisa configurá-lo\nno painel do provedor, e porque o formato da assinatura é o que decide\nse a notificação será aceita.\n\n**Os webhooks que o Lume envia para a sua URL** estão na seção\n`webhooks` deste contrato (`transaction.updated` e `ping`). O cadastro\nda URL é feito pelo banco, a pedido: você informa o endereço `https`,\nrecebe o segredo da assinatura uma única vez e passa a receber cada\nmudança de estado das transações da conta, assinada.\n"
    },
    {
      "name": "Indicação",
      "description": "A rede de indicação de quem está na sessão: resumo, indicados diretos e\nganhos. Só leitura e só de sessão. Indicado indireto aparece só em\nnúmero, nunca com nome.\n"
    },
    {
      "name": "Estabelecimento",
      "description": "O portal de quem recebe por maquininha ou link (funcionalidade\n`merchant_portal`): resumo de vendas, lista e detalhe das vendas e a\nagenda de recebíveis (lançamentos futuros). Só leitura e só de sessão:\numa chave de API recebe `403 FORBIDDEN`. Com a funcionalidade desligada,\n`403 FEATURE_DISABLED` (`details.feature: merchant_portal`).\n\nAs vendas e os recebíveis são uma cópia do adquirente, atualizada por uma\nrotina a cada poucos minutos (`merchant.lastSyncAt` diz quando foi a\núltima concluída; com muitas vendas, uma cópia pode levar várias\nvoltas). Valores sempre em centavos. Do cartão, no máximo a bandeira e os\nquatro últimos dígitos. A conta que ainda não foi credenciada pelo\natendimento recebe `404 NOT_FOUND` nestas rotas.\n"
    },
    {
      "name": "Assinaturas",
      "description": "Planos mensais pagos com o saldo da conta: escolher um plano, ver a\nmensalidade e o histórico, trocar de plano (vale na próxima\nmensalidade) e cancelar. Só de sessão: uma chave de API\nrecebe `403 FORBIDDEN` nestas rotas. Existe só onde há transferência\ninterna (funcionalidade `subscriptions`); desligada, `403\nFEATURE_DISABLED` — menos ver e cancelar o plano vivo\n(`GET /v1/subscription` e `POST /v1/subscription/cancel`), que\ncontinuam valendo para o cliente poder sair do plano a qualquer momento,\ne desfazer uma troca agendada quando voltar não aumenta a mensalidade\n(`POST /v1/subscription/plan-change/cancel`).\n\nO plano é também uma **cesta de serviços**: cotas por serviço (Pix\nenviado, TED, boletos…) por período pago, de um vencimento ao seguinte\n(`GET /v1/subscription/usage`), e o cliente escolhe o dia de cobrança\n(`POST /v1/subscription/billing-day`). Há planos para pessoa física e\npara empresa: cada conta vê os do seu público.\n"
    },
    {
      "name": "Benefícios",
      "description": "A vitrine de benefícios de quem está na sessão (funcionalidade\n`marketplace`): banners de novidades, ofertas de parceiros (\"mimos\")\nagrupadas por categoria e o link da loja. Só leitura e só de sessão:\numa chave de API recebe `403 FORBIDDEN`. Com a funcionalidade desligada,\n`403 FEATURE_DISABLED` (`details.feature: marketplace`).\n\nNada aqui move dinheiro. \"Cashback\" de oferta (`cashback_informativo`) é\no que o PARCEIRO promete; o banco não credita valor nenhum. Ofertas e\nbanners podem ser só para assinantes de alguns planos: a vitrine já vem\nfiltrada para a conta da sessão.\n"
    },
    {
      "name": "Tarifas",
      "description": "Tarifas por serviço (funcionalidade `service_fees`): a tabela vigente\npara o público da conta (pessoa física ou jurídica) e a cotação da\ntarifa de uma operação antes de confirmá-la. Catálogo fechado de oito\nserviços: `PIX_OUT`, `PIX_IN_QR`, `TED_OUT`, `INTERNAL_OUT`,\n`BILLET_ISSUE`, `BILLET_SETTLED`, `BILL_PAYMENT`, `TOPUP`. Preço fixo\nou percentual (pontos-base, piso e teto, arredondado para baixo);\nisenções legais de pessoa física travadas no servidor (Pix enviado\nsempre isento; as duas primeiras transferências internas do mês civil).\nA tarifa é lançada junto da operação, como linha própria no extrato\n(`kind: TARIFF`, `feeOf` aponta para a operação), e estornada junto\nquando a operação falha. Split da receita: a instituição pode\nratear a tarifa (e a mensalidade do plano) com contas de clientes da\nlista de destinos aprovada pelo backoffice; o destino recebe uma linha\nprópria no extrato (`kind: FEE_SHARE_RECEIVED`, \"Repasse · <descrição>\"),\nsem identificar o pagador, e o estorno da tarifa estorna o repasse. A\ntabela e a cotação servem à sessão e à chave de API com `accounts:read`\ne, nas saídas por chave, `expectedFeeCents` é obrigatório. Com a\nfuncionalidade desligada, `403 FEATURE_DISABLED` (`details.feature:\nservice_fees`), nenhuma operação cobra tarifa e nada é repassado.\n"
    },
    {
      "name": "Recarga",
      "description": "Recarga de celular (funcionalidade `topup`). As rotas existiam fora do\ncontrato; entram aqui porque a tarifa `TOPUP` (funcionalidade\n`service_fees`) passou por elas: `POST /v1/topups` aceita\n`expectedFeeCents`, responde `409 FEE_CHANGED` e `422` como as demais\noperações de saída, e devolve a tarifa em `transaction` (`feeCents`,\n`feeTransactionId`, `feeReason`, `feeStatus`). Só de sessão. Sem\nfornecedor de recarga configurado, a lista responde `available: false`\ne as demais rotas `503`.\n"
    },
    {
      "name": "Aprovação conjunta",
      "description": "Aprovação conjunta da conta PJ (funcionalidade `approvals`). As rotas\nexistiam fora do contrato; a de aprovar entra aqui porque a tarifa\n(funcionalidade `service_fees`) passou por ela: quem aprova vê a\ntarifa da execução e a confirma em `expectedFeeCents`. Só de sessão.\n"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Primeiros passos"
        ],
        "summary": "Verifica se o serviço está no ar",
        "description": "Não consulta o provedor bancário de propósito: um BaaS lento faria o\nbalanceador tirar do ar uma instância perfeitamente saudável.\n",
        "security": [],
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "No ar",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "ok"
                      ]
                    },
                    "time": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/ready": {
      "get": {
        "tags": [
          "Primeiros passos"
        ],
        "summary": "Verifica se a instância está apta a receber tráfego",
        "description": "Separado do `/health` de propósito: vivo e apto são perguntas\ndiferentes, com consequências diferentes. O supervisor reinicia quem\nnão está vivo; o balanceador tira do ar quem não está apto. Misturar as\nduas faz um banco lento reiniciar todas as instâncias ao mesmo tempo, e\né assim que uma indisponibilidade parcial vira total.\n\nAo contrário do `/health`, este confere o banco de dados.\n",
        "security": [],
        "operationId": "getReady",
        "responses": {
          "200": {
            "description": "Apta",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "ready"
                      ]
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Dependência essencial indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "unavailable"
                      ]
                    },
                    "reason": {
                      "type": "string",
                      "examples": [
                        "repositório indisponível"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/features": {
      "get": {
        "tags": [
          "Primeiros passos"
        ],
        "summary": "Lista as funcionalidades ligadas nesta instalação",
        "description": "Cada instalação liga só o que o seu provedor bancário oferece. Esta\nrota diz o que está ligado, para a tela esconder o que não está em vez\nde mostrar um botão que vai responder `403 FEATURE_DISABLED`.\n\nPública, como o `/health`: a tela de entrada precisa saber, antes do\nlogin, se oferece \"abrir conta\". A resposta é a mesma para todo mundo e\nsó muda quando a instalação é reconfigurada; não traz provedor,\nambiente nem configuração.\n\nTraz também o nome público da instituição prestadora da instalação\n(`institution`), que a tela imprime na linha \"Instituição\" dos\ncomprovantes.\n",
        "security": [],
        "operationId": "listFeatures",
        "responses": {
          "200": {
            "description": "Funcionalidades ligadas",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Features"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/app/versions": {
      "get": {
        "tags": [
          "Primeiros passos"
        ],
        "summary": "Versão do app que a instalação anuncia",
        "description": "A versão mais nova do app publicada para cada plataforma, a mínima que\nainda opera e onde atualizar. O app compara com a própria versão (o\n`version` do `app.json`): abaixo de `latestVersion`, avisa que há\nversão nova; abaixo de `minimumVersion`, bloqueia o uso até atualizar.\n\nPública, como `/v1/features`: o aviso aparece também na tela de\nentrada, antes do login. A resposta é a mesma para todo mundo e só muda\nquando a instalação é reconfigurada (`LUME_APP_<PLATAFORMA>_*`).\nPlataforma não configurada não vem; sem nenhuma, o corpo é `{}` e o\napp segue sem aviso.\n",
        "security": [],
        "operationId": "getAppVersions",
        "responses": {
          "200": {
            "description": "Versões anunciadas, por plataforma",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppVersions"
                },
                "example": {
                  "ios": {
                    "latestVersion": "0.2.0",
                    "minimumVersion": "0.1.0",
                    "updateUrl": "itms-beta://beta.itunes.apple.com/v1/app/6828064942"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/onboarding": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Abre uma conta",
        "description": "Pública, como o login: quem abre conta ainda não tem credencial. O\ncaminho depende de `accountOpening.mode` em `GET /v1/features`.\n\n**E-mail confirmado antes**, em todos os modos: `emailVerificationId` é\no pedido de `POST /v1/onboarding/email-verifications` confirmado em\n`.../{id}/confirm` para este mesmo `email`. Ele é consumido depois de\ntodas as conferências do pedido e antes de qualquer gravação (e, nos\nmodos do provedor, antes de o cliente ir ao provedor): um campo\ninválido não o gasta, e a abertura com ele ausente, de outro e-mail, não\nconfirmado, vencido ou já usado é `422 INVALID_REQUEST`, sem criar nada.\nSe a gravação falhar depois do consumo, confirme o e-mail de novo.\n\n**Documento já cadastrado** responde como o cadastro recebido (`201`,\ncom os mesmos campos), **sem criar nada**: a rota é pública, e uma\nrecusa distinta diria a qualquer um quem é cliente. Quem é avisado é o\ndono do documento, pelo e-mail que ele cadastrou. A conferência vem\ndepois do consumo da confirmação do e-mail, então cada tentativa custa\num e-mail confirmado.\n\n**Abertura parada no meio** (modos do provedor): quando o e-mail\nconfirmado é o MESMO do cadastro existente, o titular ainda não é\ncliente de fato (sem autenticador, todas as contas próprias em\nabertura, sem representar ninguém) e o processo dele é do mesmo modo e\nainda está na coleta, a abertura é retomada: a senha deste pedido passa\na valer, as sessões do titular caem e o `onboardingToken` devolvido é o\ndele, para seguir de onde parou. Nada é criado de novo.\n\n**Contato já cadastrado no provedor** (modos do provedor): quando o\nprovedor recusa criar o cliente porque o celular ou o e-mail já estão\nem outro cadastro lá, a resposta é `409 CONFLICT` com a mensagem que\ndiz qual dado trocar e `details.field` (`phone`, `email`, ou vazio\nquando o provedor não diz qual). Nada é criado, e a confirmação do\ne-mail já foi consumida: o próximo pedido precisa de outra.\n\n**`own`** (fluxo do Lume): o cadastro inteiro vai neste pedido, com\n`selfie` e `documents`, e a conta nasce em análise (`PENDING`). Quem\ndecide é a análise no backoffice. A resposta traz `account`.\n\n**`hosted`** (o provedor financeiro conduz o cadastro): este pedido\ncria o titular e o cliente no provedor; a tela de cadastro do\nprovedor, onde a pessoa envia os documentos e faz a biometria, vem de\n`POST /v1/onboarding/hosted-link`, com o `onboardingToken` (a abertura\nnão devolve `url`). `selfie` e `documents` são recusados (`422`). O celular é\nobrigatório (é por ele que o provedor manda o link da biometria); na\nempresa, `email` e `phone` são os do representante legal que fará a\nbiometria, e `businessType` e `tradeName` são obrigatórios. Com\n`birthDate`, menor de 18 anos é recusado. A resposta traz o\n`onboardingToken`, que lê o estágio em `GET /v1/onboarding` e pede a\ntela de novo em `POST /v1/onboarding/hosted-link` por uma hora; depois\ndisso, a pessoa entra com a senha (conta em análise já entra) e usa a\nsessão. A conta é liberada quando o provedor aprova o cadastro.\n\n**`own` com o provedor financeiro decidindo o cadastro** (a Azify, que\ndeclara o onboarding próprio): a tela é a do Lume, mas o cadastro vai\nao provedor pela API, e quem decide é ele. Este pedido cria o titular e\no cliente no provedor, como no `hosted`, e a resposta traz o\n`onboardingToken`, sem `url` e sem `account`. Pessoa física:\n`birthDate` é obrigatório e menor de 18 anos é recusado. Empresa: só\ncom a funcionalidade `business_opening_own` ligada (em construção,\ndesligada por padrão; desligada, CNPJ é recusado com `422`), e só\n`businessType` `MEI` ou `EI`, com exatamente um representante (quem\nabre a conta e faz a biometria); `LTDA`, `SA` e `TS` são recusadas com\n`422`, e `birthDate` não vai (o do representante vai nos dados do\ncadastro). `selfie` e `documents` são recusados (`422`), porque vão na\nbiometria e, na empresa, em `POST /v1/onboarding/documents`. A pessoa\nsegue por `PUT /v1/onboarding/profile` (na empresa, depois do\ndocumento), `GET /v1/onboarding/terms`, a biometria\n(`POST /v1/onboarding/biometrics/session`) e\n`POST /v1/onboarding/complete`. Sem a biometria configurada na\ninstalação, este pedido responde `503` antes de criar qualquer coisa.\n",
        "security": [],
        "operationId": "openAccount",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "document",
                  "email",
                  "password",
                  "emailVerificationId"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Nome completo ou razão social."
                  },
                  "document": {
                    "type": "string",
                    "maxLength": 24,
                    "description": "CPF ou CNPJ",
                    "com ou sem pontuação.": null
                  },
                  "email": {
                    "type": "string",
                    "maxLength": 254
                  },
                  "emailVerificationId": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "O pedido de código do e-mail, confirmado\n(`POST /v1/onboarding/email-verifications/{id}/confirm`).\nVale 15 minutos depois de confirmado, uma vez, para este `email`.\n",
                    "examples": [
                      "ver_8c1d2e4b9a7f40c1b2d3e4f5a6b7c8d9"
                    ]
                  },
                  "phone": {
                    "type": "string",
                    "maxLength": 32,
                    "description": "Celular com DDD. Obrigatório no modo `hosted`."
                  },
                  "city": {
                    "type": "string",
                    "maxLength": 80,
                    "examples": [
                      "Niterói, RJ"
                    ]
                  },
                  "password": {
                    "type": "string"
                  },
                  "referralCode": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "birthDate": {
                    "type": "string",
                    "format": "date",
                    "maxLength": 10,
                    "description": "Só pessoa física. Obrigatório quando o provedor decide o cadastro no modo `own`."
                  },
                  "businessType": {
                    "type": "string",
                    "enum": [
                      "MEI",
                      "EI",
                      "LTDA",
                      "SA",
                      "TS"
                    ],
                    "description": "Natureza jurídica da empresa, obrigatória nos modos do\nprovedor. No `own` com o provedor decidindo, só `MEI` ou\n`EI`, e só com `business_opening_own` ligada.\n"
                  },
                  "tradeName": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Nome fantasia. Só empresa."
                  },
                  "stateRegistration": {
                    "type": "string",
                    "maxLength": 32,
                    "description": "Inscrição estadual. Só empresa."
                  },
                  "representatives": {
                    "type": "array",
                    "maxItems": 10,
                    "description": "Quem responde pela empresa. Ao menos um, no máximo dez; só empresa.",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "document"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 120
                        },
                        "document": {
                          "type": "string",
                          "maxLength": 24,
                          "description": "CPF."
                        },
                        "role": {
                          "type": "string",
                          "maxLength": 60
                        },
                        "email": {
                          "type": "string",
                          "maxLength": 254
                        },
                        "phone": {
                          "type": "string",
                          "maxLength": 32
                        }
                      }
                    }
                  },
                  "selfie": {
                    "type": "string",
                    "description": "Foto do rosto, em base64. Só o fluxo do Lume (modo `own` sem o provedor decidindo) e pessoa física."
                  },
                  "documents": {
                    "type": "array",
                    "description": "Arquivos do cadastro, em base64. Só o fluxo do Lume (modo `own` sem o provedor decidindo).",
                    "items": {
                      "type": "object",
                      "properties": {
                        "kind": {
                          "type": "string"
                        },
                        "content": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cadastro recebido e conta em análise. `account` no fluxo do Lume;\n`stage`, `onboardingToken` e `expiresIn` nos modos do provedor\n(`hosted` e `own` com o provedor decidindo). No `hosted`, a tela\ndo provedor vem de `POST /v1/onboarding/hosted-link`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "message"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "IN_REVIEW"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "branch": {
                          "type": "string"
                        },
                        "number": {
                          "type": "string"
                        }
                      }
                    },
                    "stage": {
                      "$ref": "#/components/schemas/OnboardingStage"
                    },
                    "onboardingToken": {
                      "type": "string",
                      "description": "Credencial de acompanhamento desta abertura. Não abre sessão."
                    },
                    "expiresIn": {
                      "type": "integer",
                      "examples": [
                        3600
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Conflito passageiro na gravação da abertura (`CONFLICT`): repita o\npedido. Documento de cliente **não** responde 409 nesta rota (veja\na descrição); só a abertura pelo atendimento, autenticada, o faz.\n\nExceção (PROVISÓRIA, 27/10/2026): o documento de quem ainda está\nabrindo conta (contas em abertura, cadastro não enviado), com uma\nsenha diferente da do cadastro ou com o autenticador já\nconfigurado, responde 409 com `details.reason =\nONBOARDING_IN_PROGRESS`: a tela pede a senha e segue por\n`POST /v1/onboarding/resume`. Com a mesma senha (e sem\nautenticador), a resposta é `201` com o token de acompanhamento do\npróprio titular e `onboarding: true`: a abertura continua de onde\nparou.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campo faltando ou inválido, inclusive senha fora da política (mínimo de\n10 caracteres, com letras e ao menos um número, símbolo ou espaço)\n(`INVALID_REQUEST`); e-mail sem confirmação válida\n(`INVALID_REQUEST`, \"Confirme o e-mail com o código enviado antes de\nabrir a conta.\"); campo de texto acima do `maxLength` ou mais\nrepresentantes que o `maxItems` (`INVALID_REQUEST`, \"O campo ... é\nlongo demais.\", contado em caracteres depois de aparar os espaços;\no valor nunca volta na resposta); ou o provedor financeiro recusou\no cadastro (`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "O arranjo de pagamento está indisponível (`PROVIDER_ERROR`); ou a\ninstância já processa o máximo de aberturas simultâneas\n(`RATE_LIMITED`, com `Retry-After`): repita em instantes.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Estágio da abertura de conta",
        "description": "A abertura decidida pelo provedor financeiro (modo `hosted`, ou `own`\ncom o provedor decidindo) do próprio titular: o do `onboardingToken` ou\no da sessão. Quem abriu conta pelo\nfluxo do Lume recebe `404`. `reason` e `pending` só aparecem com texto\nque pode ser mostrado ao cliente.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "getOnboarding",
        "responses": {
          "200": {
            "description": "Estágio atual",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "mode",
                    "stage",
                    "personType"
                  ],
                  "properties": {
                    "mode": {
                      "type": "string",
                      "examples": [
                        "hosted",
                        "own"
                      ]
                    },
                    "stage": {
                      "$ref": "#/components/schemas/OnboardingStage"
                    },
                    "personType": {
                      "type": "string",
                      "enum": [
                        "PF",
                        "PJ"
                      ]
                    },
                    "documents": {
                      "type": "array",
                      "description": "Só na abertura própria de empresa (`own`, `PJ`): os\ndocumentos já aceitos pelo provedor, sem o arquivo, para a\ntela retomar sem pedir de novo.\n",
                      "items": {
                        "$ref": "#/components/schemas/OnboardingDocument"
                      }
                    },
                    "businessType": {
                      "type": "string",
                      "enum": [
                        "MEI",
                        "EI"
                      ],
                      "description": "Só na abertura própria de empresa, com `documents`. A natureza jurídica, para a tela pedir o documento pelo nome."
                    },
                    "reason": {
                      "type": "string"
                    },
                    "pending": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "title": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/onboarding/email-verifications": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Envia o código de confirmação do e-mail da abertura",
        "description": "Primeiro passo da abertura de conta, em todos os modos: o Lume envia um\ncódigo de seis dígitos ao e-mail informado. A pessoa o confirma em\n`POST /v1/onboarding/email-verifications/{id}/confirm` e abre a conta\ncom o `verificationId` em `POST /v1/onboarding`. Pública, como a\nabertura: ainda não há titular nem conta.\n\nO e-mail é gravado sem espaço nas pontas e em minúsculas; a abertura\nconfere o mesmo e-mail. O código vale 10 minutos. Um pedido novo ao\nmesmo e-mail cancela o anterior. Limites (`429 RATE_LIMITED`, com\n`Retry-After` em segundos): 60 segundos entre dois envios ao mesmo\ne-mail; 3 por hora e 5 por dia ao mesmo e-mail; 10 por hora por\nendereço IP. E-mail com cinco códigos errados nos últimos 15 minutos\nnão recebe outro até a janela passar (`429 TOO_MANY_ATTEMPTS`).\n\nO código nunca aparece em resposta nem em log.\n",
        "security": [],
        "operationId": "requestSignupEmailVerification",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "examples": [
                      "rosalina.alencar@email.com.br"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Código enviado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verificationId": {
                      "type": "string",
                      "examples": [
                        "ver_8c1d2e4b9a7f40c1b2d3e4f5a6b7c8d9"
                      ]
                    },
                    "sentTo": {
                      "type": "string",
                      "description": "O e-mail mascarado, para a tela.",
                      "examples": [
                        "ro***@email.com.br"
                      ]
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Até quando o código vale."
                    },
                    "resendAfter": {
                      "type": "string",
                      "format": "date-time",
                      "description": "A partir de quando dá para pedir outro ao mesmo e-mail."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "E-mail inválido (`INVALID_REQUEST`), instalação sem fornecedor de\ne-mail (`INVALID_REQUEST`), ou o fornecedor recusou o destino\n(`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de envio (`RATE_LIMITED`) ou e-mail travado por códigos\nerrados (`TOO_MANY_ATTEMPTS`). `Retry-After` diz quantos segundos\nesperar.\n",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "O fornecedor de e-mail não respondeu, ou a abertura está\nindisponível nesta instalação (`PROVIDER_ERROR`). O código pode ter\nsaído; o pedido não vale e conta nos limites.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/onboarding/email-verifications/{id}/confirm": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Confirma o código do e-mail da abertura",
        "description": "A pessoa digita o código recebido. Certo, o pedido fica confirmado e\nvale 15 minutos, uma vez, para abrir a conta com aquele e-mail\n(`emailVerificationId` em `POST /v1/onboarding`).\n\nCódigo errado é `422 INVALID_REQUEST` com `details.remainingAttempts`.\nNo quinto erro do pedido, ou no quinto do e-mail em 15 minutos somando\ntodos os pedidos, a resposta é `429 TOO_MANY_ATTEMPTS` e o pedido não\nserve mais; espere o `Retry-After` e peça outro código.\n\nPedido que não existe é `404`. Pedido já confirmado, usado,\nsubstituído ou vencido é `409 CONFLICT`: peça outro.\n",
        "security": [],
        "operationId": "confirmSignupEmailVerification",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "examples": [
                      "482913"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "E-mail confirmado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verificationId": {
                      "type": "string"
                    },
                    "verifiedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "validUntil": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Até quando a confirmação serve à abertura."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Pedido já confirmado, usado, substituído ou vencido (`CONFLICT`). Peça outro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Código incorreto (`INVALID_REQUEST`), com `details.remainingAttempts`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Pedido ou e-mail travado por códigos errados (`TOO_MANY_ATTEMPTS`).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/onboarding/hosted-link": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Pede de novo a tela de cadastro do provedor",
        "description": "É por aqui que a tela de cadastro do provedor chega ao front, logo\ndepois da abertura e sempre que a pessoa precisa voltar a ela. Só\nenquanto o cadastro não foi enviado para análise\n(`CUSTOMER_CREATED` ou `IN_PROGRESS`); depois, `409`. Sem corpo.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "createOnboardingHostedLink",
        "responses": {
          "200": {
            "description": "Endereço da tela de cadastro",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "url"
                  ],
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O cadastro já foi enviado, a conta não está mais em abertura (bloqueada ou encerrada), ou a abertura não é do modo `hosted` (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "O provedor financeiro recusou o pedido (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/onboarding/resume": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Continua a abertura parada no meio, pela página pública",
        "description": "Para quem começou a abertura e parou na coleta (PROVISÓRIO,\n27/10/2026): com o documento e a senha escolhida na abertura, devolve\num `onboardingToken` novo, o mesmo da abertura recém-feita, que só\nalcança as rotas de `/v1/onboarding`. Não abre sessão e não dá acesso\nà conta. Quem já configurou o autenticador (entrou pela área logada)\ninforma também o código dele (`code`), que vale uma vez só.\n\nVale enquanto o titular não é cliente de fato: todas as contas em\nabertura, sem representação, e o cadastro ainda não enviado\n(`CUSTOMER_CREATED` ou `IN_PROGRESS`). Fora disso, `409`, mas só com a\nsenha certa; documento inexistente responde como senha errada. As\ntentativas contam junto com as do login (`429`).\n",
        "security": [],
        "operationId": "resumeOnboarding",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document",
                  "password"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ",
                    "com ou sem máscara.": null
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  },
                  "code": {
                    "type": "string",
                    "description": "Código do aplicativo autenticador, só para quem já o configurou. Sem ele, `401` com `details.field = \"code\"`, e só depois da senha certa."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Abertura retomada. `mode` é o da abertura (`own` ou `hosted`); no `hosted`, a tela do provedor vem de `POST /v1/onboarding/hosted-link`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "onboardingToken",
                    "stage",
                    "mode"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "IN_REVIEW"
                      ]
                    },
                    "stage": {
                      "type": "string",
                      "examples": [
                        "CUSTOMER_CREATED",
                        "IN_PROGRESS"
                      ]
                    },
                    "mode": {
                      "type": "string",
                      "examples": [
                        "own",
                        "hosted"
                      ]
                    },
                    "onboardingToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "description": "Validade do token",
                      "em segundos.": null
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Corpo ilegível (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Documento ou senha incorretos, ou o código do autenticador faltando ou incorreto, com `details.field = \"code\"` (`UNAUTHORIZED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "A abertura não está mais em preenchimento (enviada, aprovada, conta ativa) (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Documento inválido (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/onboarding/profile": {
      "put": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Envia os dados do cadastro ao provedor",
        "description": "Só a abertura própria pelo provedor (`own` com o provedor decidindo),\nenquanto o cadastro não foi enviado\n(`CUSTOMER_CREATED` ou `IN_PROGRESS`); depois, `409`. Pode repetir até\na conclusão: cada envio regrava o cadastro no provedor.\n\n**Pessoa física:** nome e data de nascimento não vão aqui: são os\ninformados em `POST /v1/onboarding`. Mãe, PEP, endereço e renda vão ao\nprovedor e, depois que ele os aceita, ficam gravados também no Lume, na\nqualificação do cliente que o backoffice mostra; o nome social vai só\nao provedor. `birthDate`, `occupation` e `business` são recusados\n(`400`), como qualquer campo fora do contrato.\n\n**Empresa MEI ou EI** (`business_opening_own`; desligada, `403\nFEATURE_DISABLED`): `motherName`, `pep`, `address` e `declaredIncome`\nsão os do representante legal, e `birthDate` (o dele; menor de 18 anos\né recusado), `occupation` e `business` são obrigatórios. `socialName`\ne `averageMonthlyIncomeCents` não valem (a renda média é a da empresa,\nem `business`). O nome, o CPF e o contato do representante são os da\nabertura. O documento da empresa vai antes, em\n`POST /v1/onboarding/documents`; sem ele, `409`. Os dados do\nrepresentante vão só ao provedor (PROVISÓRIO): a qualificação do Lume\né do titular, e o titular é a empresa.\n\nO primeiro envio também pede ao provedor o processo biométrico do\ncliente, que vai junto (a Azify o exige no cadastro) e é o mesmo que a\nbiometria usa depois (na empresa, vai no representante: PROVISÓRIO).\nSem a biometria do provedor configurada na instalação, `503`.\nCom sucesso, o estágio vai a `IN_PROGRESS`; se o provedor falhar, o\nestágio não muda.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "updateOnboardingProfile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "motherName",
                  "pep",
                  "address",
                  "declaredIncome"
                ],
                "properties": {
                  "socialName": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Nome social",
                    "se houver. Só pessoa física.": null
                  },
                  "motherName": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 200
                  },
                  "pep": {
                    "type": "string",
                    "enum": [
                      "NONE",
                      "SELF",
                      "RELATED"
                    ],
                    "description": "Exposição política (pessoa exposta, ou relacionada a uma)."
                  },
                  "address": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "street",
                      "number",
                      "neighborhood",
                      "city",
                      "state",
                      "zipCode"
                    ],
                    "properties": {
                      "street": {
                        "type": "string",
                        "maxLength": 200
                      },
                      "number": {
                        "type": "string",
                        "maxLength": 20,
                        "examples": [
                          "45",
                          "S/N"
                        ]
                      },
                      "complement": {
                        "type": "string",
                        "maxLength": 100
                      },
                      "neighborhood": {
                        "type": "string",
                        "maxLength": 200
                      },
                      "city": {
                        "type": "string",
                        "maxLength": 200
                      },
                      "state": {
                        "type": "string",
                        "examples": [
                          "RJ"
                        ],
                        "description": "UF",
                        "uma das 27.": null
                      },
                      "country": {
                        "type": "string",
                        "examples": [
                          "BR"
                        ],
                        "description": "ISO 3166-1 alfa-2. Padrão: BR."
                      },
                      "zipCode": {
                        "type": "string",
                        "examples": [
                          "24220-031",
                          "24220031"
                        ],
                        "description": "CEP",
                        "com ou sem hífen.": null
                      }
                    }
                  },
                  "declaredIncome": {
                    "type": "string",
                    "examples": [
                      "LESS_THAN_ONE_THOUSAND",
                      "OVER_TWENTY_THOUSAND"
                    ],
                    "description": "Faixa de renda, no código do provedor. Hoje o Lume confere\nsó o formato (maiúsculas e `_`); a lista fechada das faixas\nentra quando o provedor confirmar.\n"
                  },
                  "averageMonthlyIncomeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Renda média mensal, em centavos. Opcional; zero não vai ao provedor. Só pessoa física."
                  },
                  "birthDate": {
                    "type": "string",
                    "format": "date",
                    "maxLength": 10,
                    "description": "Data de nascimento do representante legal. Só empresa, e obrigatória nela."
                  },
                  "occupation": {
                    "type": "string",
                    "examples": [
                      "OCP0001"
                    ],
                    "description": "Ocupação do representante legal, no código do provedor\n(`OCP0001` a `OCP0105`, lista do artigo 2.3.4 da Azify; sem\ndiferença de maiúscula). Só empresa, e obrigatória nela.\n"
                  },
                  "business": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "declaredAnnualBilling",
                      "address"
                    ],
                    "description": "A própria empresa. Só empresa, e obrigatório nela.",
                    "properties": {
                      "declaredAnnualBilling": {
                        "type": "string",
                        "enum": [
                          "UP_TO_FIFTY_THOUSAND",
                          "MORE_THAN_FIFTY_THOUSAND_UP_TO_ONE_HUNDRED_THOUSAND",
                          "MORE_THAN_ONE_HUNDRED_THOUSAND_UP_TO_TWO_HUNDRED_AND_FIFTY_THOUSAND",
                          "MORE_THAN_TWO_HUNDRED_AND_FIFTY_THOUSAND_UP_TO_FIVE_HUNDRED_THOUSAND",
                          "MORE_THAN_FIVE_HUNDRED_THOUSAND_UP_TO_ONE_MILLION",
                          "MORE_THAN_ONE_MILLION_UP_TO_TWO_MILLION_AND_FIVE_HUNDRED_THOUSAND",
                          "MORE_THAN_TWO_MILLION_AND_FIVE_HUNDRED_THOUSAND_UP_TO_FIVE_MILLION",
                          "MORE_THAN_FIVE_MILLION_UP_TO_TEN_MILLION",
                          "MORE_THAN_TEN_MILLION_UP_TO_TWENTY_FIVE_MILLION",
                          "MORE_THAN_TWENTY_FIVE_MILLION_UP_TO_FIFTY_MILLION",
                          "MORE_THAN_FIFTY_MILLION_UP_TO_ONE_HUNDRED_MILLION",
                          "MORE_THAN_ONE_HUNDRED_MILLION_UP_TO_TWO_HUNDRED_AND_FIFTY_MILLION",
                          "MORE_THAN_TWO_HUNDRED_AND_FIFTY_MILLION_UP_TO_FIVE_HUNDRED_MILLION",
                          "MORE_THAN_FIVE_HUNDRED_MILLION",
                          "EXEMPT_COMPANY",
                          "INACTIVE_COMPANY"
                        ],
                        "description": "Faixa de faturamento anual, no código do provedor (sem diferença de maiúscula)."
                      },
                      "averageMonthlyIncomeCents": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Renda média mensal da empresa, em centavos. Opcional; zero não vai ao provedor."
                      },
                      "address": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "street",
                          "number",
                          "neighborhood",
                          "city",
                          "state",
                          "zipCode"
                        ],
                        "description": "Endereço comercial, com as mesmas regras de `address`.",
                        "properties": {
                          "street": {
                            "type": "string",
                            "maxLength": 200
                          },
                          "number": {
                            "type": "string",
                            "maxLength": 20
                          },
                          "complement": {
                            "type": "string",
                            "maxLength": 100
                          },
                          "neighborhood": {
                            "type": "string",
                            "maxLength": 200
                          },
                          "city": {
                            "type": "string",
                            "maxLength": 200
                          },
                          "state": {
                            "type": "string",
                            "examples": [
                              "SP"
                            ]
                          },
                          "country": {
                            "type": "string",
                            "examples": [
                              "BR"
                            ]
                          },
                          "zipCode": {
                            "type": "string",
                            "examples": [
                              "01311-000"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dados aceitos pelo provedor",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "stage"
                  ],
                  "properties": {
                    "stage": {
                      "$ref": "#/components/schemas/OnboardingStage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Corpo ilegível ou com campo que o contrato não tem (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O cadastro já foi enviado, a conta não está mais em abertura (bloqueada, encerrada ou já ativa), a abertura não é a própria, ou a empresa ainda não enviou o documento (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campo faltando ou inválido (`INVALID_REQUEST`), ou o provedor recusou os dados (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/onboarding/terms": {
      "get": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Termos do provedor para o aceite",
        "description": "Os documentos de termos do provedor que a pessoa aceita ao concluir, em\n`urls`, na ordem do provedor (desde 25/10/2026, a Azify devolve dois).\n`url` repete o primeiro, para o cliente que ainda não lê a lista. O\nLume guarda a lista e a versão como o que foi mostrado; a conclusão só\naceita essa mesma lista. Pedir de novo antes do aceite troca o que foi\nmostrado; depois do aceite, devolve os termos aceitos. A versão é a do\nprovedor quando ele a informar; até lá, é a impressão digital da lista\n(`url-sha256:…`; com um documento só, a do endereço, como antes). Só a\nabertura própria, antes do envio do cadastro.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "getOnboardingTerms",
        "responses": {
          "200": {
            "description": "Termos a aceitar",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "url",
                    "urls",
                    "version"
                  ],
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "description": "O primeiro documento de `urls`."
                    },
                    "urls": {
                      "type": "array",
                      "minItems": 1,
                      "maxItems": 5,
                      "items": {
                        "type": "string",
                        "format": "uri"
                      },
                      "description": "Todos os documentos a mostrar e aceitar, na ordem do provedor."
                    },
                    "version": {
                      "type": "string",
                      "examples": [
                        "url-sha256:3b1f0c9a7d2e4f61"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O cadastro já foi enviado, a conta não está mais em abertura (bloqueada, encerrada ou já ativa), ou a abertura não é a própria (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "O provedor recusou o pedido (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/onboarding/complete": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Aceita os termos e envia o cadastro para análise",
        "description": "Exige os dados enviados (`IN_PROGRESS`; em `CUSTOMER_CREATED`, `409`),\n`acceptTerms: true` e `termsUrls` igual à lista `urls` devolvida por\n`GET /v1/onboarding/terms`, inteira e na mesma ordem (ou, no cliente\nque ainda não manda a lista, `termsUrl` igual ao único documento); sem\nisso, `422` e nada vai ao provedor. Com mais de um documento, o\n`termsUrl` sozinho não prova o aceite e volta `422`.\nExige também a biometria aberta (alguma sessão pedida em\n`POST /v1/onboarding/biometrics/session` para esta abertura; o processo\nbiométrico, que nasce no envio dos dados, não basta); sem ela, `409`, sem gravar o\naceite nem chamar o provedor. Na empresa, exige também o documento\naceito em `POST /v1/onboarding/documents` e os dados do cadastro\nenviados depois dele (documento trocado depois dos dados pede os dados\nde novo), sem envio do documento em andamento; sem isso, `409`. O aceite é gravado com data e hora, endereço, versão, IP e user agent\nantes de o provedor concluir, e o primeiro aceite nunca é regravado.\nSe o provedor falhar, o estágio fica em `IN_PROGRESS` e a pessoa pode\nrepetir. Com sucesso, o estágio vai a `SUBMITTED` (ou ao que o evento\ndo provedor já trouxe), e a conta é liberada quando o provedor aprova o\ncadastro. Depois do envio, `409`.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "completeOnboarding",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "acceptTerms"
                ],
                "properties": {
                  "acceptTerms": {
                    "type": "boolean",
                    "description": "Precisa ser `true`."
                  },
                  "termsUrls": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5,
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "description": "A lista `urls` de `GET /v1/onboarding/terms`, inteira e na mesma ordem."
                  },
                  "termsUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Forma antiga: o `url` de `GET /v1/onboarding/terms`. Só vale com um documento."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cadastro enviado para análise",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "stage"
                  ],
                  "properties": {
                    "stage": {
                      "$ref": "#/components/schemas/OnboardingStage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Corpo ilegível ou com campo que o contrato não tem (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Dados ainda não enviados, biometria ainda não aberta, documento da empresa ausente ou trocado depois dos dados, cadastro já enviado, conta fora de abertura (bloqueada, encerrada ou já ativa), ou a abertura não é a própria (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Sem o aceite, termos não abertos ou outro endereço\n(`INVALID_REQUEST`), ou o provedor recusou a conclusão\n(`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/onboarding/documents": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Envia o documento da empresa ao provedor",
        "description": "Só a abertura própria de empresa MEI ou EI (`own` com o provedor\ndecidindo, `PJ`), com a funcionalidade `business_opening_own` ligada\n(desligada, `403 FEATURE_DISABLED`), enquanto o cadastro não foi\nenviado (`CUSTOMER_CREATED` ou `IN_PROGRESS`); depois, `409`. Pessoa\nfísica, `409`.\n\n`kind` é `LEGAL_DOCUMENTS`: o Certificado da Condição de\nMicroempreendedor Individual (MEI) ou o Requerimento de Empresário\n(EI). `file` é o arquivo em base64, com ou sem o prefixo de data URL.\nSó PDF, conferido pelos bytes (e não pelo nome ou pelo tipo\ndeclarado), até 3 MB (PROVISÓRIO: o mesmo teto dos outros documentos,\naté o provedor informar o dele).\n\nO arquivo vai direto ao provedor e **não fica guardado no Lume**: ficam\no tipo, o tamanho, a impressão digital (sha256) e a data. A resposta\nnunca traz o id nem o endereço do documento no provedor. O endereço é\nguardado só enquanto o cadastro está na coleta, e no máximo 7 dias\n(PROVISÓRIO); passado isso, os dados do cadastro pedem o documento de\nnovo (`409`), e o mesmo arquivo pode ir outra vez.\n\nUm documento por abertura: o novo aceito substitui o anterior, e\ndepois dele os dados do cadastro (`PUT /v1/onboarding/profile`) têm de\nir de novo. O mesmo arquivo já aceito responde `200` com\n`reused: true`, sem ir ao provedor. Sem resposta do provedor (prazo\nesgotado), `503`, e o mesmo arquivo fica travado por 15 minutos\n(`409`) em vez de sair de novo às cegas: o provedor pode tê-lo\nrecebido. Dois envios ao mesmo tempo não saem juntos (`409` para o\nsegundo). Até 10 envios ao provedor por abertura em 24 horas, aceitos\nou recusados (o mesmo arquivo reaproveitado não conta); acima, `429`\n(PROVISÓRIO). A instância processa poucos envios ao mesmo tempo:\ncheia, `503 RATE_LIMITED` com `Retry-After`.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "uploadOnboardingDocument",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "kind",
                  "file"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "LEGAL_DOCUMENTS"
                    ],
                    "description": "Sem diferença de maiúscula."
                  },
                  "file": {
                    "type": "string",
                    "description": "O PDF em base64 (até 3 MB antes da codificação)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O mesmo arquivo já tinha sido aceito; nada foi ao provedor (`reused` verdadeiro).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingDocumentUpload"
                }
              }
            }
          },
          "201": {
            "description": "Documento aceito pelo provedor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingDocumentUpload"
                }
              }
            }
          },
          "400": {
            "description": "Corpo ilegível ou com campo que o contrato não tem (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Cadastro já enviado, conta fora de abertura, abertura que não é a\nprópria de empresa, envio do documento em andamento, ou o envio\nanterior do mesmo arquivo ficou sem resposta há menos de 15\nminutos (`CONFLICT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Arquivo acima de 3 MB (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Tipo inválido, arquivo ilegível ou que não é PDF (`INVALID_REQUEST`), ou o provedor recusou o documento (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Teto de envios do documento nas últimas 24 horas (`RATE_LIMITED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Sem resposta do provedor, ou o provedor indisponível\n(`PROVIDER_ERROR`); ou a instância já processa o máximo de envios\nsimultâneos (`RATE_LIMITED`, com `Retry-After`): repita em instantes.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/onboarding/biometrics/session": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Abre a sessão da biometria do modo próprio",
        "description": "PROPOSTA (AZF-026), até a Azify confirmar o que o processamento exige.\n\nDevolve o que o SDK de biometria do provedor (o Aziface, no web e no\napp) precisa para iniciar, na abertura do modo `own` ainda na coleta\n(`CUSTOMER_CREATED` ou `IN_PROGRESS`). O titular é o do\n`onboardingToken` ou o da sessão; não há corpo.\n\n- `deviceKeyIdentifier`, `productionKey` e `publicKey` vão ao\n  `initialize` do SDK.\n- `processUrl` é o `baseUrl` do SDK: o repasse do Lume\n  (`POST /v1/onboarding/biometrics/process-request`).\n- `sdkToken` vai no cabeçalho `x-token-bearer` do SDK. É uma credencial\n  opaca do Lume, e não a do provedor: vale até `expiresAt` (15 minutos)\n  e 30 chamadas, só para este titular e esta abertura. Pedir uma sessão\n  nova revoga a anterior.\n\nPor titular, no máximo 5 sessões por hora e 20 por dia (PROVISÓRIO,\nAZF-064f); acima disso, `429 TOO_MANY_ATTEMPTS` com `Retry-After`, sem\nchamar o provedor. Cada janela recomeça depois de uma pausa do tamanho\ndela sem sessão nova; quem passa do teto espera a janela inteira.\n",
        "security": [
          {
            "onboardingToken": []
          },
          {
            "session": []
          }
        ],
        "operationId": "createOnboardingBiometricSession",
        "responses": {
          "200": {
            "description": "O que o SDK precisa para iniciar",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "deviceKeyIdentifier",
                    "productionKey",
                    "publicKey",
                    "sdkToken",
                    "processUrl",
                    "expiresAt"
                  ],
                  "properties": {
                    "deviceKeyIdentifier": {
                      "type": "string"
                    },
                    "productionKey": {
                      "type": "string"
                    },
                    "publicKey": {
                      "type": "string"
                    },
                    "sdkToken": {
                      "type": "string"
                    },
                    "processUrl": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O cadastro já foi enviado, a conta não está mais em abertura\n(bloqueada, encerrada ou já ativa), a abertura é do modo `hosted` ou\nainda não tem cadastro no provedor (`CONFLICT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Sessões demais deste titular na janela (`TOO_MANY_ATTEMPTS`): 5 por\nhora ou 20 por dia. `Retry-After` diz quantos segundos esperar.\n",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "O provedor da biometria falhou, não respondeu no prazo de uma\nchamada ou respondeu fora do formato (`PROVIDER_ERROR`). Tente de novo.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          },
          "504": {
            "description": "O pedido passou do prazo da API enquanto esperava o provedor da\nbiometria (`PROVIDER_ERROR`). Tente de novo.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/onboarding/biometrics/process-request": {
      "post": {
        "tags": [
          "Abertura de conta"
        ],
        "summary": "Repassa o pedido do SDK de biometria ao processamento",
        "description": "PROPOSTA (AZF-026), até a Azify confirmar o que o processamento exige.\n\nChamada pelo SDK de biometria (é o `processUrl` da sessão), não pela\ntela. O corpo é o do SDK e é repassado como veio ao processamento do\nprovedor, que recebe a credencial do parceiro: o Lume só confere que é\nJSON com `requestBlob` e não guarda nem registra nada dele. A resposta\ndo processamento volta como veio (status e corpo).\n\nO `externalDatabaseRefID` (o registro na base biométrica) é sorteado\npelo SDK da Aziface a cada `photoMatch` (prefixo fixo e UUID, como\n`aziface_web_<uuid>`), e o Lume o prende (AZF-064f; PROVISÓRIO até a\nAzify dizer como o processamento o usa):\n\n- ausente, vazio ou nulo: o corpo vai como veio;\n- só passa na forma que o SDK sorteia; o campo repetido, outra grafia\n  do nome, um valor que não é texto ou qualquer chave do primeiro\n  nível fora do ASCII também são recusados;\n- o valor é comparado sem diferenciar maiúsculas de minúsculas;\n- o primeiro repasse deste `sdkToken` que traz um valor o fixa nele, e\n  os seguintes só passam com o mesmo; um valor já usado por outro\n  titular é recusado.\n\nA recusa é `422` e nada é repassado. Para outro `photoMatch` (que\nsorteia outro valor), peça uma sessão nova e inicie o SDK com o\n`sdkToken` novo. A inicialização da FaceTec também passa por aqui,\ncom o mesmo corpo, e no SDK web leva um valor sorteado só para ela:\no Lume não a distingue da captura, então ela também fixa o valor.\nUse uma sessão para a inicialização e outra para cada captura.\n\nTeto do corpo: 10 MiB (configurável); acima dele, `413` sem repassar.\nPrazo: 25 s; esgotado, `504`. Falha do processamento não muda a\nabertura: o SDK repete.\n\nUm `401` ou `403` sem o `code` do `Error` do Lume é a resposta do\nprocessamento, devolvida como veio (a credencial do parceiro foi\nrecusada lá, e o Lume pede outra no próximo repasse): repita. O `401`\ncom `code: UNAUTHORIZED` é o do Lume: o `sdkToken` não vale mais, peça\noutra sessão.\n",
        "security": [
          {
            "sdkToken": []
          }
        ],
        "operationId": "forwardOnboardingBiometricRequest",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "requestBlob"
                ],
                "additionalProperties": true,
                "properties": {
                  "requestBlob": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A resposta do processamento, como veio (`responseBlob` e o resto)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "O corpo não é JSON ou não tem `requestBlob` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`sdkToken` ausente, vencido, revogado, sem chamadas, de uma\nabertura que já não está na coleta ou de uma conta que não está\nmais em abertura (`UNAUTHORIZED`). Peça outra sessão.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "description": "O corpo passou do teto (`INVALID_REQUEST`). Nada foi repassado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "O `externalDatabaseRefID` do corpo não vale para esta sessão\n(`INVALID_REQUEST`): fora da forma que o SDK sorteia, repetido, com\noutra grafia, que não é texto, com chave do primeiro nível fora do\nASCII, diferente do que este `sdkToken` já\nfixou, ou já usado por outro titular. Nada foi repassado. Peça uma\nsessão nova.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "O processamento falhou ou respondeu fora do formato (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          },
          "504": {
            "description": "O processamento não respondeu dentro do prazo (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sandbox/keys": {
      "post": {
        "tags": [
          "Primeiros passos"
        ],
        "summary": "Emite uma chave de sandbox",
        "description": "Cria uma chave de API **e** uma conta de testes isolada, com saldo\nfictício de R$ 5.000,00, uma chave PIX já cadastrada e credenciais para\nabrir a mesma conta no internet banking.\n\nSem aprovação e sem confirmação de e-mail: no sandbox nada é real. O que\nexiste é teto — cinco chaves por e-mail por dia e, por endereço de\norigem, dez de imediato e depois uma por minuto (`429` com\n`Retry-After`).\n\n**O segredo aparece uma única vez.** Não há endpoint que o mostre de\nnovo; guardá-lo para poder reexibi-lo transformaria o banco de dados\nnuma lista de credenciais em claro. Perdeu, emite outra.\n\nEste endpoint só existe no sandbox. Em produção ele responde `403`:\nchave de produção é processo comercial, emitida depois da assinatura.\nFora de produção também responde `403` quando a instalação não ligou o\nautoatendimento (`LUME_SANDBOX_KEYS=true`) ou não roda o provedor de\npagamentos simulado: a conta que ele cria só é inofensiva onde o\ndinheiro é fictício.\n",
        "security": [],
        "operationId": "issueSandboxKey",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Para contato e para o teto de emissão. Não é verificado e\nnão autentica nada.\n",
                    "examples": [
                      "dev@integradora.com.br"
                    ]
                  },
                  "company": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Aparece como nome do titular da conta de testes. Sem quebra\nde linha nem caractere de controle.\n",
                    "examples": [
                      "Integradora Ltda"
                    ]
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Como esta chave aparece na sua lista. Sem quebra de linha\nnem caractere de controle.\n",
                    "examples": [
                      "Ambiente local do Bruno"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Chave e conta de testes criadas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apiKey": {
                      "type": "string",
                      "description": "O segredo. Aparece só aqui.",
                      "examples": [
                        "lume_sk_sandbox_k7m2p9qr4tvxaz3n6hjd8swy5cbe2fgk4mpq"
                      ]
                    },
                    "prefix": {
                      "type": "string",
                      "description": "Parte pública, para identificar a chave depois.",
                      "examples": [
                        "lume_sk_sandbox_k7m2p9qr"
                      ]
                    },
                    "environment": {
                      "type": "string",
                      "examples": [
                        "sandbox"
                      ]
                    },
                    "header": {
                      "type": "string",
                      "examples": [
                        "X-API-Key"
                      ]
                    },
                    "scopes": {
                      "type": "array",
                      "description": "O que esta chave alcança. No sandbox, todos.",
                      "items": {
                        "type": "string",
                        "enum": [
                          "accounts:read",
                          "pix:read",
                          "pix:write",
                          "pix:keys:write",
                          "beneficiaries:write"
                        ]
                      },
                      "examples": [
                        [
                          "accounts:read",
                          "pix:read",
                          "pix:write",
                          "pix:keys:write",
                          "beneficiaries:write"
                        ]
                      ]
                    },
                    "rateLimitPerMinute": {
                      "type": "integer",
                      "examples": [
                        60
                      ]
                    },
                    "expiresAfterDaysIdle": {
                      "type": "integer",
                      "examples": [
                        30
                      ]
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "examples": [
                            "acc_sbx_9f2c1a7b"
                          ]
                        },
                        "branch": {
                          "type": "string",
                          "examples": [
                            "0001"
                          ]
                        },
                        "number": {
                          "type": "string",
                          "examples": [
                            "900142-2"
                          ]
                        },
                        "name": {
                          "type": "string",
                          "examples": [
                            "Integradora Ltda (teste)"
                          ]
                        },
                        "balanceCents": {
                          "type": "integer",
                          "examples": [
                            500000
                          ]
                        }
                      }
                    },
                    "credentials": {
                      "type": "object",
                      "description": "Para abrir a mesma conta no internet banking e no\naplicativo. O segredo TOTP é o que permite gerar o código\nde step-up sem um telefone na mão.\n",
                      "properties": {
                        "document": {
                          "type": "string",
                          "examples": [
                            "52998224725"
                          ]
                        },
                        "password": {
                          "type": "string",
                          "examples": [
                            "sbx-3k9mqp2tvxza7nhd"
                          ]
                        },
                        "totpSecret": {
                          "type": "string",
                          "examples": [
                            "SEGREDO-BASE32-DA-CONTA-DE-SANDBOX"
                          ]
                        },
                        "totpUri": {
                          "type": "string",
                          "examples": [
                            "otpauth://totp/Lume%20Sandbox:dev@integradora.com.br?secret=..."
                          ]
                        }
                      }
                    },
                    "warning": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/auth/login": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Primeira etapa do acesso interativo",
        "description": "A senha sozinha nunca abre sessão. O token devolvido aqui só serve para\napresentar o segundo fator em `POST /v1/auth/mfa`, e vale 5 minutos.\n\nA resposta é idêntica para documento inexistente e senha errada,\ninclusive no tempo de resposta: responder diferente entregaria quem é\ncliente do banco.\n",
        "security": [],
        "operationId": "login",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document",
                  "password"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ, com ou sem pontuação. O CNPJ pode ter letras\n(CNPJ alfanumérico, que a Receita Federal atribui às novas\ninscrições a partir de julho de 2026): minúsculas são\naceitas, e a forma gravada é em maiúsculas, sem pontuação.\n",
                    "examples": [
                      "123.456.789-09"
                    ]
                  },
                  "password": {
                    "type": "string",
                    "examples": [
                      "lume-demo-2026"
                    ]
                  },
                  "deviceFingerprint": {
                    "type": "string",
                    "description": "Identifica o aparelho. Um aparelho que não está habilitado\nvem marcado em `newDevice`, e a tela explica por que o\ncódigo está sendo pedido. A recusa do aparelho só acontece\ndepois do segundo fator, em `POST /v1/auth/mfa` (ver\n`DEVICE_NOT_ENABLED` em `Error`).\n",
                    "examples": [
                      "b7f3c1e9a2d84f60"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Senha aceita; falta o segundo fator",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mfaToken": {
                      "type": "string",
                      "description": "Presente quando o segundo fator já está cadastrado."
                    },
                    "mfaRequired": {
                      "type": "boolean",
                      "examples": [
                        true
                      ]
                    },
                    "newDevice": {
                      "type": "boolean",
                      "description": "Presente junto de `mfaToken`.",
                      "examples": [
                        false
                      ]
                    },
                    "enrollment": {
                      "type": "boolean",
                      "description": "`true` quando a conta ainda não cadastrou o segundo\nfator. Nesse caso a resposta traz `enrollToken`,\n`totpSecret` e `otpauthUri` no lugar de `mfaToken`, e o\ncliente conclui em `POST /v1/auth/mfa/enroll`.\n",
                      "examples": [
                        true
                      ]
                    },
                    "enrollToken": {
                      "type": "string",
                      "description": "Vale 5 minutos e só serve em `POST /v1/auth/mfa/enroll`."
                    },
                    "totpSecret": {
                      "type": "string",
                      "description": "Chave do autenticador, em texto. Dado sensível, não\nregistrar em log. Muda a cada login até o cadastro ser\nconcluído: vale só a que veio junto do `enrollToken`\napresentado em `POST /v1/auth/mfa/enroll`.\n"
                    },
                    "otpauthUri": {
                      "type": "string",
                      "description": "URI `otpauth://` para desenhar o QR code ou abrir no autenticador. Dado sensível."
                    },
                    "onboarding": {
                      "type": "boolean",
                      "description": "`true` quando o titular ainda está abrindo conta (contas\nem abertura e cadastro não enviado; PROVISÓRIO,\n27/10/2026): no lugar do segundo fator, a resposta traz\n`onboardingToken`, `mode` e `stage`, como\n`POST /v1/onboarding/resume`, e o cliente segue para a\nabertura, sem sessão. Quem já cadastrou o autenticador\nrecebe esta mesma resposta em `POST /v1/auth/mfa`.\n",
                      "examples": [
                        true
                      ]
                    },
                    "onboardingToken": {
                      "type": "string",
                      "description": "Presente com `onboarding`. Só alcança as rotas de `/v1/onboarding`."
                    },
                    "mode": {
                      "type": "string",
                      "examples": [
                        "hosted",
                        "own"
                      ]
                    },
                    "stage": {
                      "type": "string",
                      "examples": [
                        "CUSTOMER_CREATED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/password/setup/request": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Pede o código do primeiro acesso",
        "description": "Para o cliente que o atendimento abriu no backoffice: o atendente não\nrecebe senha, e o cliente a cria no \"Primeiro acesso\". Este pedido\nenvia um código de seis dígitos ao contato cadastrado na abertura\n(e-mail ou celular, o canal escolhido pelo atendente; o destino nunca\nvem do pedido). O código vale 10 minutos, há 60 segundos entre dois\nenvios ao mesmo destino e tetos por documento, destino e IP. Enquanto\no código enviado vale, um pedido novo ao mesmo destino não envia outro\nnem anula o que foi enviado; os tetos de destino e de titular são\nmenores que os do contato verificado na sessão (2 por hora e 3 por dia\nao mesmo destino), e contados à parte dos dele.\n\nA resposta é sempre `202`, exista ou não o documento e tenha ou não\nprimeiro acesso pendente: responder diferente entregaria quem é cliente\ndo banco. Não depende da funcionalidade de abertura de conta, porque o\ncliente já aberto precisa dela.\n",
        "security": [],
        "operationId": "requestPasswordSetup",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ, com ou sem pontuação.",
                    "examples": [
                      "123.456.789-09"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido aceito. Se havia primeiro acesso pendente, o código foi enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/password/setup": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Cria a senha no primeiro acesso",
        "description": "Confere o código recebido em `POST /v1/auth/password/setup/request`,\naplica a política de senha e grava a senha do cliente. Depois, o login\nnormal (`POST /v1/auth/login`) leva ao cadastro do segundo fator.\n\nCódigo errado, vencido, já usado ou sem pedido, e documento sem primeiro\nacesso pendente, respondem o mesmo `422`: nada diz qual aconteceu. Cada\nerro conta como tentativa do documento, e no teto (5 em 15 minutos) a\nresposta é `429`, para documento existente ou não. Senha fora da\npolítica é `422` e não gasta o código. O código vale uma vez só.\n",
        "security": [],
        "operationId": "setupPassword",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document",
                  "code",
                  "password"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ, com ou sem pontuação.",
                    "examples": [
                      "123.456.789-09"
                    ]
                  },
                  "code": {
                    "type": "string",
                    "description": "Seis dígitos recebidos no contato cadastrado.",
                    "examples": [
                      "482913"
                    ]
                  },
                  "password": {
                    "type": "string",
                    "description": "A nova senha, na política de senha do sistema. Nunca registrar em log."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Senha criada."
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/password/reset/request": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Pede o código para recuperar a senha",
        "description": "\"Esqueci a senha\", para quem já tem senha. Envia um código de seis\ndígitos ao contato cadastrado: o e-mail, ou o celular quando a conta\nfoi aberta com envio por SMS. O destino nunca vem do pedido. O código\nvale 10 minutos, há 60 segundos entre dois envios ao mesmo destino e\nhá tetos por documento, destino e IP. Enquanto o código enviado vale,\num pedido novo ao mesmo destino não envia outro nem anula o que foi\nenviado; os tetos são os do primeiro acesso (2 por hora e 3 por dia ao\nmesmo destino).\n\nA resposta é sempre `202`, exista ou não o documento: responder\ndiferente entregaria quem é cliente do banco. Quem tem primeiro acesso\npendente não recebe código aqui; o caminho dele é\n`POST /v1/auth/password/setup/request`.\n",
        "security": [],
        "operationId": "requestPasswordReset",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ, com ou sem pontuação.",
                    "examples": [
                      "123.456.789-09"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido aceito. Se o documento tem cadastro com senha, o código foi enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/password/reset": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Define uma senha nova com o código recebido e o do autenticador",
        "description": "Confere dois códigos: o recebido em\n`POST /v1/auth/password/reset/request` e o do aplicativo autenticador\n(`totpCode`), quando a conta já cadastrou o segundo fator. Só o código\ndo contato não basta, porque quem invadiu o e-mail tomaria a conta.\nQuem perdeu o autenticador, ou ainda não o cadastrou, recupera o acesso\npelo atendimento: sem segundo fator cadastrado, a resposta é o mesmo `422`.\nExceção: quem ainda não é cliente de fato (sem autenticador, com todas\nas contas próprias em abertura, sem representar nenhum titular e com o\ncadastro da abertura ainda não enviado para análise) troca a senha só\ncom o código do contato, e `totpCode` pode ir vazio.\n\nA ordem das conferências:\n\n1. senha fora da política: `422`, antes de tudo, sem gastar código\n   nenhum;\n2. código do autenticador errado: `422`, sem gastar o código do\n   contato;\n3. código do contato errado, vencido, já usado ou sem pedido, e\n   documento sem cadastro: o mesmo `422`, com a mesma mensagem.\n\nCada erro conta como tentativa do documento a partir desta origem\n(endereço). No teto (5 em 15 minutos) a resposta é `429`, exista ou\nnão o documento; de outra origem, a recuperação continua.\n\nCom os dois códigos certos, a senha é gravada e **todas** as sessões do\ntitular são encerradas na mesma transação, em todos os aparelhos. A\ntrava de tentativas do login do documento é liberada, e a troca entra\nno histórico de segurança como `PASSWORD_CHANGE`. O código do contato\nvale uma vez só.\n",
        "security": [],
        "operationId": "resetPassword",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document",
                  "code",
                  "password"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "CPF ou CNPJ, com ou sem pontuação.",
                    "examples": [
                      "123.456.789-09"
                    ]
                  },
                  "code": {
                    "type": "string",
                    "description": "Seis dígitos recebidos no contato cadastrado.",
                    "examples": [
                      "482913"
                    ]
                  },
                  "totpCode": {
                    "type": "string",
                    "description": "Seis dígitos do aplicativo autenticador. Obrigatório para\nconta com segundo fator cadastrado.\n",
                    "examples": [
                      "731045"
                    ]
                  },
                  "password": {
                    "type": "string",
                    "description": "A nova senha, na política de senha do sistema. Nunca registrar em log."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Senha redefinida, e todas as sessões encerradas."
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/mfa/enroll": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Cadastra o segundo fator no primeiro acesso",
        "description": "Para a conta que ainda não cadastrou o segundo fator: `POST\n/v1/auth/login` devolve `enrollment: true` com a chave do autenticador.\nO cliente a cadastra no aplicativo e apresenta aqui o primeiro código de\nseis dígitos, dentro dos 5 minutos de validade do `enrollToken`. A\nchave só fica guardada quando o código a confirma: cada login antes\ndisso sorteia uma nova, a primeira confirmada é a que fica, e as\noutras deixam de servir. O\ncódigo confere, o cadastro é gravado e a sessão abre na mesma resposta,\nno mesmo formato de `POST /v1/auth/mfa` (no navegador, o refresh vai em\ncookie). Um código só vale uma vez. Após 5 erros em 15 minutos, as\ntentativas ficam bloqueadas.\n\nA conta em análise pode cadastrar o segundo fator e entrar; movimentar\ndinheiro continua exigindo conta ativa em cada operação.\n",
        "security": [],
        "operationId": "enrollMFA",
        "parameters": [
          {
            "$ref": "#/components/parameters/DeviceModel"
          },
          {
            "$ref": "#/components/parameters/DeviceManufacturer"
          },
          {
            "$ref": "#/components/parameters/DeviceOSVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enrollToken",
                  "code"
                ],
                "properties": {
                  "enrollToken": {
                    "type": "string",
                    "description": "O `enrollToken` devolvido por `POST /v1/auth/login`."
                  },
                  "code": {
                    "type": "string",
                    "description": "Seis dígitos do aplicativo autenticador.",
                    "examples": [
                      "482913"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Segundo fator cadastrado e sessão aberta",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "examples": [
                        900
                      ]
                    },
                    "name": {
                      "type": "string",
                      "examples": [
                        "Marina Advíncula Rocha"
                      ]
                    },
                    "accountId": {
                      "type": "string",
                      "examples": [
                        "acc_u1"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`enrollToken` inválido ou expirado (\"Cadastro expirado. Entre\nnovamente para recomeçar.\"), código incorreto (\"Código inválido ou\nexpirado.\") ou código já usado.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/DeviceNotEnabled"
          },
          "409": {
            "description": "O segundo fator já está cadastrado; o cliente deve entrar normalmente (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/mfa": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Segunda etapa do acesso interativo",
        "description": "Troca o token de MFA e o código de seis dígitos por uma sessão. Um\ncódigo válido só vale uma vez: quem o intercepta não tem meio minuto\npara reutilizá-lo.\n\nCom o código certo, o aparelho do login (`deviceFingerprint`) é\navaliado. Senha e código certos não habilitam um aparelho: no modo\nimpor da instalação, aparelho que não está habilitado não recebe\nsessão (`403 DEVICE_NOT_ENABLED`) e passa a constar como pendente em\n`GET /v1/security/devices`. No modo observar, o padrão, o aparelho é\nhabilitado na entrada, como antes. Aparelho desabilitado ou bloqueado\né recusado nos dois modos, e desabilitar ou bloquear um aparelho\nencerra na hora todas as sessões abertas nele.\n",
        "security": [],
        "operationId": "verifyMFA",
        "parameters": [
          {
            "$ref": "#/components/parameters/DeviceModel"
          },
          {
            "$ref": "#/components/parameters/DeviceManufacturer"
          },
          {
            "$ref": "#/components/parameters/DeviceOSVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mfaToken",
                  "code"
                ],
                "properties": {
                  "mfaToken": {
                    "type": "string",
                    "description": "O `mfaToken` devolvido por `POST /v1/auth/login`.",
                    "examples": [
                      "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                    ]
                  },
                  "code": {
                    "type": "string",
                    "description": "Seis dígitos do aplicativo autenticador.",
                    "examples": [
                      "482913"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessão aberta. Exceção (PROVISÓRIA, 27/10/2026): o titular que\nainda está abrindo conta (contas em abertura, cadastro não enviado)\nnão recebe sessão; recebe `onboarding: true` com `onboardingToken`,\n`mode` e `stage`, como `POST /v1/onboarding/resume`, e segue para a\nabertura.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "examples": [
                        900
                      ]
                    },
                    "name": {
                      "type": "string",
                      "examples": [
                        "Marina Advíncula Rocha"
                      ]
                    },
                    "accountId": {
                      "type": "string",
                      "examples": [
                        "acc_u1"
                      ]
                    },
                    "onboarding": {
                      "type": "boolean",
                      "description": "Presente e `true` só no desvio para a abertura; sem sessão."
                    },
                    "onboardingToken": {
                      "type": "string",
                      "description": "Presente com `onboarding`. Só alcança as rotas de `/v1/onboarding`."
                    },
                    "mode": {
                      "type": "string",
                      "examples": [
                        "hosted",
                        "own"
                      ]
                    },
                    "stage": {
                      "type": "string",
                      "examples": [
                        "CUSTOMER_CREATED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/DeviceNotEnabled"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/refresh": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Renova a sessão",
        "description": "Devolve um par novo e gasta o anterior. A rotação existe por uma razão\nsó: sem ela, um token de trinta dias vale trinta dias para quem o\ncopiar, e o dono legítimo nunca fica sabendo. Um token já gasto que\nreaparece derruba a linhagem inteira nascida daquele login.\n\nNo modo impor da instalação, renovar exige que o aparelho em que a\nsessão foi aberta continue habilitado. Se não estiver, a resposta é\n`401 DEVICE_NOT_ENABLED` e a linhagem inteira é encerrada.\n",
        "security": [],
        "operationId": "refreshSession",
        "parameters": [
          {
            "$ref": "#/components/parameters/DeviceModel"
          },
          {
            "$ref": "#/components/parameters/DeviceManufacturer"
          },
          {
            "$ref": "#/components/parameters/DeviceOSVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "examples": [
                      "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Par novo emitido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "examples": [
                        900
                      ]
                    },
                    "name": {
                      "type": "string",
                      "examples": [
                        "Marina Advíncula Rocha"
                      ]
                    },
                    "accountId": {
                      "type": "string",
                      "examples": [
                        "acc_u1"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/auth/logout": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Encerra a sessão",
        "description": "Público de propósito: sair precisa funcionar quando a sessão já está\nestranha, e o que ele invalida é o próprio token apresentado. Responde\n`204` mesmo para token ilegível, para não virar um oráculo sobre quais\ntokens ainda valem.\n",
        "security": [],
        "operationId": "logout",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refreshToken"
                ],
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "examples": [
                      "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                    ]
                  },
                  "allDevices": {
                    "type": "boolean",
                    "description": "Derruba a linhagem inteira nascida daquele login.",
                    "examples": [
                      false
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Encerrada"
          }
        }
      }
    },
    "/v1/auth/step-up": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Emite a prova de segundo fator para uma operação",
        "description": "O `purpose` amarra o token à operação, e o token vale 5 minutos. Envie o\nvalor devolvido no cabeçalho `X-Step-Up` da chamada que o exige.\n\nValores em uso:\n\n- `transfer`: transferência a partir de R$ 1.000,00, ou quando a soma\n  do dia chega a esse valor;\n- `ted`: toda TED, qualquer que seja o valor;\n- `pix_key`: cadastro de chave PIX;\n- `review`: liberar transferência retida;\n- `card`: ver o número do cartão ou desbloqueá-lo;\n- `biometrics`: cadastrar ou trocar a biometria facial;\n- `limits`: pedir aumento de limite;\n- `password`: trocar a senha (`POST /v1/auth/password`);\n- `dispute`: contestar um Pix enviado (MED), sempre;\n- `preferences`: desligar uma proteção ou um aviso em Segurança\n  (`PATCH /v1/security/preferences`);\n- `devices`: remover um aparelho (`DELETE /v1/security/devices/{id}`);\n- `subscription`: aderir a um plano, trocá-lo, trocar o dia de\n  cobrança ou cancelá-lo (`POST /v1/subscription`,\n  `POST /v1/subscription/plan-change`,\n  `POST /v1/subscription/plan-change/cancel`,\n  `POST /v1/subscription/billing-day`,\n  `POST /v1/subscription/billing-day/cancel` e\n  `POST /v1/subscription/cancel`), sempre;\n- `account_block`: bloquear a própria conta\n  (`POST /v1/security/account-block`).\n\nO token vale uma vez só, e a marca de uso é guardada no servidor, não\nsó no processo: reiniciar a API ou cair em outra instância não o\ndevolve. O mesmo vale para o código de seis dígitos aceito aqui e em\n`POST /v1/auth/mfa`.\n\n**Esta rota é de sessão (`Authorization: Bearer`) e não aceita chave de\nAPI**: com `X-API-Key` a resposta é `403 FORBIDDEN`. O token de step-up\nsai de uma sessão do titular e vale na chamada seguinte, qualquer que\nseja a credencial dela. Isso inclui o cadastro de chave PIX\n(`pix:keys:write`, que exige step-up sempre) e o Pix a partir de\nR$ 1.000,00, por operação ou pela soma do dia.\n\nNo sandbox, faça login e MFA com as `credentials` devolvidas em\n`POST /v1/sandbox/keys` (`POST /v1/auth/login`, depois\n`POST /v1/auth/mfa`) e calcule o código de seis dígitos com o\n`totpSecret` — qualquer biblioteca de TOTP o calcula. Em produção, o\ntoken vem de uma sessão do próprio titular, que tem o aplicativo\nautenticador.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "stepUp",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "purpose"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "examples": [
                      "482913"
                    ]
                  },
                  "selfie": {
                    "type": "string",
                    "description": "Imagem da selfie em base64, com ou sem o prefixo\n`data:...;base64,`. Opcional: envie quando a operação\nrespondeu `403 BIOMETRIC_REQUIRED`, ou com `purpose:\nbiometrics` para trocar a biometria na hora\n(`POST /v1/security/biometrics`). Precisa haver biometria\ncadastrada (`409` se não) e a instalação precisa oferecê-la\n(`422` se não); a selfie que não confere conta como\ntentativa errada.\n"
                  },
                  "purpose": {
                    "type": "string",
                    "enum": [
                      "transfer",
                      "ted",
                      "pix_key",
                      "review",
                      "card",
                      "biometrics",
                      "limits",
                      "password",
                      "dispute",
                      "preferences",
                      "devices",
                      "subscription",
                      "account_block"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prova emitida",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stepUpToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "examples": [
                        300
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/auth/password": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Troca a senha do titular logado",
        "description": "Pede a senha atual, a senha nova e o segundo fator: `X-Step-Up` com\n`purpose: password`. A sessão sozinha não basta, nem a senha atual\nsozinha.\n\nA conferência segue esta ordem, e o `X-Step-Up` é o último passo:\n\n1. senha atual errada: `422`;\n2. senha nova igual à atual ou fora da política de senha (mínimo de 10\n   caracteres, com letras e ao menos um número, símbolo ou espaço):\n   `422`;\n3. sem `X-Step-Up` válido para `password`: `403 STEP_UP_REQUIRED`.\n\nNenhuma dessas recusas gasta a prova, que pode ser reenviada com os\ncampos corrigidos. Por isso, a tela pode mandar o pedido sem\n`X-Step-Up` para conferir os campos e só pedir o código quando a\nresposta for `403`. Depois do passo 3 a prova já foi gasta: um `409`\nou uma falha do servidor pedem um código novo.\n\nSenha atual errada responde `422`, e não `401`, porque `401` quer\ndizer sessão encerrada. Cada erro da senha atual conta para a trava do\ntitular nesta rota e também para a do login do documento a partir\ndesta origem (5 em 15 minutos em cada uma, depois `429`), e entra no\nhistórico de segurança\ncomo `FAILED_LOGIN`.\n\nQuando a senha é trocada, as outras sessões do titular são encerradas\nna mesma transação, em todos os aparelhos. A sessão que fez a troca\ncontinua aberta. A troca entra no histórico como `PASSWORD_CHANGE`.\nSe outra sessão trocou a senha entre a conferência e a gravação, a\nresposta é `409`.\n\n**Rota de sessão**: com `X-API-Key`, a resposta é `403 FORBIDDEN`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "changePassword",
        "parameters": [
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currentPassword",
                  "newPassword"
                ],
                "properties": {
                  "currentPassword": {
                    "type": "string",
                    "description": "A senha em uso. Nunca registrar em log."
                  },
                  "newPassword": {
                    "type": "string",
                    "description": "A senha nova, na política de senha do sistema. Nunca registrar em log."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Senha trocada, e as outras sessões encerradas."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "409": {
            "description": "A senha mudou em outra sessão durante a troca.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyAttempts"
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Dados do titular e da conta",
        "operationId": "getMe",
        "responses": {
          "200": {
            "description": "Titular e conta",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "branch": {
                          "type": "string",
                          "description": "Agência da conta. Com provedor financeiro que mantém a conta, é a do provedor, e vem vazia enquanto ele não a informa (ver `numberPending`).",
                          "examples": [
                            "0002"
                          ]
                        },
                        "number": {
                          "type": "string",
                          "description": "Número da conta, o que um terceiro usa para mandar Pix ou TED por agência e conta. Com provedor financeiro que mantém a conta, é o do provedor, e vem vazio enquanto ele não o informa.",
                          "examples": [
                            "41700001"
                          ]
                        },
                        "numberPending": {
                          "type": "boolean",
                          "description": "A conta aguarda a agência e o número do provedor (cadastro em análise): `branch` e `number` vêm vazios."
                        },
                        "numberUnavailable": {
                          "type": "boolean",
                          "description": "A agência e o número não puderam ser lidos agora: vêm vazios, sem `accountType` nem `bank`. Não quer dizer que a conta está em análise; tente de novo."
                        },
                        "accountType": {
                          "type": "string",
                          "enum": [
                            "CHECKING",
                            "SAVINGS",
                            "PAYMENT",
                            "SALARY"
                          ],
                          "description": "Tipo da conta, como a TED o pede (PAYMENT = conta de pagamento). Só com provedor financeiro que mantém a conta."
                        },
                        "bank": {
                          "type": "object",
                          "description": "Instituição da conta, pela lista oficial de participantes do Banco Central. Só com provedor financeiro que mantém a conta.",
                          "properties": {
                            "ispb": {
                              "type": "string",
                              "examples": [
                                "18684408"
                              ]
                            },
                            "code": {
                              "type": "string",
                              "description": "Código de compensação; vazio se a lista não tiver o ISPB ou não puder ser lida.",
                              "examples": [
                                "562"
                              ]
                            },
                            "name": {
                              "type": "string",
                              "description": "Nome reduzido; vazio nos mesmos casos do `code`.",
                              "examples": [
                                "AZIMUT BRASIL DTVM LTDA"
                              ]
                            }
                          }
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "PENDING",
                            "ACTIVE",
                            "BLOCKED",
                            "CLOSED"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/accounts/balance": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Saldo disponível",
        "description": "`balanceCents` já está descontado do que foi reservado. `heldCents` vai\njunto para a tela poder explicar a diferença, em vez de o cliente\ndescobrir sozinho que \"sumiu\" dinheiro.\n\n`blockedCents` e `suspenseCents` vêm do provedor financeiro, lidos na\nhora, e só existem quando ele lê o saldo por conta (hoje, a Azify) e a\nleitura deu certo. **Ausente não é zero**: significa \"não sei\" (outro\nprovedor, conta ainda sem vínculo, falha ou demora do provedor), e a\nresposta continua `200` com o saldo do razão. Eles **não** entram em\n`balanceCents`, que continua sendo o que o servidor deixa gastar.\n\nA leitura do provedor vale por até 20 segundos por conta, e `asOf` diz\nde quando é o dado mais antigo da resposta: o instante dessa leitura,\nou o de agora quando só há o razão.\n",
        "operationId": "getBalance",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Saldo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "balanceCents": {
                      "type": "integer",
                      "examples": [
                        487235
                      ]
                    },
                    "heldCents": {
                      "type": "integer",
                      "examples": [
                        0
                      ]
                    },
                    "blockedCents": {
                      "type": "integer",
                      "minimum": 0,
                      "examples": [
                        15000
                      ],
                      "description": "Valor bloqueado por ordem judicial ou medida cautelar (`balances.blocked` do provedor). Ausente quando o provedor não informa; não é o mesmo que zero."
                    },
                    "suspenseCents": {
                      "type": "integer",
                      "minimum": 0,
                      "examples": [
                        0
                      ],
                      "description": "Valor em suspenso informado pelo provedor (`balances.suspense`). O significado ainda não foi esclarecido pelo provedor: os aplicativos do Lume não o mostram ao cliente. Ausente quando o provedor não informa."
                    },
                    "currency": {
                      "type": "string",
                      "examples": [
                        "BRL"
                      ]
                    },
                    "asOf": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/accounts/statement": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Extrato",
        "description": "Ordenado do mais recente para o mais antigo. O sinal de `amountCents`\nindica a direção: negativo saiu, positivo entrou.\n",
        "operationId": "getStatement",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Padrão 50, teto 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "RFC 3339.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "examples": {
              "inicio": {
                "value": "2026-09-01T00:00:00Z"
              }
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "examples": {
              "fim": {
                "value": "2026-09-30T23:59:59Z"
              }
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TransactionKind"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TransactionStatus"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Busca na descrição e no nome da contraparte.",
            "schema": {
              "type": "string"
            },
            "examples": {
              "termo": {
                "value": "nota 8841"
              }
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Lançamentos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Transaction"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/transactions/{id}": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Uma transação",
        "description": "Transação de outra conta responde `404`, e não `403`: é o que impede\ndescobrir identificadores por tentativa.\n",
        "operationId": "getTransaction",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "examples": {
              "transacao": {
                "value": "a35c25155c2d13436508c4dd4ad82a48"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A transação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/transactions/{id}/review": {
      "post": {
        "tags": [
          "Conta"
        ],
        "summary": "Decide sobre uma transferência retida",
        "description": "Uma transferência com sinal de risco para em `HELD_FOR_REVIEW` e espera\na decisão do titular. `release` conclui, `block` devolve o dinheiro à\nconta na hora.\n\nLiberar move dinheiro e é irreversível: exige `X-Step-Up` com\n`purpose: review`. Bloquear não exige — travar a saída nunca pode ser\nmais difícil do que autorizá-la.\n\nLiberar pelo aplicativo tem ainda duas condições, conferidas\nantes de a prova ser consumida: a transação precisa ter sido retida há\npelo menos **15 minutos**, e o `riskScore` dela precisa ser menor que\n**85**. Com score 85 ou mais a transação está em análise pela\ninstituição e só o backoffice a libera. Nos dois casos a resposta é\n`409 CONFLICT`, a transação continua `HELD_FOR_REVIEW` (o valor segue\nreservado) e `block` segue valendo na hora.\n\nUma recarga de celular retida não pode ser liberada com a\nfuncionalidade `topup` desligada: a resposta é `403` com\n`code: FEATURE_DISABLED` e `details.feature: topup`, a transação\ncontinua `HELD_FOR_REVIEW` (o valor segue reservado desde a retenção)\ne nada é enviado à operadora.\n`block` segue valendo e devolve o dinheiro.\n\nDo mesmo jeito, um Pix por agência e conta retido não pode ser liberado\ncom a funcionalidade `pix_manual` desligada: `403 FEATURE_DISABLED`,\ncom `details.feature: pix_manual` (ou `pix_transfer`, se o Pix estiver\ndesligado), a transação continua `HELD_FOR_REVIEW` e nada é enviado ao\nprovedor. `block` segue valendo.\n\nAs recusas da liberação que não dependem da prova saem antes de o\n`X-Step-Up` ser conferido, e a prova enviada junto não é consumida:\nrecarga retida com `topup` desligada (`403 FEATURE_DISABLED`) ou sem\nprovedor de recarga na instalação (`501`), Pix por agência e conta\nretido com `pix_manual` ou `pix_transfer` desligada (`403 FEATURE_DISABLED`), e pagamento\nde QR Code retido que não pode ser liberado (`409`). Para o resto (transação\ninexistente, de outra conta, já decidida) a ordem é a de sempre: sem a\nprova, `403 STEP_UP_REQUIRED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "reviewTransaction",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "examples": {
              "transacao": {
                "value": "a35c25155c2d13436508c4dd4ad82a48"
              }
            }
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision"
                ],
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "release",
                      "block"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decisão aplicada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) para liberar, ou a\nrecarga retida não pode ser liberada com a funcionalidade `topup`\ndesligada, ou o Pix por agência e conta retido com `pix_manual` ou `pix_transfer`\ndesligada (`FEATURE_DISABLED`, com `details.feature`). Também\n`403 FORBIDDEN` (\"Conta bloqueada.\") quando a conta não está ativa:\n`release` é recusado por qualquer canal e a transação segue\n`HELD_FOR_REVIEW`; `block` continua permitido e devolve o valor.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A transação já foi decidida, ou é um pagamento de QR Code retido\nque não pode ser liberado (`CONFLICT`): o código não foi guardado.\nEla continua `HELD_FOR_REVIEW`; só se pode bloqueá-la, e o cliente\npaga de novo. Também `409 CONFLICT` quando a liberação pelo\naplicativo vem antes de 15 minutos da retenção (\"aguarde para\nliberar\") ou a transação tem `riskScore` 85 ou mais (\"em análise\npela instituição\"): continua retida, e só se pode bloqueá-la.\n\n`details.reason` diz qual foi: `WAIT` (espera; vem também\n`details.releaseAvailableAt`, data-hora RFC 3339 em UTC a partir da\nqual a liberação abre), `INSTITUTION` (só o backoffice libera) ou\n`ALREADY_DECIDED` (já decidida). O QR retido que não se libera vem\nsem `details`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Dados inválidos (`INVALID_REQUEST`), ou `release` recusado por limite\n(`LIMIT_EXCEEDED`): o teto diário ou o noturno, o teto noturno para\num contato novo (`pixNightNewPeerCents`, por operação e pela soma\nda janela para a mesma chave ou conta nunca paga antes das 20h), ou\no bloqueio noturno das preferências, valem no momento da\nliberação. A recarga retida confere os tetos dela (por recarga e\ndiário de recarga) do mesmo jeito. Nesse caso a transação segue\n`HELD_FOR_REVIEW`, com o valor reservado.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "Recarga retida sem provedor de recarga nesta instalação\n(`PROVIDER_ERROR`). A transação continua `HELD_FOR_REVIEW`, com o\nvalor reservado; `block` devolve o dinheiro.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{id}/refund": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Devolve um Pix recebido",
        "description": "Devolve, total ou parcialmente, um Pix que a conta recebeu\n(`kind: PIX_RECEIVED`, `COMPLETED`). Só com sessão de cliente: **não**\naceita chave de API.\n\n**`Idempotency-Key` é obrigatório**, como no Pix. Repetir a mesma chave\ncom o mesmo corpo devolve a resposta original (`Idempotent-Replay: true`)\ne o dinheiro sai uma vez só.\n\nResponde `202` com a **devolução**, uma transação nova\n(`kind: PIX_REFUND_SENT`, `amountCents` negativo, `refundOf` com o id do\nPix recebido) em `PROCESSING`. O desfecho chega depois, por aviso do\nbanco parceiro: `COMPLETED` quando a devolução sai, ou `FAILED` quando\né recusada, e nesse caso o valor volta à conta e volta a ser\ndevolvível. Um prazo esgotado no banco parceiro deixa a devolução em\n`PROCESSING`: o Lume nunca a repete.\n\nO valor devolvível é o do Pix recebido menos a soma das devoluções que\nnão falharam (`PROCESSING` e `COMPLETED`); `refundableCents` no Pix\nrecebido diz quanto resta. Pedir mais que isso responde `422\nINVALID_REQUEST` com o valor que ainda cabe. Duas devoluções\nsimultâneas do mesmo Pix nunca passam do valor original.\n\nAs regras são as do Pix que sai da conta: a partir de R$ 1.000,00, ou\nquando a soma dos Pix enviados no dia chega a esse valor, exige\n`X-Step-Up` com `purpose: transfer` (e selfie, acima do limiar de\nbiometria), a conta precisa estar ativa, a devolução **conta** nos\nlimites do Pix (por operação, diário e noturno) e exige saldo. Não passa\npelo motor de risco: o destino é fixo, quem pagou. Na alçada da conta\nempresarial — o valor somado às saídas do dia sem segunda assinatura —\na devolução é recusada com `422 APPROVAL_REQUIRED`: não há aprovação\nconjunta de devolução.\n\nTransação inexistente ou de outra conta responde `404`, sem distinguir.\nA transação que não é um Pix recebido, ou que ainda não está\n`COMPLETED`, responde `422`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "refundPix",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id do Pix recebido (o `kind` é `PIX_RECEIVED`).",
            "schema": {
              "type": "string"
            },
            "examples": {
              "transacao": {
                "value": "a35c25155c2d13436508c4dd4ad82a48"
              }
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amountCents"
                ],
                "properties": {
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Inteiro em centavos; no máximo `refundableCents`. 2500 são R$ 25,00.",
                    "examples": [
                      2500
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140,
                    "examples": [
                      "Valor enviado por engano"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Aceita; a devolução em `PROCESSING`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`), ou a conta está bloqueada (`FORBIDDEN`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A mesma chave de idempotência já foi usada com outro conteúdo\n(`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior ainda está em\ncurso (`IDEMPOTENCY_IN_FLIGHT` — repita em alguns segundos).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Valor inválido, ou acima do que ainda pode ser devolvido\n(`INVALID_REQUEST`; a mensagem traz o valor que cabe); a transação\nnão é um Pix recebido, ainda não foi concluída, ou não pode ser\ndevolvida por aqui (`INVALID_REQUEST`); o banco desta instalação não\ndevolve Pix (`INVALID_REQUEST`); saldo insuficiente\n(`INSUFFICIENT_FUNDS`); limite excedido (`LIMIT_EXCEEDED`); valor\nque, somado às saídas do dia, alcança a alçada da conta empresarial\n(`APPROVAL_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/disputes": {
      "get": {
        "tags": [
          "PIX"
        ],
        "summary": "Contestações de Pix da conta",
        "description": "As contestações (MED, Mecanismo Especial de Devolução) que a conta abriu,\nda mais recente para a mais antiga. Só com sessão de cliente: **não**\naceita chave de API (a contestação é ato do titular).\n\nCom `refresh=true` a lista confere o andamento no banco parceiro das\ncontestações que ainda andam (no máximo uma vez a cada 30 s por\ncontestação, e só para algumas por chamada); se o banco não responder,\ndevolve o último estado conhecido. Nunca traz contestação de outra conta.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listPixDisputes",
        "parameters": [
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "description": "Com `true`, confere o andamento no banco parceiro antes de responder\n(a tela da lista). Sem, devolve só o que está gravado: é o que a carga\ndas notificações usa, e não espera pelo banco.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Contestações da conta",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Dispute"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Esta operação não pode ser feita com chave de API (`FORBIDDEN`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Contesta um Pix enviado",
        "description": "Abre uma contestação por suspeita de fraude (MED) sobre um Pix **enviado**,\nconcluído (`COMPLETED`) e feito por este banco, até **80 dias** depois do\nenvio (`disputableUntil` no detalhe da transação diz até quando; o prazo é\ndo servidor). O valor contestado é o da transação. Só com sessão de\ncliente: **não** aceita chave de API.\n\n**`Idempotency-Key` é obrigatório.** Repetir a mesma chave com o mesmo\ncorpo devolve a resposta original (`Idempotent-Replay: true`) e abre uma\ncontestação só. **Exige `X-Step-Up`** com `purpose: dispute`\n(`403 STEP_UP_REQUIRED` sem ele; a recusa não é desfecho da chave de\nidempotência). A abertura fica registrada no histórico de segurança\n(`PIX_DISPUTE`).\n\nUma contestação vale por transação. Enquanto houver uma em andamento, e\n**depois de `CANCELLED` ou `CONCLUDED`**, a mesma transação não pode ser\ncontestada de novo (`422`). Depois de `NOT_REGISTERED` (o banco não\nregistrou) pode. A conta **bloqueada contesta** (o segundo fator e o\nteto seguram uma sessão roubada, e a vítima de tomada de conta é\njustamente a bloqueada); a **encerrada não** (`403 FORBIDDEN`). Há um\nteto por conta: 5 contestações a cada 24 horas e\n20 a cada 30 dias (as não registradas não contam); acima,\n`429 RATE_LIMITED` com `Retry-After`, registrado no histórico de\nsegurança, e o atendimento decide o resto.\n\nO `fraudType` é um de `SCAM` (golpe), `ACCOUNT_TAKEOVER` (conta invadida),\n`COERCION` (coerção), `FRAUDULENT_ACCESS` (acesso fraudulento),\n`OTHER` (outra razão, exige `details`) e `UNKNOWN`. O relato (`details`)\né texto livre de até 500 caracteres: dado pessoal, vai ao banco do\nrecebedor para a análise.\n\nResponde `201` com a contestação em `OPENING`, **sem o relato** (`details`):\na resposta fica guardada para repetir o pedido, e o relato é dado pessoal\nque não precisa existir em mais um lugar; leia-o em\n`GET /v1/pix/disputes/{id}`. O protocolo (`protocol`) e\no prazo de análise (`analysisDeadline`) aparecem quando o banco confirma o\nregistro. Se a resposta do banco se perder, a contestação fica em\n`OPENING`, o Lume **não** a abre de novo e a conferência a resolve.\n\nTransação inexistente ou de outra conta responde `404`, sem distinguir.\nEsta versão não permite editar a contestação.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "openPixDispute",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "transactionId",
                  "fraudType"
                ],
                "properties": {
                  "transactionId": {
                    "type": "string",
                    "description": "Id do Pix enviado (`TRANSFER_PIX`, `PAYMENT_PIX` ou `PAYMENT_QRCODE`, `COMPLETED`).",
                    "examples": [
                      "a35c25155c2d13436508c4dd4ad82a48"
                    ]
                  },
                  "fraudType": {
                    "type": "string",
                    "enum": [
                      "SCAM",
                      "ACCOUNT_TAKEOVER",
                      "COERCION",
                      "FRAUDULENT_ACCESS",
                      "OTHER",
                      "UNKNOWN"
                    ]
                  },
                  "details": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Relato do cliente. Obrigatório com `OTHER`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Aberta; em `OPENING` até o banco confirmar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` com `purpose: dispute` (`STEP_UP_REQUIRED`); a\nconta está encerrada (`FORBIDDEN`); esta operação não pode ser feita\ncom chave de API (`FORBIDDEN`); ou a funcionalidade está desligada\nnesta instalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A mesma chave de idempotência já foi usada com outro conteúdo\n(`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior ainda está em\ncurso (`IDEMPOTENCY_IN_FLIGHT` — repita em alguns segundos).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A transação não é um Pix enviado e concluído deste banco, passou de\n80 dias, já tem contestação em andamento ou encerrada, o tipo de\nfraude é inválido, falta o relato com `OTHER` ou o relato passa de\n500 caracteres (`INVALID_REQUEST`); o banco recusou o registro\n(`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/disputes/{id}": {
      "get": {
        "tags": [
          "PIX"
        ],
        "summary": "Uma contestação de Pix",
        "description": "Confere o andamento no banco parceiro (no máximo uma vez a cada 30 s) e\ndevolve a contestação com o protocolo, os prazos, o resultado e o valor\ndevolvido. Só com sessão de cliente: **não** aceita chave de API.\n\n`CONCLUDED` no banco parceiro **não** quer dizer aceita: leia\n`infractionResult` e `refundResult`. `refundedCents` é o que de fato\nentrou na conta (devoluções recebidas ligadas ao Pix, `PIX_REFUND_RECEIVED`);\n`refundAnnouncedCents` é o que o banco parceiro anunciou e pode estar à\nfrente. O MED nunca credita por si.\n\nContestação inexistente ou de outra conta responde `404`, sem distinguir.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getPixDispute",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "examples": {
              "contestacao": {
                "value": "6b8e2e90475db750adf02ef9c9da8c2e"
              }
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "A contestação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Esta operação não pode ser feita com chave de API (`FORBIDDEN`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/pix/disputes/{id}/cancel": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Cancela uma contestação de Pix",
        "description": "Cancela enquanto o banco parceiro aceita (`canCancel`: com o protocolo já\nconhecido e antes da conclusão). Responde `200` com a contestação em\n`CANCELLING`; o `CANCELLED` chega depois. Só com sessão de cliente:\n**não** aceita chave de API. Não pede segundo fator: o cancelamento fica\nregistrado no histórico de segurança (`PIX_DISPUTE`).\n\nDepois de cancelada a mesma transação **não** pode ser contestada de novo.\nFora do estado permitido responde `422 INVALID_REQUEST`. Se o banco não\nconfirmar, responde `503` e nada muda por aqui: a consulta seguinte diz o\nque houve.\n\nContestação inexistente ou de outra conta responde `404`, sem distinguir.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelPixDispute",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelamento pedido; a contestação em `CANCELLING`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Esta operação não pode ser feita com chave de API (`FORBIDDEN`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "A contestação não pode ser cancelada agora (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/keys": {
      "get": {
        "tags": [
          "PIX"
        ],
        "summary": "Chaves PIX da conta",
        "description": "O estado de cada chave vem do DICT, pelo provedor: na sessão do cliente\na lista consulta o provedor (no máximo uma vez a cada 30 s por conta) e\natualiza o que está gravado antes de responder. Se o provedor não\nresponder, devolve o último estado conhecido — nunca um `ACTIVE`\npresumido. Pela chave de API a lista é só o gravado: não consulta o\nprovedor nem grava.\n\nUma chave que o provedor tem e o Lume não (criada fora) aparece aqui.\nUma chave que o provedor deixou de listar passa a `REMOVED`, desde que a\nlista dele traga ao menos tantas chaves quanto as vivas daqui; lista\nvazia ou menor não remove nada.\n\n`max` é o teto de chaves do titular no DICT: 5 para pessoa física e 20\npara pessoa jurídica. Chave `REMOVED` não conta.\n",
        "operationId": "listPixKeys",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Chaves cadastradas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PixKey"
                      }
                    },
                    "max": {
                      "type": "integer",
                      "examples": [
                        5
                      ],
                      "description": "5 (pessoa física) ou 20 (pessoa jurídica)."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Cadastra uma chave PIX",
        "description": "Exige `X-Step-Up` com `purpose: pix_key`. Uma chave sequestrada\nredireciona todo dinheiro que alguém tentar enviar ao titular — é a\noperação mais sensível da conta depois da própria transferência.\n\nO tipo é deduzido do valor: CPF, CNPJ, e-mail, telefone ou chave\naleatória.\n\n- **CPF e CNPJ**: só o documento do próprio titular da conta (CPF para\n  pessoa física, CNPJ para pessoa jurídica). Outro documento é\n  `422 INVALID_REQUEST`. CNPJ alfanumérico ainda não se cadastra\n  (`422 INVALID_REQUEST`), recusado antes de o `X-Step-Up` ser\n  consumido.\n- **Chave aleatória (`EVP`)**: pede-se, não se informa. Envie\n  `{\"type\": \"EVP\"}` sem `key`; quem sorteia o valor é o DICT. Um\n  UUID escolhido pelo cliente é `422 INVALID_REQUEST`.\n- **E-mail e telefone**: exigem `verificationId`, a confirmação por\n  código enviado ao próprio e-mail ou telefone\n  (`POST /v1/pix/keys/verifications` e `.../{id}/confirm`), em toda\n  instalação. Sem o fornecedor de e-mail ou de SMS, o pedido do código\n  daquele canal responde `422 INVALID_REQUEST` (\"verificação por SMS\n  indisponível nesta instalação\"), e sem `verificationId` não há\n  cadastro. A confirmação vale para\n  aquele valor, aquela conta e aquele titular, por 15 minutos depois de\n  confirmada, e é consumida no cadastro. Ausente, expirada, já usada, de outro valor,\n  de outra conta ou de outro titular: `422 INVALID_REQUEST`. O código\n  de verificação não substitui o `X-Step-Up`: um prova que o contato é\n  seu, o outro que é o titular quem opera. Pela chave de API não se\n  cadastram e-mail nem telefone (`403 FORBIDDEN`).\n\nA conta precisa estar ativa. O titular tem no máximo 5 chaves (pessoa\nfísica) ou 20 (pessoa jurídica) no DICT; passar disso é\n`409 CONFLICT`. O cadastro entra no histórico de segurança do titular\n(`PIX_KEY`), com a chave mascarada.\n\nA chave é registrada no DICT antes de existir aqui. Se o arranjo\nrecusar — porque a chave já pertence a outro titular, por exemplo —\nnada é gravado e a resposta é `422 PROVIDER_ERROR`. O estado\nintermediário não existe de propósito: uma chave que aparece na sua\nlistagem e que o arranjo não conhece é uma chave pela qual ninguém\nconsegue pagar o titular.\n",
        "operationId": "addPixKey",
        "parameters": [
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "`key` para CPF, CNPJ, e-mail e telefone; `type: EVP` sem `key`\npara a chave aleatória. `verificationId` é obrigatório em\ne-mail e telefone.\n",
                "properties": {
                  "key": {
                    "type": "string",
                    "examples": [
                      "financeiro@integradora.com.br"
                    ]
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "EVP"
                    ],
                    "description": "Só `EVP`, sem `key`, para pedir uma chave aleatória."
                  },
                  "verificationId": {
                    "type": "string",
                    "description": "A confirmação de posse do e-mail ou do telefone. Obrigatório\nnesses dois tipos; ignorado nos demais.\n"
                  }
                },
                "examples": {
                  "documento": {
                    "summary": "CPF ou CNPJ do titular",
                    "value": {
                      "key": "12345678909"
                    }
                  },
                  "email": {
                    "summary": "E-mail confirmado por código",
                    "value": {
                      "key": "financeiro@integradora.com.br",
                      "verificationId": "ver_8c1d2e"
                    }
                  },
                  "aleatoria": {
                    "summary": "Chave aleatória, pedida",
                    "value": {
                      "type": "EVP"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Chave cadastrada, com o estado que o DICT informou. Logo depois da\ncriação o estado pode ainda ser `PENDING`; a lista traz o\ndefinitivo.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixKey"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "409": {
            "description": "A chave já está na sua conta, ou o titular está no teto de chaves\ndo DICT (5 para pessoa física, 20 para pessoa jurídica).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Chave inválida (`INVALID_REQUEST`); CPF ou CNPJ que não é o\ndocumento do titular; e-mail ou telefone sem confirmação válida; ou\nrecusada pelo arranjo (`PROVIDER_ERROR`) — inclusive quando ela já\npertence a outro titular.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/keys/verifications": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Envia o código de verificação de uma chave de e-mail ou telefone",
        "description": "Primeiro passo do cadastro de chave de e-mail ou de telefone: o Lume\nenvia um código de seis dígitos ao próprio endereço (por e-mail) ou\nnúmero (por SMS, só celular do Brasil). O titular o confirma em\n`POST /v1/pix/keys/verifications/{id}/confirm` e cadastra a chave com o\n`verificationId` em `POST /v1/pix/keys`, que continua exigindo\n`X-Step-Up`. Só pela sessão do titular: a chave de API não pede código.\n\nO tipo é deduzido do valor como no cadastro; mande o telefone com\n`+55` na frente (onze dígitos sem o código do país podem ser lidos como\nCPF). CPF, CNPJ e aleatória não se confirmam por código\n(`422 INVALID_REQUEST`). Chave que a conta já tem é `409 CONFLICT`, sem\nenviar nada. A conta precisa estar ativa (`403 FORBIDDEN`).\n\nO código vale 10 minutos. Um pedido novo ao mesmo destino cancela o\nanterior. Limites (`429 RATE_LIMITED`, com `Retry-After` em segundos):\n60 segundos entre dois envios ao mesmo destino; 3 por hora e 5 por dia\nao mesmo destino; 5 por hora e 10 por dia por titular; 10 por hora por\nendereço IP; e o teto diário de SMS da instalação. Titular com cinco\ncódigos errados nos últimos 15 minutos não pede outro até a janela\npassar (`429 TOO_MANY_ATTEMPTS`).\n\nO envio entra no histórico de segurança (`CONTACT_CODE_SENT`) com o\ndestino mascarado. O código nunca aparece em resposta nem em log.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "requestPixKeyVerification",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "E-mail ou telefone (com `+55`) que vai virar chave."
                  }
                },
                "examples": {
                  "email": {
                    "summary": "E-mail",
                    "value": {
                      "key": "financeiro@integradora.com.br"
                    }
                  },
                  "telefone": {
                    "summary": "Celular",
                    "value": {
                      "key": "+5521987654321"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Código enviado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verificationId": {
                      "type": "string",
                      "examples": [
                        "ver_8c1d2e4b9a7f40c1b2d3e4f5a6b7c8d9"
                      ]
                    },
                    "channel": {
                      "type": "string",
                      "enum": [
                        "EMAIL",
                        "SMS"
                      ]
                    },
                    "sentTo": {
                      "type": "string",
                      "description": "O destino mascarado, para a tela.",
                      "examples": [
                        "fi***@integradora.com.br",
                        "(21) 9****-4321"
                      ]
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Até quando o código vale."
                    },
                    "resendAfter": {
                      "type": "string",
                      "format": "date-time",
                      "description": "A partir de quando dá para pedir outro ao mesmo destino."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "A chave já está na sua conta (`CONFLICT`). Nada foi enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Chave inválida, ou que não é e-mail nem celular (`INVALID_REQUEST`);\ninstalação sem o fornecedor do canal (`INVALID_REQUEST`, \"verificação\npor SMS indisponível nesta instalação\"); ou o fornecedor recusou o\ndestino (`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de envio (`RATE_LIMITED`) ou titular travado por códigos\nerrados (`TOO_MANY_ATTEMPTS`). `Retry-After` diz quantos segundos\nesperar.\n",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "O fornecedor de e-mail ou de SMS não respondeu (`PROVIDER_ERROR`).\nO código pode ter saído; o pedido não vale e conta nos limites.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pix/keys/verifications/{id}/confirm": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Confirma o código de verificação",
        "description": "O titular digita o código recebido. Certo, o pedido fica verificado e\nvale 15 minutos, uma vez, para cadastrar aquela chave naquela conta\n(`verificationId` em `POST /v1/pix/keys`).\n\nCódigo errado é `422 INVALID_REQUEST` com `details.remainingAttempts`.\nNo quinto erro do pedido, ou no quinto do titular em 15 minutos somando\ntodos os pedidos, a resposta é `429 TOO_MANY_ATTEMPTS` e o pedido não\nserve mais; o titular espera o `Retry-After` e pede outro código. A\ntrava entra no histórico de segurança\n(`CONTACT_VERIFICATION_LOCKED`), e a confirmação também\n(`CONTACT_VERIFIED`).\n\nPedido de outro titular ou de outra conta é `404`, igual ao que não\nexiste. Pedido já confirmado, usado, substituído ou vencido é\n`409 CONFLICT`: peça outro.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "confirmPixKeyVerification",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "O `verificationId` devolvido no envio.",
            "schema": {
              "type": "string"
            },
            "examples": {
              "pedido": {
                "value": "ver_8c1d2e4b9a7f40c1b2d3e4f5a6b7c8d9"
              }
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Os seis dígitos recebidos.",
                    "examples": [
                      "123456"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contato verificado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verificationId": {
                      "type": "string"
                    },
                    "verifiedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "validUntil": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Até quando o pedido serve ao cadastro."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O pedido já foi confirmado, usado, substituído ou venceu (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Código incorreto (`INVALID_REQUEST`, com `details.remainingAttempts`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Pedido ou titular travado por códigos errados\n(`TOO_MANY_ATTEMPTS`). `Retry-After`, quando vem, diz quantos\nsegundos esperar para pedir outro código.\n",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pix/keys/{id}": {
      "delete": {
        "tags": [
          "PIX"
        ],
        "summary": "Remove uma chave PIX",
        "description": "A chave sai do DICT antes de sair daqui. Apagar só do nosso lado\ndeixaria o arranjo apontando para uma conta que não espera mais receber\npor ela — e sem nenhuma tela onde o titular a veja para tentar de novo.\n\nVale para as cinco espécies, inclusive CPF e CNPJ. A conta precisa\nestar ativa (`403 FORBIDDEN` se não); não exige `X-Step-Up`. A chave de\noutro titular é `404`. Uma chave `REMOVED` (que já não é sua no DICT) sai\nsó da lista. A exclusão entra no histórico de segurança do titular\n(`PIX_KEY`).\n\n**Só com sessão de cliente**: não aceita chave de API (com `X-API-Key`\na resposta é `403 FORBIDDEN`), nem com `pix:keys:write`. Excluir não\npede segundo fator, e uma chave de API vazada apagava todas as chaves\nda conta de uma vez; o cadastro continua pela chave de API, porque\nexige o `X-Step-Up` do titular.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "removePixKey",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "O `id` da chave, e nao a chave em si.",
            "schema": {
              "type": "string"
            },
            "examples": {
              "chave": {
                "value": "9f2c1a7b4e6d8035"
              }
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "204": {
            "description": "Removida"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "O arranjo recusou a exclusão (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/keys/portability": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Traz uma chave PIX de outra instituição",
        "description": "Pede a portabilidade de uma chave que hoje está em outra instituição\n(funcionalidade `pix_key_claims`, desligada por padrão; desligada,\n`403 FEATURE_DISABLED`). É o botão \"Trazer chave de outra instituição\"\nda tela de chaves.\n\n- **Tipos**: CPF, CNPJ, e-mail e telefone. A chave aleatória não se\n  porta (`422 INVALID_REQUEST`). CPF e CNPJ só o documento do próprio\n  titular; e-mail e telefone exigem `verificationId`, como no\n  cadastro (`POST /v1/pix/keys/verifications`).\n- **Exige `X-Step-Up`** com `purpose: pix_key` e **`Idempotency-Key`**.\n  Repetir a mesma chave devolve o mesmo pedido, sem pedir de novo à\n  outra ponta.\n- A conta precisa estar ativa. A chave pedida conta para o teto do\n  titular (5 pessoa física, 20 pessoa jurídica) desde o pedido.\n- **Só com sessão de cliente**: não aceita chave de API, e o\n  atendimento não pede pelo titular.\n\nA resposta é `202`: o andamento vem depois, pela lista de chaves\n(`status`, `statusDetail`, `claimActions`, `claimRequest`). Se a\noutra ponta não respondeu a tempo, o pedido fica `UNKNOWN` e **não deve\nser repetido**: um novo pedido para a mesma chave é `409 CONFLICT` até\no andamento chegar. O pedido entra no histórico de segurança do\ntitular (`PIX_KEY`), com a chave mascarada.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "startPixKeyPortability",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "examples": [
                      "12345678909"
                    ]
                  },
                  "verificationId": {
                    "type": "string",
                    "description": "A confirmação de posse do e-mail ou do telefone. Obrigatório\nnesses dois tipos; ignorado nos demais.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido registrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixKeyClaimRequest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` com `purpose: pix_key` (`STEP_UP_REQUIRED`); a\nconta não está ativa, a origem não é o titular (`FORBIDDEN`); ou a\nfuncionalidade está desligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A chave já está na sua conta, já há pedido em andamento para ela,\nela não pode vir para esta conta, o titular está no teto de chaves,\nou a `Idempotency-Key` já foi usada em outro pedido.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Chave inválida ou aleatória; CPF ou CNPJ que não é o do titular;\ne-mail ou telefone sem confirmação válida (`INVALID_REQUEST`); ou\nrecusa da outra ponta (`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/keys/{id}/claim": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Responde a uma portabilidade ou reivindicação de chave PIX",
        "description": "Age sobre o processo em andamento de uma chave da conta\n(funcionalidade `pix_key_claims`, desligada por padrão; desligada,\n`403 FEATURE_DISABLED`). A ação precisa estar em `claimActions` da\nchave na lista (`GET /v1/pix/keys`); fora disso, `409 CONFLICT`.\n\n| `action` | Quem | O quê |\n| --- | --- | --- |\n| `ACCEPT` | o titular da chave pedida por outra instituição | libera a chave para a outra instituição |\n| `REJECT` | o mesmo | mantém a chave (recusa o pedido) |\n| `OPEN` | quem pediu a chave aqui | confirma a abertura do pedido de reivindicação |\n| `CANCEL` | quem pediu a chave aqui | desiste do pedido |\n| `COMPLETE` | quem pediu a chave aqui | confirma a chave depois que a outra instituição a liberou |\n\n- **Exige `X-Step-Up`** com `purpose: pix_key` em toda ação e\n  **`Idempotency-Key`**. Repetir a mesma chave devolve o mesmo pedido,\n  sem repetir a ação.\n- A chave de outro titular é `404`. A conta precisa estar ativa.\n- **Só com sessão de cliente**: não aceita chave de API, e o\n  atendimento não responde pelo titular.\n- Um pedido por vez por chave, e uma ação por andamento: depois de\n  agir, a próxima ação só aparece quando a outra ponta responder.\n\nA resposta é `202`; o resultado vem pela lista de chaves. `UNKNOWN`\né a outra ponta sem resposta a tempo: **não repita**. A ação entra no\nhistórico de segurança do titular (`PIX_KEY`), com a chave mascarada.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "actOnPixKeyClaim",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "O `id` da chave, e nao a chave em si.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "$ref": "#/components/schemas/PixKeyClaimAction"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido registrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixKeyClaimRequest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` com `purpose: pix_key` (`STEP_UP_REQUIRED`); a\nconta não está ativa, a origem não é o titular (`FORBIDDEN`); ou a\nfuncionalidade está desligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A ação não cabe à chave agora, já há pedido em andamento, ou a\n`Idempotency-Key` já foi usada em outro pedido.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Ação inválida (`INVALID_REQUEST`) ou recusa da outra ponta (`PROVIDER_ERROR`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/lookup": {
      "get": {
        "tags": [
          "PIX"
        ],
        "summary": "Consulta o titular de uma chave no DICT",
        "description": "Chame antes de transferir e mostre o nome devolvido para conferência.\n\nA consulta é feita em nome da conta em uso, e uma conta bloqueada não\nconsulta (`403 FORBIDDEN`). Há um limite **por conta e por titular**,\nalém do limite por endereço: 20 consultas a cada 10 minutos, e a chave\nque o DICT não conhece conta como 3. Toda rota que devolve o dono de\numa chave gasta o mesmo saldo: esta, a leitura de BR Code\n(`POST /v1/pix/brcode/parse`) e o favorecido por chave\n(`POST /v1/beneficiaries`). A consulta feita dentro do envio\n(`POST /v1/pix/transfers`) tem saldo próprio, de 60 a cada 10 minutos,\ncom a mesma regra para a chave que não existe. A chave de API gasta\no mesmo limite da conta. Acima, `429 RATE_LIMITED` com `Retry-After`;\na primeira recusa fica no histórico de segurança do titular\n(`PIX_LOOKUP_LIMIT`). O `document` é sempre a **chave mascarada**,\nnunca o CPF ou o CNPJ do titular, mesmo quando o arranjo informa o\ndocumento inteiro. A consulta mostrada na tela não é a do envio: cada\n`POST /v1/pix/transfers` consulta a chave de novo, e é essa consulta que\ndefine o identificador da transferência.\n\n**Instalação de testes do provedor.** Em alguns provedores, qualquer\nchave sintaticamente válida existe no DICT - com uma exceção\ndeliberada: **chave cujo último caractere seja `0`** (um CPF terminado\nem zero, por exemplo) é recusada pelo arranjo, com\n`422 PROVIDER_ERROR`. É o caminho de erro que a sua tela precisa saber\ndesenhar, e ele é reproduzível.\n\nEm outros, quem responde é o DICT do próprio provedor (ou a massa de\ntestes dele), e a regra do último caractere não vale: a chave que ele\nnão conhece é `422 PROVIDER_ERROR`. Não suponha que uma chave\ninexistente numa instalação de testes também seja inexistente em outra,\nnem o contrário.\n",
        "operationId": "lookupPixKey",
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "examples": {
              "email": {
                "value": "eurico.b@email.com.br"
              }
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Titular da chave",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "description": "Nome do titular, como o DICT o devolve.",
                      "examples": [
                        "Auto Pecas Farol LTDA"
                      ]
                    },
                    "document": {
                      "type": "string",
                      "description": "A chave mascarada, e nao o CPF do titular. Para chave de\ne-mail sai `e*******@email.com.br`; para CPF,\n`***.533.447-**`. Conferir o titular nao exige ver o\ndocumento inteiro de um terceiro.\n",
                      "examples": [
                        "e*******@email.com.br"
                      ]
                    },
                    "bank": {
                      "type": "string",
                      "description": "Instituição do recebedor: o nome ou o código ISPB (8\ndígitos), conforme o que o provedor devolve na consulta.\n",
                      "examples": [
                        "Itau",
                        "18684408"
                      ]
                    },
                    "recipientRef": {
                      "type": "string",
                      "description": "Referência opaca do titular devolvido, ligada à conta e à\nchave. Mande-a em `expectedRecipientRef` no envio\n(`POST /v1/pix/transfers`), no agendamento\n(`POST /v1/schedules/pix`) e no pedido de aprovação\n(`POST /v1/approvals/pix`): se a chave tiver passado a ser\nde outro titular, a operação é recusada antes de reservar\nqualquer valor. Ausente quando o arranjo não permite\nprovar o titular. As telas da plataforma (web e\naplicativo) sempre a repetem no envio quando ela vem; na\nintegração por chave de API (`POST /v1/pix/transfers`),\nrepeti-la é opcional (recomendado).\n",
                      "examples": [
                        "3f2a9c0d6b1e4f7a8c5d2e9b0a1f3c4d"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Chave malformada (`INVALID_REQUEST`) ou recusada pelo arranjo\n(`PROVIDER_ERROR`) - inclusive quando ela simplesmente não existe no\nDICT.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/transfers": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Transfere por PIX",
        "description": "**`Idempotency-Key` é obrigatório.** Sem ele a chamada responde `400`\nantes de qualquer efeito. Repetir a mesma chave com o mesmo corpo\ndevolve a resposta original, marcada com `Idempotent-Replay: true`, e o\ndinheiro sai uma vez só. A mesma chave com corpo diferente responde\n`409`.\n\nResponde `202`, e não `201`: a transferência foi aceita, mas o desfecho\npode ainda não existir. Acompanhe pelo `status` da transação.\n\nTrês desfechos possíveis no corpo da resposta:\n\n- `PROCESSING` ou `COMPLETED` — seguiu o caminho normal.\n- `HELD_FOR_REVIEW` — o motor de risco reteve. O titular decide em\n  `POST /v1/transactions/{id}/review`, e `riskSignals` explica o que\n  chamou atenção e o que foi conferido e está normal.\n\nA partir de R$ 1.000,00 - o valor exato inclusive - exige `X-Step-Up`\ncom `purpose: transfer`, mesmo\ncom a sessão aberta. Vale também pela soma do dia: o Pix que leva a\nsoma dos Pix enviados no dia (por chave, por agência e conta, QR Code e\ndevolução) a R$ 1.000,00 exige o `X-Step-Up`, ainda que abaixo desse\nvalor (`403 STEP_UP_REQUIRED`, sem reservar nada; a instalação pode\nconfigurar o teto da soma).\n\nEm instalações cujo provedor exige uma pessoa na ponta, o envio só sai\npor sessão de cliente com aparelho identificado. Por chave\nde API, ou por uma sessão sem aparelho, a resposta é `503\nPROVIDER_ERROR` **antes** de qualquer débito: nada é reservado, e\nrepetir pelo mesmo canal não adianta.\n\n**Pix por agência e conta** (funcionalidade `pix_manual`). Informe\n**exatamente um** entre `key` (Pix por chave), `bankAccount` (banco,\nagência, conta, tipo, nome e CPF/CNPJ digitados) e `beneficiaryId`\n(favorecido por conta bancária salvo em `/v1/beneficiaries`); dois ou\nmais respondem `422 INVALID_REQUEST`. Sem chave não há consulta ao\nDICT: **o nome e o documento são os informados pelo cliente e não são\nconfirmados pelo banco de destino**. As regras são as do Pix por chave\n(segundo fator com `purpose: transfer`, selfie, alçada, conta ativa,\nlimites do Pix inclusive o noturno, risco). Na alçada da conta\nempresarial (o valor somado às saídas do dia sem segunda assinatura) o\nPix por agência e conta é recusado com `422\nAPPROVAL_REQUIRED`, a mesma resposta da alçada no resto da API: não há\naprovação conjunta dele, e o pedido de aprovação\n(`POST /v1/approvals/pix`) só aceita Pix por chave. O banco parceiro\nnão tem idempotência nem identificador antes do envio: o Lume registra\na tentativa e nunca a repete, e enquanto houver um Pix por agência e conta **igual** (mesma\nconta, mesmo destino, mesmo valor) sem desfecho, outro igual responde\n`409 CONFLICT`. **Repetição com a mesma `Idempotency-Key`:** se a chave\njá é de um Pix por agência e conta desta conta (por exemplo, o cliente\nrepetiu depois de um `5xx`), a resposta é a transação anterior, com o\nmesmo status, e nada é enviado nem reservado de novo — mesmo depois de\na soma do dia alcançar a alçada, e também quando o favorecido salvo\nfoi apagado nesse meio-tempo (o desfecho anterior vale mais que o\n`404`). Essa repetição é decidida pela regra de negócio e **não** traz\no cabeçalho `Idempotent-Replay`. Chave reaproveitada com outro valor,\ndestino, tipo de conta ou documento responde `409\nIDEMPOTENCY_KEY_REUSED`. A resposta traz `bankAccount` com a conta e o\ndocumento mascarados; `counterparty.pixKey` vem vazio.\n",
        "operationId": "createPixTransfer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amountCents"
                ],
                "description": "Exatamente um entre `key`, `bankAccount` e `beneficiaryId`.",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Chave PIX do destinatário.",
                    "examples": [
                      "eurico.b@email.com.br"
                    ]
                  },
                  "bankAccount": {
                    "$ref": "#/components/schemas/BankAccountInput"
                  },
                  "beneficiaryId": {
                    "type": "string",
                    "description": "Favorecido por conta bancária (`kind: bank_account`) da\nprópria conta. O de outra conta, ou inexistente, responde\n`404`; o favorecido por chave, `422`.\n"
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Inteiro em centavos. 5000 são R$ 50,00.",
                    "examples": [
                      5000
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140,
                    "examples": [
                      "Pagamento da nota 8841"
                    ]
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa do serviço que a tela mostrou (`GET /v1/fees/quote`),\nem centavos (funcionalidade `service_fees`). Se a tarifa real\nfor MAIOR, a operação é recusada com `409 CONFLICT` e\n`details.reason: FEE_CHANGED` (`details.feeCents` traz a\nnova, `details.expectedFeeCents` a enviada e\n`details.feeReason` o motivo) antes de reservar qualquer\nvalor; menor ou igual, segue. Essa recusa NÃO fica gravada\nsob a `Idempotency-Key`: refaça a cotação e repita com a\ntarifa nova (a mesma chave vale) e um novo segundo fator (o\nanterior foi gasto). Negativo é `422 INVALID_REQUEST`.\nAusente, a sessão aceita qualquer tarifa; com chave de API e\n`service_fees` ligada ele é obrigatório: sem ele, `422\nINVALID_REQUEST` antes do segundo fator, nada reservado e a\n`Idempotency-Key` livre. Saldo que cobre o valor mas\nnão a tarifa responde `422 INSUFFICIENT_FUNDS` com\n`details.amountCents`, `details.feeCents` e\n`details.totalCents`; com tarifa de entrada ainda pendente na\nconta, a saída tem de deixar saldo para ela, e a recusa traz\n`details.pendingFeesCents` (somado no total). A tarifa cobrada volta no\n`Transaction` (`feeCents`, `feeTransactionId`, `feeReason`,\n`feeStatus`).\n",
                    "examples": [
                      190
                    ]
                  },
                  "expectedRecipientRef": {
                    "type": "string",
                    "description": "O `recipientRef` de `GET /v1/pix/lookup`: o titular que a\ntela mostrou. Só vale com `key`. O envio consulta a chave de\nnovo e, se o titular de agora for outro, recusa com `409\nCONFLICT` e `details.reason: RECIPIENT_CHANGED`; se o\narranjo não permitir provar o titular, `details.reason:\nRECIPIENT_UNVERIFIED`. Nada é reservado nem enviado, e a\nrecusa NÃO fica gravada sob a `Idempotency-Key`: consulte a\nchave, mostre o titular novo e repita com o novo\n`recipientRef` e um novo segundo fator (o anterior foi\ngasto). Opcional (opt-in) na integração por chave de API:\nausente, o envio não confere o titular; recomendado\nconsultar a chave, mostrar o titular e repassar o\n`recipientRef`. As telas da plataforma (web e aplicativo)\nsempre o enviam quando a consulta o devolve.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Aceita",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "404": {
            "description": "O `beneficiaryId` não é um favorecido desta conta (exceto na repetição\nde um Pix por agência e conta já feito com a mesma `Idempotency-Key`,\nque devolve `202`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A mesma chave de idempotência já foi usada com outro conteúdo\n(`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior ainda está em\ncurso (`IDEMPOTENCY_IN_FLIGHT` — repita em alguns segundos). No\nPix por agência e conta, também um igual ainda em processamento\n(`CONFLICT`, \"Já há uma transferência igual em processamento.\"), e\na repetição com a mesma chave de um Pix em trânsito cujo destino\nainda não foi gravado (`IDEMPOTENCY_IN_FLIGHT`; não é \"outro\nconteúdo\": aguarde e repita com a mesma chave). Com\n`expectedRecipientRef`, o titular da chave mudou (`CONFLICT`,\n`details.reason: RECIPIENT_CHANGED`) ou não pôde ser confirmado\n(`RECIPIENT_UNVERIFIED`), sem nada reservado.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Saldo insuficiente (`INSUFFICIENT_FUNDS`), limite excedido\n(`LIMIT_EXCEEDED`), chave inválida ou da própria conta. No Pix por\nagência e conta: mais de um destino informado, dados do destino\ninválidos (`INVALID_REQUEST`, `details.field` diz o campo:\n`bankIspb`, `branch`, `account`, `accountType`, `holderName`,\n`holderDocument` ou `description`), a conta de destino é a\nprópria, ou o valor, somado às saídas do dia, alcança a alçada da\nconta empresarial (`APPROVAL_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de consultas de chave da conta no envio atingido\n(`RATE_LIMITED`, 60 a cada 10 minutos; a chave que não existe conta\ncomo 3). `Retry-After` diz quanto esperar. Nada foi reservado nem\nenviado; repita com a mesma `Idempotency-Key`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor desta instalação não envia Pix por agência e conta."
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/ted/transfers": {
      "post": {
        "tags": [
          "TED"
        ],
        "summary": "Envia uma TED",
        "description": "TED de saída para uma conta de **outra instituição**, com os dados do\nfavorecido digitados ou a partir de um favorecido por conta bancária\nsalvo (`beneficiaryId`; o servidor completa o destino). Só com sessão de\ncliente: **não** aceita chave de API.\n\n**`Idempotency-Key` é obrigatório**, como no Pix. Repetir a mesma chave\ncom o mesmo corpo devolve a resposta original (`Idempotent-Replay: true`)\ne a TED sai uma vez só.\n\n**Exige `X-Step-Up` com `purpose: ted` em toda TED**, qualquer que seja o\nvalor (decisão ainda a confirmar; o `purpose: transfer` do Pix não vale).\n\n**Janela.** A TED só funciona em dias úteis, das 08h às 16h30 (horário\nda operação; configurável por instalação). Fora dela a TED é **recusada**\nantes de qualquer débito, com `422 INVALID_REQUEST` e a janela e o\npróximo dia útil em `details` - não é agendada. Feriado: o Lume não tem\ncalendário de feriados; o banco recusa e o resultado é o mesmo (valor\ndevolvido, mesma resposta).\n\n**No máximo uma vez.** O banco parceiro não tem chave de idempotência\npara TED. O Lume registra a tentativa antes de enviar e nunca a repete:\nse o prazo esgotar ou a resposta se perder, a TED fica `PROCESSING` e o\ndesfecho chega pelo aviso do banco ou pelo extrato. Enquanto houver uma\nTED **igual** (mesma conta, mesmo favorecido, mesmo valor) ainda sem desfecho\n(em envio, aceita e não liquidada, ou de desfecho desconhecido), outra\nigual responde `409 CONFLICT`.\n\nResponde `202`: a TED foi aceita, mas a liquidação vem depois.\nAcompanhe pelo `status` da transação. Na alçada da conta empresarial\n(o valor somado às saídas do dia sem segunda assinatura) a TED é\nrecusada com `422 APPROVAL_REQUIRED`: ainda não há aprovação conjunta\nde TED.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "createTedTransfer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amountCents"
                ],
                "description": "`beneficiaryId` **ou** os seis dados do favorecido (`bankIspb`,\n`branch`, `account`, `accountType`, `holderName`,\n`holderDocument`), nunca os dois.\n",
                "properties": {
                  "beneficiaryId": {
                    "type": "string",
                    "description": "Favorecido por conta bancária (`kind: bank_account`) da\nprópria conta. O de outra conta, ou inexistente, responde\n`404`; o favorecido por chave, `422`.\n"
                  },
                  "bankIspb": {
                    "type": "string",
                    "description": "ISPB do banco de destino, 8 dígitos, de uma instituição de\n`GET /v1/banks`. Fora da lista: `422`, `details.field:\nbankIspb`.\n",
                    "examples": [
                      "18236120"
                    ]
                  },
                  "branch": {
                    "type": "string",
                    "description": "Agência, de 1 a 5 dígitos.",
                    "examples": [
                      "0001"
                    ]
                  },
                  "account": {
                    "type": "string",
                    "description": "Conta com o dígito verificador concatenado, de 1 a 20\ndígitos. Pontuação (`91926197-7`) é aceita e retirada.\n",
                    "examples": [
                      "919261977"
                    ]
                  },
                  "accountType": {
                    "type": "string",
                    "enum": [
                      "CHECKING",
                      "SAVINGS",
                      "PAYMENT",
                      "SALARY"
                    ]
                  },
                  "holderName": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 140,
                    "description": "Nome do titular da conta de destino, como digitado."
                  },
                  "holderDocument": {
                    "type": "string",
                    "description": "CPF ou CNPJ do titular de destino, com ou sem pontuação."
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Inteiro em centavos. 5000 são R$ 50,00.",
                    "examples": [
                      5000
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140,
                    "description": "Mais de 140 caracteres é recusado (`422`, `details.field: description`)."
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa do serviço que a tela mostrou (`GET /v1/fees/quote`),\nem centavos (funcionalidade `service_fees`). Se a tarifa real\nfor MAIOR, a operação é recusada com `409 CONFLICT` e\n`details.reason: FEE_CHANGED` (`details.feeCents` traz a\nnova, `details.expectedFeeCents` a enviada e\n`details.feeReason` o motivo) antes de reservar qualquer\nvalor; menor ou igual, segue. Essa recusa NÃO fica gravada\nsob a `Idempotency-Key`: refaça a cotação e repita com a\ntarifa nova (a mesma chave vale) e um novo segundo fator (o\nanterior foi gasto). Negativo é `422 INVALID_REQUEST`.\nAusente, aceita qualquer tarifa. Saldo que cobre o valor mas\nnão a tarifa responde `422 INSUFFICIENT_FUNDS` com\n`details.amountCents`, `details.feeCents` e\n`details.totalCents`; com tarifa de entrada ainda pendente na\nconta, a saída tem de deixar saldo para ela, e a recusa traz\n`details.pendingFeesCents` (somado no total). A tarifa cobrada volta no\n`Transaction` (`feeCents`, `feeTransactionId`, `feeReason`,\n`feeStatus`).\n",
                    "examples": [
                      190
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Aceita. `status` é `PROCESSING` (o normal), `COMPLETED`,\n`HELD_FOR_REVIEW` ou `FAILED` (recusada pelo banco, valor devolvido).\n`ted` traz o destino com a conta e o documento mascarados.\n\n**Repetição com a mesma `Idempotency-Key`:** se a chave já é de uma\nTED desta conta (por exemplo, o cliente repetiu depois de um `5xx`),\na resposta é a TED anterior, com o mesmo status, e nada é enviado nem\nreservado de novo. Vale também quando o favorecido salvo foi apagado\nnesse meio-tempo: o desfecho anterior (`202`) vale mais que o `404`.\nSe a TED anterior falhou antes de gravar o destino (valor devolvido),\na repetição devolve essa mesma TED, `FAILED`. Essa repetição é decidida\npela regra de negócio, e **não** traz o cabeçalho `Idempotent-Replay`\n(só a repetição servida pelo registro da resposta o traz). Chave\nreaproveitada com outro valor, destino ou tipo responde `409`; uma\nfalha de leitura do banco ao procurar a chave responde `5xx`, nunca\ncomo ausência.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`), conta bloqueada\n(`FORBIDDEN`), ou TED desligada nesta\ninstalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "O `beneficiaryId` não é um favorecido desta conta (exceto na repetição\nde uma TED já feita com a mesma `Idempotency-Key`, que devolve `202`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Já há uma transferência igual em processamento (`CONFLICT`,\n\"Já há uma transferência igual em processamento.\"), ou a mesma chave\nde idempotência foi usada com outro conteúdo\n(`IDEMPOTENCY_KEY_REUSED`). A repetição com a mesma chave de uma TED\nem trânsito cujo destino ainda não foi gravado responde\n`IDEMPOTENCY_IN_FLIGHT` (não é \"outro conteúdo\": aguarde e repita\ncom a mesma chave).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Dados do favorecido inválidos (`INVALID_REQUEST`, `details.field`\ndiz o campo; `bankIspb` também quando o ISPB tem 8 dígitos mas não\nestá em `GET /v1/banks`); **fora da janela** (`INVALID_REQUEST`, com\n`details.window` - \"08:00-16:30\" -, `details.nextBusinessDay` e\n`details.nextOpensAt`); destino na própria instituição do banco\nparceiro, que só leva TED para outras instituições\n(`INVALID_REQUEST`, `details.field` = `bankIspb`; para essa conta,\nuse Pix); limite diário de TED ou teto noturno\nexcedido (`LIMIT_EXCEEDED`); saldo insuficiente\n(`INSUFFICIENT_FUNDS`); conta de destino igual à própria conta;\nvalor que, somado às saídas do dia, alcança a alçada da conta\nempresarial (`APPROVAL_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor desta instalação não envia TED."
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/banks": {
      "get": {
        "tags": [
          "TED"
        ],
        "summary": "Lista as instituições de destino (TED e Pix por agência e conta)",
        "description": "A lista oficial do Banco Central, reunida por ISPB:\n\n- `ted: true` - participante do STR com código de compensação (COMPE):\n  recebe TED. Ficam fora os participantes sem código (Selic, Bacen,\n  STN, câmaras).\n- `pix: true` - participante **ativo** do Pix na modalidade \"Provedor de\n  Conta Transacional\": recebe Pix por agência e conta. Ficam fora os\n  iniciadores e os em adesão.\n\nEm liquidação extrajudicial fica fora das duas. É a mesma lista que o\nservidor confere: a TED (`POST /v1/ted/transfers`) só sai para `ted`, o\nPix por agência e conta (`POST /v1/pix/transfers` e\n`POST /v1/schedules/pix` com `bankAccount`) só para `pix`, e o\nfavorecido por conta bancária (`POST /v1/beneficiaries`) só é salvo com\num dos dois; fora dela, `422` com `details.field: bankIspb`.\n\nVem inteira (pouco mais de mil instituições), em ordem de ISPB: a busca\ne o filtro por finalidade são do cliente. `sources` diz de que arquivo\ndo Banco Central cada lista saiu. Só com sessão de cliente: **não**\naceita chave de API.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listBanks",
        "responses": {
          "200": {
            "description": "As instituições.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankDirectory"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/pix/brcode": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Gera um BR Code de cobrança estático",
        "description": "Rota anterior ao recurso `POST /v1/pix/qrcodes`, que a substitui:\ngera uma cobrança **estática** pelo mesmo caminho e devolve a mesma\ncobrança, acrescida de `payload` e `amountCents` como sempre. Fica por\ncompatibilidade; o que é novo usa `/v1/pix/qrcodes`.\n\nA chave precisa estar cadastrada na própria conta e ativa: gerar\ncobrança com a chave de outra pessoa é exatamente o golpe que o QR Code\nfalsificado explora. **A conta precisa estar ativa**: conta bloqueada,\npendente ou encerrada não gera cobrança (`403 FORBIDDEN`), porque cobrar\né redirecionar entrada de dinheiro.\n\n`amountCents` maior que zero produz um código de uso único.\n\nO código é **gravado** (aparece em `GET /v1/pix/qrcodes`) e, quando o\nprovedor da instalação gera cobrança (`pix_qr_dynamic`), nasce nele, e\né assim que o Pix pago volta ligado à cobrança. `Idempotency-Key` é\nopcional aqui (8 a 128 caracteres, `400` se for outro tamanho): com\nele, repetir o pedido devolve a mesma cobrança, e a mesma chave com\noutro pedido é `409` (`IDEMPOTENCY_KEY_REUSED`).\n",
        "operationId": "createBRCode",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Opcional. Com ela, repetir o pedido devolve a mesma cobrança.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "examples": [
                      "financeiro@integradora.com.br"
                    ]
                  },
                  "amountCents": {
                    "type": "integer",
                    "description": "Omitido ou zero gera um código de valor livre.",
                    "examples": [
                      12990
                    ]
                  },
                  "city": {
                    "type": "string",
                    "deprecated": true,
                    "description": "**Ignorada.** A cidade do recebedor é a do cadastro do\ntitular, normalizada pelo servidor (maiúsculas, sem acento,\nsem \"de/do/da/dos/das\", até 15 caracteres; sem cidade no\ncadastro, `BRASIL`). Aceita só para não quebrar quem a\nenvia.\n",
                    "examples": [
                      "RIO DE JANEIRO"
                    ]
                  },
                  "txid": {
                    "type": "string",
                    "description": "Seu identificador da cobrança, até 25 caracteres\nalfanuméricos. **Só vale quando o código é gerado\nlocalmente** (provedor sem `pix_qr_dynamic`); com a Azify o\nidentificador de conciliação é gerado pelo Lume. Omitido,\nvale o identificador de conciliação.\n",
                    "examples": [
                      "PEDIDO8841"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cobrança gerada: o recurso de `POST /v1/pix/qrcodes` (`id`, `type`,\n`status`...), que inclui `payload` e `amountCents`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixQRCode"
                }
              }
            }
          },
          "400": {
            "description": "O corpo é ilegível, ou o `Idempotency-Key` enviado não tem de 8 a 128 caracteres.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "A chave não pertence a esta conta, não está ativa, ou a conta não\nestá ativa; chave de API sem o escopo `pix:write`; cobrança por QR\nCode desligada nesta instalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A chave de idempotência já foi usada com outro pedido\n(`IDEMPOTENCY_KEY_REUSED`), ou a tentativa anterior ainda está em\nandamento (`IDEMPOTENCY_IN_FLIGHT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/qrcodes": {
      "get": {
        "tags": [
          "PIX"
        ],
        "summary": "Cobranças por QR Code da conta",
        "description": "As cobranças por QR Code desta conta em uso, da mais recente para a\nmais antiga (até 100). `status` é `OPEN`, `PAID`, `EXPIRED` ou\n`DELETED`; `EXPIRED` é a aberta cujo vencimento passou, mesmo sem\nevento do provedor. Chave de API precisa do escopo `pix:read`.\n",
        "operationId": "listPixQRCodes",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Cobranças",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PixQRCode"
                      }
                    },
                    "keyTypes": {
                      "type": "array",
                      "description": "Tipos de chave que a cobrança por QR Code aceita nesta\ninstalação (a Azify: `CPF`, `CNPJ` e `EVP`; o gerador local:\ntodos). A tela só oferece as chaves destes tipos.\n",
                      "items": {
                        "type": "string",
                        "enum": [
                          "CPF",
                          "CNPJ",
                          "EMAIL",
                          "PHONE",
                          "EVP"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Gera uma cobrança por QR Code (estática ou dinâmica)",
        "description": "Sem `expiresAt` a cobrança é **estática**; com `expiresAt`, **dinâmica**\n(exige `amountCents` maior que zero e vencimento no futuro, em até um\nano).\n\n- **Estática**: valor fixo (`amountCents` > 0, uso único) ou livre\n  (`amountCents` omitido ou zero: quem paga escolhe o valor, e o código\n  pode ser pago mais de uma vez). `amountEditable: true` com valor\n  fixo é `422`.\n- **Dinâmica**: valor e vencimento; `amountEditable: true` deixa o\n  pagador mudar o valor. **Uso único** (decisão da instalação, não um\n  campo do pedido). Só existe quando o provedor da instalação gera\n  cobrança (`pix_qr_dynamic`); sem ele, `501`.\n\n**Quem gera o código** é o provedor, quando declara `pix_qr_dynamic`\n(a Azify): é o que traz o Pix pago de volta ligado à cobrança. Sem a\ncapacidade (a Celcoin), o BR Code estático é gerado localmente, e o\ncrédito do Pix não se liga à cobrança.\n\n**A cidade** do recebedor é a do cadastro do titular, normalizada pelo\nservidor; o pedido não a recebe.\n\n**A chave** é da própria conta, ativa e de um tipo que o provedor gera\nem QR Code (a Azify: CPF, CNPJ e chave aleatória; e-mail e telefone\nsão `422`). **A conta precisa estar ativa** (`403` se não).\n\n**Idempotência obrigatória**: a mesma `Idempotency-Key` devolve a mesma\ncobrança (marcada com `Idempotent-Replay: true`), e não outro código\npagável; com pedido diferente, `409` (`IDEMPOTENCY_KEY_REUSED`; a rota\n`POST /v1/pix/brcode` compartilha o espaço de chaves da conta). Uma\nrecusa `4xx` definitiva (`403`, `422`...) fica gravada sob a chave:\ndepois de corrigir o pedido, use uma chave nova. **O `409`\n`IDEMPOTENCY_IN_FLIGHT`, o `5xx` e a falha de rede não renovam a\nchave**: o código pode ter sido gerado, e repetir com a mesma chave\ndevolve a mesma cobrança em vez de outro código pagável.\n\nSem segundo fator: gerar cobrança não tira dinheiro. Chave de API\nprecisa do escopo `pix:write`.\n",
        "operationId": "createPixQRCode",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Uma chave da própria conta, ativa.",
                    "examples": [
                      "financeiro@integradora.com.br"
                    ]
                  },
                  "amountCents": {
                    "type": "integer",
                    "format": "int64",
                    "minimum": 0,
                    "description": "Inteiro em centavos. Obrigatório (maior que zero) na\ndinâmica; omitido ou zero na estática é valor livre.\n",
                    "examples": [
                      19900
                    ]
                  },
                  "amountEditable": {
                    "type": "boolean",
                    "default": false,
                    "description": "O pagador pode mudar o valor. Só na dinâmica; na estática\no valor livre já é editável por natureza.\n"
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Vencimento, no futuro e em até um ano. Presente = cobrança\ndinâmica. Guardado em UTC.\n",
                    "examples": [
                      "2026-10-10T15:00:00Z"
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140,
                    "description": "Texto que o pagador vê. Estático: texto livre; dinâmico: um\nitem `Descricao` de `additionalInfo`.\n",
                    "examples": [
                      "Pedido 42"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cobrança gerada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixQRCode"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "A chave não é desta conta ou não está ativa; a conta não está ativa;\nchave de API sem o escopo `pix:write`; cobrança por QR Code desligada\nnesta instalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A mesma chave de idempotência já foi usada com outro conteúdo ou\noutro pedido (`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior\nainda está em andamento (`IDEMPOTENCY_IN_FLIGHT`; inclui a tentativa\nanterior cujo resultado no provedor ainda não se conhece: espere e\nrepita com a mesma chave).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Tipo de chave que o provedor não gera, vencimento no passado ou\nlonge demais, valor inválido para o tipo (dinâmico sem valor,\nestático com valor editável), descrição longa demais, ou pedido\nrecusado pelo provedor (`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor desta instalação não gera cobrança com vencimento.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/qrcodes/{id}": {
      "delete": {
        "tags": [
          "PIX"
        ],
        "summary": "Exclui uma cobrança por QR Code",
        "description": "**O provedor primeiro, o banco depois**: a falha do provedor mantém a\ncobrança `OPEN` e responde `503` (ou `422`, se ele a recusar). Um\ncódigo que o provedor já não tem (`404` dele) conta como excluído. Devolve a cobrança com\n`status: DELETED`; excluir de novo devolve o mesmo, sem outra chamada\nao provedor.\n\n- Cobrança já **paga** não se exclui: `409`.\n- Cobrança de outra conta é `404`, igual à que não existe.\n- A conta precisa estar ativa (`403` se não).\n- Cobrança gerada **localmente** (provedor sem `pix_qr_dynamic`) só sai\n  da lista: o BR Code já entregue continua podendo ser pago.\n\nChave de API precisa do escopo `pix:write`.\n",
        "operationId": "deletePixQRCode",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "O `id` da cobrança.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Cobrança excluída",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixQRCode"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A cobrança já foi paga, ou foi gerada por outro provedor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "O provedor recusou a exclusão (`PROVIDER_ERROR`); a cobrança continua aberta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/brcode/parse": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Lê um BR Code",
        "description": "Confere o código antes de pagar. Quem o lê é o provedor da instalação,\ncom a conta em uso, e **o código pode ser estático ou dinâmico**: o\nleitor local do Lume só complementa a exibição (`city`, `txid`,\n`oneTime`) e uma falha dele não recusa o código. Nas instalações cujo\nprovedor não decodifica, o código é lido localmente (só estático) e o\nnome vem do DICT.\n\nO nome devolvido vem do provedor ou do DICT, e não do que está escrito\nno código: a leitura do QR não é confiança suficiente, e quem responde\npela titularidade é o diretório.\n\n**O que esta resposta mostra não autoriza o pagamento.** O pagamento\n(`POST /v1/pix/brcode/pay`) lê o código de novo e paga o que essa\nleitura disser; nenhum identificador do provedor (`qrCodeId`,\n`endToEndId`) vai ao cliente.\n",
        "operationId": "parseBRCode",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payload"
                ],
                "properties": {
                  "payload": {
                    "type": "string",
                    "description": "O conteúdo lido do QR Code, no padrão EMV (copia e cola).",
                    "examples": [
                      "00020126580014BR.GOV.BCB.PIX0136financeiro@integradora.com.br5204000053039865802BR6014RIO DE JANEIRO62070503***6304ABCD"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Código lido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "qrType",
                    "amountEditable",
                    "document"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "description": "A chave Pix do recebedor. Vazia quando o código não a traz (QR dinâmico)."
                    },
                    "name": {
                      "type": "string"
                    },
                    "bank": {
                      "type": "string",
                      "description": "O banco do recebedor, quando o provedor o informa."
                    },
                    "city": {
                      "type": "string"
                    },
                    "amountCents": {
                      "type": "integer",
                      "description": "O valor do código, em centavos; zero quando o código não\ntem valor.\n"
                    },
                    "txid": {
                      "type": "string"
                    },
                    "oneTime": {
                      "type": "boolean"
                    },
                    "qrType": {
                      "type": "string",
                      "enum": [
                        "STATIC",
                        "DYNAMIC"
                      ],
                      "description": "Se o código é estático ou dinâmico."
                    },
                    "amountEditable": {
                      "type": "boolean",
                      "description": "`true` quando quem paga escolhe o valor: o código não\ntem valor, ou o recebedor deixou o pagador alterá-lo. O\npagamento exige `amountCents` nesse caso. `false`: o\nvalor é o do código.\n"
                    },
                    "document": {
                      "type": "string",
                      "description": "CPF ou CNPJ do recebedor, **sempre mascarado**\n(`***.556.504-**`); vazio quando o provedor não o\ninforma.\n",
                      "examples": [
                        "***.556.504-**"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Código ilegível, adulterado, vencido ou já pago\n(`INVALID_REQUEST`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de consultas de chave da conta atingido (`RATE_LIMITED`,\no mesmo de `GET /v1/pix/lookup`); `Retry-After` diz quanto esperar.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/pix/brcode/pay": {
      "post": {
        "tags": [
          "PIX"
        ],
        "summary": "Paga um QR Code Pix",
        "description": "Paga um QR Code estático ou dinâmico a partir do copia e cola.\n\n**`Idempotency-Key` é obrigatório**, como em `POST /v1/pix/transfers`:\na mesma chave com o mesmo corpo devolve a resposta original (marcada\ncom `Idempotent-Replay: true`) e o dinheiro sai uma vez só; com corpo\ndiferente, `409`.\n\n**Quem decide o pagamento é o servidor.** Ele lê o código de novo, no\nmomento de pagar e com a conta em uso, e paga pelo identificador dessa\nleitura. O cliente manda o código e, quando o valor é dele, o valor;\nnunca informa o recebedor, o `endToEndId` nem o `qrCodeId`, e a tela de\n`parse` não vale como autorização.\n\n**Valor.**\n\n- Valor fixo (`amountEditable: false` no `parse`): paga o valor do\n  código. `amountCents` é opcional e, se vier, tem de ser igual ao do\n  código; diferente, `422` e nada é enviado.\n- Valor editável ou sem valor (`amountEditable: true`):\n  `amountCents` é obrigatório, inteiro em centavos, maior que zero.\n\nMesmas regras de qualquer Pix: conta ativa, a própria chave recusada,\nlimites (o QR conta no limite do Pix), análise de risco, segundo fator\na partir de R$ 1.000,00 ou quando a soma dos Pix do dia chega a esse\nvalor (`X-Step-Up` com `purpose: transfer`), selfie\nquando a instalação a exige e a alçada de aprovação conjunta da conta\nda empresa. **Na alçada — o valor somado às saídas do dia sem segunda\nassinatura — o pagamento é recusado** (`422`, `APPROVAL_REQUIRED`):\npedir aprovação de QR Code ainda não existe.\n\nComo no Pix por chave, o envio sai no máximo uma vez. Prazo esgotado\nnão é falha: a transação fica `PROCESSING` e o desfecho vem por evento\nou conciliação.\n\n**Retida.** Se o risco reter o pagamento (`HELD_FOR_REVIEW`), nada é\nenviado. Nas instalações cujo provedor paga pelo identificador da\nleitura, o código não é guardado e a retida **não pode ser liberada**\n(`409`): só bloqueada, o que devolve o valor, e o cliente paga de novo.\n\nEm instalações cujo provedor exige uma pessoa na ponta, o pagamento só\nsai por sessão de cliente com aparelho identificado; por chave de API ou\nsessão sem aparelho, a resposta é `503 PROVIDER_ERROR` antes de\nqualquer débito.\n",
        "operationId": "payBRCode",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payload"
                ],
                "properties": {
                  "payload": {
                    "type": "string",
                    "description": "O copia e cola do QR Code.",
                    "examples": [
                      "00020126580014BR.GOV.BCB.PIX0136financeiro@integradora.com.br5204000053039865802BR6014RIO DE JANEIRO62070503***6304ABCD"
                    ]
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Inteiro em centavos. Obrigatório quando o código não tem\nvalor ou é editável; opcional, e igual ao do código, quando\no valor é fixo.\n",
                    "examples": [
                      1990
                    ]
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140,
                    "examples": [
                      "Pagamento por código PIX"
                    ]
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa do serviço que a tela mostrou (`GET /v1/fees/quote`),\nem centavos (funcionalidade `service_fees`). Se a tarifa real\nfor MAIOR, a operação é recusada com `409 CONFLICT` e\n`details.reason: FEE_CHANGED` (`details.feeCents` traz a\nnova, `details.expectedFeeCents` a enviada e\n`details.feeReason` o motivo) antes de reservar qualquer\nvalor; menor ou igual, segue. Essa recusa NÃO fica gravada\nsob a `Idempotency-Key`: refaça a cotação e repita com a\ntarifa nova (a mesma chave vale) e um novo segundo fator (o\nanterior foi gasto). Negativo é `422 INVALID_REQUEST`.\nAusente, a sessão aceita qualquer tarifa; com chave de API e\n`service_fees` ligada ele é obrigatório: sem ele, `422\nINVALID_REQUEST` antes do segundo fator, nada reservado e a\n`Idempotency-Key` livre. Saldo que cobre o valor mas\nnão a tarifa responde `422 INSUFFICIENT_FUNDS` com\n`details.amountCents`, `details.feeCents` e\n`details.totalCents`; com tarifa de entrada ainda pendente na\nconta, a saída tem de deixar saldo para ela, e a recusa traz\n`details.pendingFeesCents` (somado no total). A tarifa cobrada volta no\n`Transaction` (`feeCents`, `feeTransactionId`, `feeReason`,\n`feeStatus`).\n",
                    "examples": [
                      190
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Aceito. A transação tem `kind: PAYMENT_QRCODE`. `status` é\n`PROCESSING` (o normal), `COMPLETED`, `HELD_FOR_REVIEW` ou `FAILED`\n(recusado pelo banco, valor devolvido).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`); conta bloqueada ou chave de API sem o\nescopo `pix:write` (`FORBIDDEN`); pagamento de QR Code desligado\nnesta instalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A mesma chave de idempotência já foi usada com outro conteúdo\n(`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior ainda está em\ncurso (`IDEMPOTENCY_IN_FLIGHT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Saldo insuficiente (`INSUFFICIENT_FUNDS`), limite excedido\n(`LIMIT_EXCEEDED`), valor que somado às saídas do dia alcança a\nalçada da empresa (`APPROVAL_REQUIRED`), chave da própria conta,\ncódigo ilegível ou\nvencido, `amountCents` ausente num código sem valor ou editável, e\n`amountCents` diferente do valor fixo do código\n(`INVALID_REQUEST`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de consultas de chave da conta no envio atingido\n(`RATE_LIMITED`, 60 a cada 10 minutos; a chave que não existe conta\ncomo 3). `Retry-After` diz quanto esperar. Nada foi reservado nem\nenviado; repita com a mesma `Idempotency-Key`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor desta instalação não paga QR Code."
          },
          "503": {
            "description": "Provedor indisponível (`PROVIDER_ERROR`). Antes de qualquer débito\n(canal recusado, leitura do código fora do ar), nada é reservado.\nSe o provedor já tinha aceitado a reserva e o envio comprovadamente\n**não saiu**, a transação fica `FAILED` e **já estornada**, e\naparece assim no extrato: o pagamento de QR Code não é reenviado\ndepois (o código não é guardado), e o cliente paga de novo.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/beneficiaries": {
      "get": {
        "tags": [
          "Beneficiários"
        ],
        "summary": "Contatos salvos",
        "operationId": "listBeneficiaries",
        "responses": {
          "200": {
            "description": "Beneficiários",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Beneficiary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Beneficiários"
        ],
        "summary": "Salva um contato",
        "description": "Por chave (`key`): o nome vem do DICT, nunca do que foi digitado.\n`alias` é prefixado ao nome real, e não o substitui: deixar o apelido\nsubstituir o titular é como um golpista faz \"Maria\" aparecer no lugar\nde uma conta que não é dela.\n\nPor conta bancária (`bankAccount`, para o Pix por agência e conta e a\nTED; exige `pix_manual` ou `ted` ligada): não há DICT, então o nome e o\ndocumento são os digitados e **não são confirmados** pelo banco de\ndestino. Único por conta, banco, agência e conta (`409` no repetido).\nExatamente um entre `key` e `bankAccount`.\n",
        "operationId": "addBeneficiary",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "examples": [
                      "eurico.b@email.com.br"
                    ]
                  },
                  "bankAccount": {
                    "$ref": "#/components/schemas/BankAccountInput"
                  },
                  "alias": {
                    "type": "string",
                    "examples": [
                      "Fornecedor de tecidos"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Salvo. Por chave, a resposta da criação não traz `document` -\nconsulte a lista para obtê-lo. Por conta bancária, traz o item como\na lista o mostra (documento e conta mascarados).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Beneficiary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Conta bloqueada, ou favorecido por conta bancária sem `pix_manual`\nnem `ted` ligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Favorecido repetido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "429": {
            "description": "Limite de consultas de chave da conta atingido (`RATE_LIMITED`,\no mesmo de `GET /v1/pix/lookup`); `Retry-After` diz quanto esperar.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/beneficiaries/{id}": {
      "delete": {
        "tags": [
          "Beneficiários"
        ],
        "summary": "Remove um contato",
        "operationId": "removeBeneficiary",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "examples": {
              "beneficiario": {
                "value": "4e6d80359f2c1a7b"
              }
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removido"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/billets": {
      "get": {
        "tags": [
          "Cobrança"
        ],
        "summary": "Cobranças emitidas",
        "description": "As cobranças desta conta, da mais recente para a mais antiga.\n\n`available: false` significa que o provedor financeiro desta instalação\nnão registra cobrança — há BaaS que paga boleto de terceiro e não emite\nnenhum. A resposta é `200` com a lista vazia, e não um erro: a tela\nprecisa dizer \"esta conta ainda não emite boleto\" em vez de parecer que\no serviço caiu.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listBillets",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Uma ou mais situações, separadas por vírgula. É o filtro que separa\n\"a receber\" de \"recebidos\".\n",
            "schema": {
              "type": "string"
            },
            "examples": {
              "abertos": {
                "value": "REGISTERING,REGISTERED"
              }
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Casa com nome ou documento do pagador, nosso número e descrição.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Cobranças",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Billet"
                      }
                    },
                    "available": {
                      "type": "boolean",
                      "description": "Falso quando o provedor desta instalação não emite boleto."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Cobrança"
        ],
        "summary": "Emite um boleto",
        "description": "Registra o título no arranjo e o devolve pronto para ser entregue ao\npagador.\n\n**A ordem é o arranjo primeiro.** Se o registro for recusado, nada é\ngravado para o cliente: um boleto que existe aqui e não lá é um número\nque o cliente imprime e nenhum caixa do país aceita. (O servidor guarda,\nantes de chamar o arranjo, só a intenção do pedido — a chave, o\nidentificador mandado ao arranjo e um hash —, que não aparece em\nnenhuma rota.)\n\n**`Idempotency-Key` é obrigatório.** Sem ele a chamada responde `400`.\nNão é burocracia: uma resposta perdida no caminho, sem a chave, vira\ndois títulos registrados para a mesma cobrança — e o pagador recebe duas\ncobranças, das quais pode pagar as duas. Repetir o mesmo pedido com a\nmesma chave, inclusive depois de um `503`, devolve o mesmo título; a\nmesma chave com outro pedido é `409` (`IDEMPOTENCY_KEY_REUSED`), mesmo\ndepois de um `5xx`. Enquanto a tentativa anterior ainda pode estar no\narranjo, a repetição é `409` (`IDEMPOTENCY_IN_FLIGHT`): espere e repita\ncom a mesma chave.\n\n**Linha digitável, código de barras, `pixPayload` e PDF só com\n`status` = `REGISTERED`.** O título em `REGISTERING` ainda pode ser\nrecusado pelo arranjo, e volta sem eles.\n\n`payer` não é opcional. A cobrança registrada não aceita título anônimo:\nsem nome e documento o arranjo recusa o registro, e é o CPF ou CNPJ do\npagador que permite ao banco dele exibir o boleto na conta.\n\nOs encargos são conferidos contra os tetos do Código de Defesa do\nConsumidor — 2% de multa e 1% de juros ao mês. O arranjo **não** recusa\num boleto acima disso: ele registra, cobra do pagador, e a devolução\nvolta meses depois contra quem emitiu.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "issueBillet",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BilletRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registrado. `201`, e não `202`: o título existe e — quando o\nregistro confirmou na hora — já é pagável. Confira `status` antes de\nentregar `digitableLine` a alguém.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billet"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Conta bloqueada. Emitir cobrança é direcionar entrada de dinheiro."
          },
          "409": {
            "description": "A chave de idempotência já foi usada com outro pedido\n(`IDEMPOTENCY_KEY_REUSED`), ou a tentativa anterior ainda pode estar\nno arranjo (`IDEMPOTENCY_IN_FLIGHT`; espere e repita com a mesma\nchave). Quando o banco já conhece o pedido e o título pode existir,\na tentativa fica aguardando conferência da equipe: a resposta é a\nmesma, e repetir não a resolve até a equipe concluir. Emitir com\noutra chave pode registrar um segundo boleto.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Pedido que o Lume ou o provedor desta instalação não aceita, com a\nmensagem que diz o que corrigir: desconto, abatimento, pagamento\nparcial, prazo de aceitação acima de 90 dias depois do vencimento,\nnome com mais de 50 caracteres, endereço do pagador incompleto.\n`LIMIT_EXCEEDED` também quando a conta atingiu o teto diário de\nemissão (quantidade de boletos, valor somado ou pagadores\ndistintos no dia): a mensagem diz qual, e a recusa não chama o\narranjo. Como toda resposta, ela fica com a chave: no dia\nseguinte, emita com outra. Repetir um pedido já emitido, com a\nmesma chave, continua devolvendo o título.\nTambém `PROVIDER_ERROR` quando a equipe confirmou que o boleto\ndeste pedido não foi emitido: emita de novo com outra chave.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor desta instalação não registra cobrança."
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/billets/{id}": {
      "get": {
        "tags": [
          "Cobrança"
        ],
        "summary": "Uma cobrança",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getBillet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Cobrança",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billet"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/billets/{id}/cancel": {
      "post": {
        "tags": [
          "Cobrança"
        ],
        "summary": "Dá baixa numa cobrança",
        "description": "Baixa o título no arranjo e depois aqui — nesta ordem, porque baixar só\ndo nosso lado deixaria lá um boleto pagável que a nossa tela diz não\nexistir, e o dinheiro entraria sem ter onde cair.\n\n`POST`, e não `DELETE`: o título **não é apagado**. Ele muda para\n`CANCELED` e continua consultável — um boleto que some da lista é um\nboleto que ninguém consegue explicar depois.\n\nO título vira `CANCELED` quando o arranjo aceita a baixa. Onde a baixa\né assíncrona (a confirmação na registradora vem depois), o servidor a\nacompanha em segundo plano; repetir a baixa de um título ainda aberto,\ndepois de uma falha, manda o mesmo pedido e é inofensivo.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelBillet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Baixado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billet"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O título não aceita mais baixa — já foi pago, já foi baixado, ou\npassou do prazo de aceitação.\n"
          },
          "501": {
            "description": "O provedor desta instalação não registra cobrança."
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/billets/lookup": {
      "post": {
        "tags": [
          "Pagamento de contas"
        ],
        "summary": "Consulta um boleto ou uma conta para pagar",
        "description": "Diz quanto custa pagar este título **agora**, antes de o cliente confirmar:\na partir do vencimento o valor impresso e o valor a pagar deixam de ser a\nmesma coisa (multa, juros e desconto correm por dia).\n\nAceita a **ficha de compensação** (boleto, 47 dígitos na linha digitável\nou 44 no código de barras) e a **guia de arrecadação** (conta de\nconcessionária ou tributo, 48 dígitos na linha ou 44 no código, sempre\ncomeçando por `8`). Os dígitos verificadores de cada bloco são\nconferidos; linha com erro de digitação responde `422`.\n\nSó de sessão, como o resto do pagamento de contas. O identificador que o\nbanco parceiro dá à consulta **nunca** sai desta resposta: quem paga é\nsempre uma nova consulta feita no momento do pagamento.\n\nOs documentos de terceiros (`beneficiaryDocument`,\n`finalBeneficiary.document`) saem **mascarados**.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "lookupBillet",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "line"
                ],
                "properties": {
                  "line": {
                    "type": "string",
                    "description": "A linha digitável (47 ou 48 dígitos) ou o código de barras\n(44), com ou sem pontuação.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O título como o banco o conhece agora.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BilletLookup"
                }
              }
            }
          },
          "400": {
            "description": "O corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Chave de API (esta rota é só de sessão), ou pagamento de contas\ndesligado nesta instalação (`FEATURE_DISABLED`).\n"
          },
          "422": {
            "description": "Linha digitável inválida ou com dígito verificador errado, título\nsem valor a pagar, ou recusa do banco parceiro com mensagem própria\n(por exemplo, \"hoje não é dia útil\").\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/billets/pay": {
      "post": {
        "tags": [
          "Pagamento de contas"
        ],
        "summary": "Paga um boleto ou uma conta",
        "description": "Paga a ficha de compensação ou a guia de arrecadação. Dinheiro sai da\nconta: **`Idempotency-Key` é obrigatório**, como em `POST /v1/pix/transfers`.\nA mesma chave com o mesmo corpo devolve a resposta original (marcada com\n`Idempotent-Replay: true`) e o título é pago uma vez só.\n\n**Quem decide o pagamento é o servidor.** Ele consulta o título de novo, no\nmomento de pagar, e confere o horário, o valor e os limites contra essa\nconsulta. O identificador do banco parceiro não passa pelo cliente.\n\n**Valor.**\n\n- Sem `amountCents`, paga o `totalCents` da consulta. `totalCents` é o\n  valor que a tela mostrou: se o total mudou desde a consulta, `409` com o\n  valor novo, e nada é pago.\n- Com `amountCents`, só para o título que permite alterar o valor\n  (`allowChangeValue: true`) e dentro da faixa `minCents` a `maxCents` da\n  consulta feita **agora**. Fora dela, ou em título que não permite, `422`.\n\n**Horário.** O banco parceiro só aceita pagamento dentro de `cutoff`\n(horário do fuso da operação). Fora dele o pagamento é **recusado** (`422`),\nantes de reservar o valor: nada é adiado para o próximo dia útil.\n\n**Confirmação.** Segundo fator (`X-Step-Up` com `purpose: transfer`) a\npartir de R$ 1.000,00, ou quando a soma dos pagamentos de contas do dia\nchega a esse valor, e, na instalação cujo banco parceiro o exige, em\n**todo** pagamento de contas, qualquer que seja o valor. Selfie quando a\ninstalação a exige, e a alçada de aprovação conjunta da conta da empresa.\nLimite por boleto (`billetSingleCents`), limite diário de pagamento de\ncontas (`billetDailyCents`) e conta ativa.\n\n**Limite diário e janela noturna.** O pagamento não pode fazer a soma do\ndia (virada no fuso da operação) dos pagamentos de contas em andamento ou\nconcluídos passar de `billetDailyCents`; os falhados e estornados não\nentram na soma. A conferência é refeita dentro da reserva, com a conta\ntravada, de modo que pagamentos simultâneos que juntos estourem o limite\ndeixam passar só o que cabe. Das 20h às 6h (fuso da operação) o pagamento\nde contas também respeita o teto noturno (`pixNightCents`), por operação e\npela soma da janela junto com Pix e TED, e o bloqueio noturno das\npreferências de segurança do titular. O pagamento de contas não fica\nretido para análise. Passar de qualquer um responde\n`422 LIMIT_EXCEEDED`.\n\n**Desfecho.** O envio ao banco sai no máximo uma vez. Prazo esgotado,\nresposta perdida e erro 5xx não são falha: a transação fica `PROCESSING` e\no desfecho vem por evento do banco ou por conciliação. `COMPLETED` pode\nsignificar pagamento aceito e liquidado só no dia útil seguinte\n(`billetReceipt.settleDate`). Recusa do banco (`FAILED`) devolve o valor.\n\nO comprovante (autenticação, data de liquidação e convênio) fica em\n`billetReceipt` no `GET /v1/transactions/{id}`. O texto do comprovante do\nbanco, que traz documentos completos, não é guardado nem devolvido.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "payBillet",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "line"
                ],
                "properties": {
                  "line": {
                    "type": "string",
                    "description": "A linha digitável (47 ou 48 dígitos) ou o código de barras (44)."
                  },
                  "totalCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "O total que a consulta mostrou, em centavos. Se o servidor\nachar outro total agora, responde `409` e não paga.\n",
                    "examples": [
                      15000
                    ]
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "O valor escolhido, em centavos, só para título com\n`allowChangeValue: true`. Dentro de `minCents` e `maxCents`.\n",
                    "examples": [
                      4321
                    ]
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa do serviço que a tela mostrou (`GET /v1/fees/quote`),\nem centavos (funcionalidade `service_fees`). Se a tarifa real\nfor MAIOR, a operação é recusada com `409 CONFLICT` e\n`details.reason: FEE_CHANGED` (`details.feeCents` traz a\nnova, `details.expectedFeeCents` a enviada e\n`details.feeReason` o motivo) antes de reservar qualquer\nvalor; menor ou igual, segue. Essa recusa NÃO fica gravada\nsob a `Idempotency-Key`: refaça a cotação e repita com a\ntarifa nova (a mesma chave vale) e um novo segundo fator (o\nanterior foi gasto). Negativo é `422 INVALID_REQUEST`.\nAusente, aceita qualquer tarifa. Saldo que cobre o valor mas\nnão a tarifa responde `422 INSUFFICIENT_FUNDS` com\n`details.amountCents`, `details.feeCents` e\n`details.totalCents`; com tarifa de entrada ainda pendente na\nconta, a saída tem de deixar saldo para ela, e a recusa traz\n`details.pendingFeesCents` (somado no total). A tarifa cobrada volta no\n`Transaction` (`feeCents`, `feeTransactionId`, `feeReason`,\n`feeStatus`).\n",
                    "examples": [
                      190
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Aceito. A transação tem `kind: PAYMENT_BILLET`. `status` é\n`PROCESSING` (o normal), `COMPLETED` ou `FAILED` (recusado pelo banco,\nvalor devolvido).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`); conta bloqueada, chave de API (esta rota é só de\nsessão) (`FORBIDDEN`); pagamento de contas desligado nesta instalação\n(`FEATURE_DISABLED`).\n"
          },
          "409": {
            "description": "O total mudou desde a consulta (`INVALID_REQUEST`, com o valor novo na\nmensagem); a chave de idempotência foi usada com outro corpo\n(`IDEMPOTENCY_KEY_REUSED`); o pedido ainda está em andamento\n(`IDEMPOTENCY_IN_FLIGHT`); ou a conta já está pagando este mesmo\ntítulo e o primeiro pagamento ainda não teve desfecho (`CONFLICT`):\ncom o banco parceiro que não tem idempotência, o primeiro pode ter\nsaído, e pagar de novo com outra chave seria pagar duas vezes.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Linha inválida; valor fora da faixa ou título que não permite alterar o\nvalor (`INVALID_REQUEST`); fora do horário de pagamento\n(`INVALID_REQUEST`); acima do limite por boleto, do limite diário de\npagamento de contas, do teto noturno ou com o bloqueio noturno ligado\n(`LIMIT_EXCEEDED`); valor que, somado às saídas do dia, alcança a\nalçada (`APPROVAL_REQUIRED`); saldo insuficiente\n(`INSUFFICIENT_FUNDS`); recusa do banco parceiro (`PROVIDER_ERROR`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/limits": {
      "get": {
        "tags": [
          "Segurança"
        ],
        "summary": "Limites em vigor",
        "description": "`stepUpThresholdCents` é o valor a partir do qual a transferência exige\n`X-Step-Up`, mesmo com a sessão aberta. `stepUpDailyThresholdCents` é a\nsoma do dia a partir da qual o envio sem `X-Step-Up` também o exige\n(Pix, pagamento de contas e recarga, cada um com a sua soma), ainda que\nabaixo de `stepUpThresholdCents`.\n\n`pixNightNewPeerCents` é o teto noturno para um contato novo: Pix (por\nchave, por agência e conta ou por QR Code), das 20h às 6h, para uma\nchave ou conta que esta conta não pagou **antes do início da janela**\n(um pagamento feito dentro da própria noite não torna o destino\nconhecido). Vale **por operação e pela soma** do que já saiu ou está\nreservado para esse destino na janela. Passar dele responde `422\nLIMIT_EXCEEDED` (mensagem \"para um contato novo\" ou \"para este contato\nnovo\"), antes de qualquer débito, e vale também ao liberar uma\ntransação retida nessa janela. Depois de um pagamento concluído ao\ndestino antes das 20h, vale só o teto noturno comum.\n\n`pixNightCents` é o teto de envio na janela noturna, das 20h às 6h no\nfuso da operação (Resolução BCB nº 142). Vale **por operação e também\npela soma** do que a conta já enviou ou reservou (retidas incluídas) por\nPix (chave e QR Code) **e TED** nessa janela, que atravessa a\nmeia-noite: vale o mais restritivo dos dois. Passar do teto responde\n`422 LIMIT_EXCEEDED`; a recusa pela soma informa o que resta. Vale\ntambém ao liberar uma transação retida: se passar do teto, a liberação\né recusada e a transação segue retida. O bloqueio noturno das\npreferências de segurança vale para Pix, TED e pagamento de contas. A\nsoma e o teto por operação valem também para o pagamento de contas\n(`POST /v1/billets/pay`); a recarga não entra neste teto.\n\n`tedDailyCents` é a soma de TEDs que a conta pode enviar por dia (virada\ndo dia no fuso da operação), aplicada em `POST /v1/ted/transfers`.\n\n`billetSingleCents`, `billetDailyCents` e `cardDailyCents` descrevem\npolíticas já configuráveis no backoffice. `billetSingleCents` vale por\npagamento de boleto ou de conta (`POST /v1/billets/pay`).\n`billetDailyCents` é a soma de pagamentos de contas que a conta pode\nfazer por dia (virada do dia no fuso da operação), aplicada em\n`POST /v1/billets/pay` e na reserva do agendamento feito no banco\nparceiro. As rotas de cartão (`/v1/cards`) são só de sessão. `cardDailyCents` segue no corpo com a\nfuncionalidade `cards` desligada: desligar não apaga o valor da conta, e\nreligar o devolve.\n\nOs limites que o titular ajusta (`items`) seguem as funcionalidades da\ninstalação: os de Pix somem com `pix_transfer` e `pix_qr_pay`\ndesligadas, o `billet_single` com `bill_payment` e `billet_issue`\ndesligadas, o `billet_daily` com `bill_payment` desligada (ele não limita\na emissão) (o de TED, que só o backoffice ajusta, some lá com `ted`\ndesligada). Os campos `pix*Cents`, `tedDailyCents`,\n`billetSingleCents` e `billetDailyCents` continuam no corpo com o valor da conta, como o\n`cardDailyCents`.\n",
        "operationId": "getLimits",
        "responses": {
          "200": {
            "description": "Limites",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pixDailyCents": {
                      "type": "integer",
                      "examples": [
                        500000
                      ]
                    },
                    "pixSingleCents": {
                      "type": "integer",
                      "examples": [
                        200000
                      ]
                    },
                    "pixNightCents": {
                      "type": "integer",
                      "examples": [
                        100000
                      ]
                    },
                    "pixNightNewPeerCents": {
                      "type": "integer",
                      "examples": [
                        10000
                      ]
                    },
                    "tedDailyCents": {
                      "type": "integer",
                      "examples": [
                        2000000
                      ]
                    },
                    "billetSingleCents": {
                      "type": "integer",
                      "examples": [
                        1000000
                      ]
                    },
                    "billetDailyCents": {
                      "type": "integer",
                      "examples": [
                        3000000
                      ]
                    },
                    "cardDailyCents": {
                      "type": "integer",
                      "examples": [
                        300000
                      ]
                    },
                    "stepUpThresholdCents": {
                      "type": "integer",
                      "examples": [
                        100000
                      ]
                    },
                    "stepUpDailyThresholdCents": {
                      "type": "integer",
                      "examples": [
                        100000
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/cards": {
      "get": {
        "tags": [
          "Cartões"
        ],
        "summary": "Cartões da conta",
        "description": "Os cartões da conta, no produto desta instalação (`model`). Só com\nsessão de cliente: **não** aceita chave de API. Leitura: não chama o\nemissor para a análise nem precisa de localização (a lista de termos\nvem do emissor; se ele não responder, vêm os termos já aceitos).\n\n- `CREDIT` (crédito simulado): cada cartão traz `limitCents` e a fatura\n  aberta em `invoice`. Não há `onboarding`.\n- `PREPAID` (pré-pago de emissor externo): a compra debita o saldo da\n  conta; não há limite nem fatura. `onboarding` diz o que falta para\n  pedir um cartão: aceitar o termo (`terms`), passar pela análise do\n  emissor (`eligibility`, que pode levar até 3 dias úteis) e estar\n  abaixo do teto (`maxVirtual`). `canRequestVirtual` resume tudo.\n\nNenhuma resposta traz número completo nem código de segurança. O\npedido ainda não confirmado pelo emissor aparece como `REQUESTED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listCards",
        "responses": {
          "200": {
            "description": "Cartões e situação do produto.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "model",
                    "items",
                    "maxVirtual"
                  ],
                  "properties": {
                    "model": {
                      "type": "string",
                      "enum": [
                        "CREDIT",
                        "PREPAID"
                      ]
                    },
                    "maxVirtual": {
                      "type": "integer",
                      "description": "Teto de cartões virtuais não cancelados."
                    },
                    "designs": {
                      "type": "object",
                      "description": "A arte de cada tipo de cartão, trocada pelo banco."
                    },
                    "onboarding": {
                      "$ref": "#/components/schemas/CardOnboarding"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Card"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Pede um cartão virtual",
        "description": "No `CREDIT`, emite na hora; o corpo é opcional e `Idempotency-Key`\ntambém (com ela, repetir devolve o mesmo cartão). A resposta é o\ncartão, com a fatura zerada.\n\nNo `PREPAID`, **`Idempotency-Key` é obrigatória**: o emissor não tem\nchave de idempotência na emissão, e o pedido nunca é repetido pelo\nservidor. Exige o termo aceito (`CARD_TERMS_REQUIRED`), a análise\naprovada (`CARD_ELIGIBILITY_REQUIRED`) e a conta ativa. A resposta é\n`{card}`, `ACTIVE` quando o emissor confirmou, ou `REQUESTED` quando a\nconfirmação chega depois (ou a resposta dele se perdeu: o pedido não é\nrefeito, e o banco confere). Nesta versão só `VIRTUAL`: `PHYSICAL`\nresponde `CARD_KIND_UNAVAILABLE`.\n\nSó com sessão de cliente. Fica no histórico de segurança\n(`CARD_ISSUED`). Operação: leva `X-Client-Location`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "requestCard",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Obrigatória no `PREPAID`; opcional no `CREDIT`. De 8 a 128 caracteres.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "VIRTUAL",
                      "PHYSICAL"
                    ],
                    "default": "VIRTUAL"
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "Apelido do cartão, sem caractere de controle. Fica só no Lume."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cartão pedido. No `PREPAID`, `{card}`; no `CREDIT`, o cartão\ndireto, com `invoice`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "card"
                      ],
                      "properties": {
                        "card": {
                          "$ref": "#/components/schemas/Card"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Card"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Conta bloqueada (`FORBIDDEN`), ou a funcionalidade desligada (`FEATURE_DISABLED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A mesma `Idempotency-Key` com outro corpo (`IDEMPOTENCY_KEY_REUSED`), ou o pedido anterior ainda em andamento (`IDEMPOTENCY_IN_FLIGHT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Termo não aceito (`CARD_TERMS_REQUIRED`), análise não aprovada\n(`CARD_ELIGIBILITY_REQUIRED`), tipo indisponível\n(`CARD_KIND_UNAVAILABLE`), teto de cartões virtuais ou apelido\ninválido (`INVALID_REQUEST`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/terms/{termId}/accept": {
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Responde ao termo de uso do cartão",
        "description": "Só no `PREPAID`. Registra a resposta do titular ao termo vigente do\nemissor (`termId` de `onboarding.terms`): primeiro no emissor, depois\nno Lume, com o contexto do aparelho (sem a localização em texto).\nRepetir o aceite é sucesso. Recusar (`accept: false`) fica registrado\ne não deixa pedir cartão.\n\nExige a conta ativa. Fica no histórico de segurança (`CARD_TERMS`).\nOperação: leva `X-Client-Location`. Só com sessão de cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "acceptCardTerm",
        "parameters": [
          {
            "name": "termId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "accept"
                ],
                "properties": {
                  "accept": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resposta registrada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "onboarding"
                  ],
                  "properties": {
                    "onboarding": {
                      "$ref": "#/components/schemas/CardOnboarding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Termo que o emissor não tem como vigente (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A instalação não é `PREPAID` (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/onboarding": {
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Inicia a análise para ter cartão",
        "description": "Só no `PREPAID`. Pede ao emissor a análise de elegibilidade (ou\nconsulta de novo a que já começou). Sem o termo aceito responde\n`CARD_TERMS_REQUIRED`. A análise pode levar até 3 dias úteis; o\nresultado chega por aviso do emissor e aparece em `GET /v1/cards`.\n\nExige a conta ativa. Operação: leva `X-Client-Location`. Só com sessão\nde cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "startCardOnboarding",
        "parameters": [
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "responses": {
          "200": {
            "description": "Situação atualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "onboarding"
                  ],
                  "properties": {
                    "onboarding": {
                      "$ref": "#/components/schemas/CardOnboarding"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "A instalação não é `PREPAID` (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Termo de uso não aceito (`CARD_TERMS_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/{id}/block": {
      "patch": {
        "tags": [
          "Cartões"
        ],
        "summary": "Bloqueia ou desbloqueia o cartão",
        "description": "`{blocked: true}` bloqueia sem segundo fator, em qualquer situação da\nconta: é a ação de quem perdeu o cartão. `{blocked: false}` desbloqueia\ne **exige `X-Step-Up`** com `purpose: card` e a conta ativa.\n\nNo `PREPAID` a situação só muda depois de o emissor aceitar; o cartão\nbloqueado pela instituição (`blockedBy: INSTITUTION`) não se\ndesbloqueia por aqui (`CARD_BLOCKED_BY_INSTITUTION`). Cartão cancelado\nresponde `409 CONFLICT`. Cartão de outra conta responde `404`, sem\ndistinguir. Fica no histórico de segurança (`CARD_STATUS`).\nOperação: leva `X-Client-Location`. Só com sessão de cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "setCardBlocked",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Step-Up",
            "in": "header",
            "required": false,
            "description": "Exigido só para desbloquear, com `purpose` card.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "blocked"
                ],
                "properties": {
                  "blocked": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O cartão com a situação nova.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Card"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` para desbloquear (`STEP_UP_REQUIRED`), ou a conta não está ativa (`FORBIDDEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Cartão cancelado (`CONFLICT`), bloqueado pela instituição\n(`CARD_BLOCKED_BY_INSTITUTION`) ou ainda não confirmado pelo\nemissor (`CARD_NOT_ACTIVE`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/{id}/cancel": {
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Cancela o cartão",
        "description": "Cancelamento definitivo, com o motivo: não tem volta. Vale nos dois\nprodutos e em qualquer situação da conta. **Exige `X-Step-Up`** com\n`purpose: card` e `Idempotency-Key`. No `PREPAID` o cancelamento é\nfeito no emissor primeiro; se ele não responder, o cartão fica\nbloqueado (o lado mais restritivo) e a resposta é `503`. Pedido ainda\nnão confirmado pelo emissor responde `CARD_NOT_ACTIVE`. Fica no\nhistórico de segurança (`CARD_STATUS`). Operação: leva\n`X-Client-Location`. Só com sessão de cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelCard",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "USER_REQUEST",
                      "LOST",
                      "STOLEN",
                      "BROKEN"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cartão cancelado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "properties": {
                    "card": {
                      "$ref": "#/components/schemas/Card"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` (`STEP_UP_REQUIRED`), ou a funcionalidade desligada (`FEATURE_DISABLED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Cartão já cancelado (`CONFLICT`), pedido ainda não confirmado (`CARD_NOT_ACTIVE`), ou a mesma `Idempotency-Key` com outro corpo (`IDEMPOTENCY_KEY_REUSED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Motivo fora da lista (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/{id}/secure-view": {
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Abre a página segura com os dados do cartão",
        "description": "Só no `PREPAID` (no `CREDIT` responde `CARD_USE_REVEAL`). Devolve a URL\nde **uso único** da página segura do emissor, onde aparecem número,\nvalidade e código de segurança: esses dados nunca passam pelo Lume.\nAbra a URL em seguida, numa aba nova ou no navegador do app; vencida\n(`expiresIn`, em segundos) ou já aberta, peça outra.\n\nA URL é credencial: a resposta vem com `Cache-Control: no-store`, e o\nLume não a grava, não a registra em log nem a devolve em erro. **Exige\n`X-Step-Up`** com `purpose: card`, a conta ativa e o cartão `ACTIVE`\n(`CARD_NOT_ACTIVE`). Fica no histórico de segurança\n(`CARD_REVEALED`). Operação: leva `X-Client-Location`. Só com sessão\nde cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cardSecureView",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "purpose": {
                    "type": "string",
                    "enum": [
                      "SENSITIVE_DATA"
                    ],
                    "default": "SENSITIVE_DATA"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A URL de uso único.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "url",
                    "expiresIn"
                  ],
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "description": "Validade em segundos."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` (`STEP_UP_REQUIRED`), ou a conta não está ativa (`FORBIDDEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Cartão não ativo (`CARD_NOT_ACTIVE`), cancelado (`CONFLICT`), ou instalação `CREDIT` (`CARD_USE_REVEAL`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Propósito fora da lista (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/{id}/reveal": {
      "post": {
        "tags": [
          "Cartões"
        ],
        "summary": "Mostra o número do cartão de crédito",
        "description": "Só no `CREDIT` (no `PREPAID` responde `CARD_USE_SECURE_VIEW`: use\n`secure-view`). Devolve número, código de segurança e validade,\npedidos ao emissor agora e não gravados. **Exige `X-Step-Up`** com\n`purpose: card`, gasto nesta operação. Fica no histórico de segurança\n(`CARD_REVEALED`). Só com sessão de cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "revealCard",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "responses": {
          "200": {
            "description": "Os dados do cartão.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "number",
                    "cvv",
                    "expiry",
                    "holder"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "number": {
                      "type": "string",
                      "description": "Agrupado de quatro em quatro."
                    },
                    "cvv": {
                      "type": "string"
                    },
                    "expiry": {
                      "type": "string",
                      "examples": [
                        "09/30"
                      ]
                    },
                    "holder": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Cartão cancelado ou sem registro no emissor (`CONFLICT`), ou instalação `PREPAID` (`CARD_USE_SECURE_VIEW`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/cards/{id}/transactions": {
      "get": {
        "tags": [
          "Cartões"
        ],
        "summary": "Compras do cartão",
        "description": "Só no `PREPAID`. As transações do cartão como o emissor avisou, das\nmais recentes, até 50, inclusive as recusadas (`approved: false`, sem\n`transactionId`: não mexeram no saldo). A aprovada que moveu dinheiro\ntraz `transactionId`, a transação do extrato da conta\n(`CARD_PURCHASE`, `CARD_REFUND`; a tarifa de saque é uma `CARD_FEE` à\nparte). Leitura: não precisa de localização. Só com sessão de cliente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listCardTransactions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "As compras do cartão.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CardTransaction"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A instalação não é `PREPAID` (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/account-block": {
      "post": {
        "tags": [
          "Segurança"
        ],
        "summary": "Bloqueia a própria conta",
        "description": "O titular bloqueia a conta quando perde o aparelho ou desconfia de uso\nindevido. Responde `200` com `{status: \"BLOCKED\"}` e **derruba todas as\nsessões do titular, inclusive a que fez o pedido**: depois da resposta o\ntoken deixa de valer, e a tela deve voltar ao login.\n\nSó com sessão de cliente: **não** aceita chave de API. **Exige\n`X-Step-Up`** com `purpose: account_block`: como só o atendimento\ndesbloqueia, sem o código uma sessão roubada trancaria o dono do lado\nde fora. Quem perdeu o aparelho com o autenticador bloqueia pelo\natendimento. A prova é conferida depois do corpo: um pedido inválido\nresponde `422` sem gastá-la. Conta de empresa: o representante\nbloqueia a conta que escolheu (`X-Account-Id`), e a sessão dele também\ncai.\n\nConta ativa ou em abertura (`PENDING`) bloqueia. Conta já `BLOCKED`\nresponde `200` sem efeito novo (quem bloqueou antes e o motivo ficam como\nestão); por isso a rota não usa `Idempotency-Key`. Conta encerrada\nresponde `409`.\n\n**O cliente não desbloqueia.** O desbloqueio é só pelo atendimento do\nbanco. O bloqueio vale no banco parceiro (motivo de pedido do cliente) e,\nse ele estiver fora do ar, fica pendente e é repetido depois: o bloqueio\nno Lume vale na hora.\n\n`note` é texto livre (até 140 caracteres, sem quebra de linha): fica só\nno histórico de segurança do titular (`ACCOUNT_STATUS`), nunca vai ao\nbanco parceiro nem ao log.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "blockOwnAccount",
        "parameters": [
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "LOST_DEVICE",
                      "SUSPECTED_MISUSE",
                      "OTHER"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 140,
                    "description": "Texto livre opcional, sem quebra de linha."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conta bloqueada (ou já estava), e todas as sessões encerradas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "BLOCKED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o `X-Step-Up` com `purpose: account_block`\n(`STEP_UP_REQUIRED`), ou a operação foi tentada com chave de API\n(`FORBIDDEN`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A conta está encerrada, ou mudou de situação durante o pedido (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Motivo fora da lista, ou observação longa demais (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/devices": {
      "get": {
        "tags": [
          "Segurança"
        ],
        "summary": "Aparelhos da pessoa",
        "description": "Os aparelhos em que a pessoa já passou pelo segundo fator, do último\nacesso ao mais antigo. São da pessoa que entrou, e não da conta que ela\nestá operando: quem representa uma empresa vê os próprios aparelhos.\n\n`status` decide o acesso: `ACTIVE` (habilitado), `PENDING` (aguardando\nhabilitação: no modo impor da instalação, não recebe sessão), `REVOKED`\n(desabilitado) ou `BLOCKED` (bloqueado; só o atendimento libera).\n`recognized` é `true` quando `status` é `ACTIVE`, e fica por\ncompatibilidade. `current` marca o aparelho que fez a chamada, pela\nimpressão em `X-Device-Fingerprint`.\n\nOs detalhes (`platform`, `osVersion`, `client`, `clientVersion`,\n`model`, `manufacturer`, `lastIp`) são o retrato do último uso, para a\npessoa reconhecer cada acesso. Vêm do `User-Agent` e dos cabeçalhos\n`X-Device-Model`, `X-Device-Manufacturer` e `X-Device-OS-Version` da\nentrada e da renovação, que o cliente escreve: servem para exibição e\nnão decidem acesso. Vazio é \"não informado\". `lastSeen` muda na entrada\ne em cada renovação; `firstSeen` é a primeira entrada.\n\n`id` é um pseudônimo estável do aparelho, e não a impressão: é ele que\n`DELETE /v1/security/devices/{id}` recebe.\n\nSó com sessão de cliente: **não** aceita chave de API.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listDevices",
        "parameters": [
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          }
        ],
        "responses": {
          "200": {
            "description": "Aparelhos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "label",
                          "status",
                          "recognized",
                          "lastSeen",
                          "firstSeen",
                          "current",
                          "platform",
                          "osVersion",
                          "client",
                          "clientVersion",
                          "model",
                          "manufacturer",
                          "lastIp"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Pseudônimo do aparelho. Não contém a impressão.",
                            "examples": [
                              "dev_3f9a1c07b2e48d615a0c9e27"
                            ]
                          },
                          "label": {
                            "type": "string",
                            "description": "Rótulo pelo `User-Agent` (`iPhone`, `Android`, `Windows`, `Mac`), para exibição.",
                            "examples": [
                              "Android"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "PENDING",
                              "ACTIVE",
                              "REVOKED",
                              "BLOCKED"
                            ]
                          },
                          "recognized": {
                            "type": "boolean"
                          },
                          "lastSeen": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Último uso — a última entrada ou renovação de sessão."
                          },
                          "firstSeen": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Primeira entrada neste aparelho."
                          },
                          "current": {
                            "type": "boolean"
                          },
                          "platform": {
                            "type": "string",
                            "description": "Sistema. Vazio quando o `User-Agent` não permite dizer.",
                            "enum": [
                              "",
                              "Windows",
                              "macOS",
                              "Linux",
                              "ChromeOS",
                              "Android",
                              "iOS",
                              "iPadOS"
                            ],
                            "examples": [
                              "Android"
                            ]
                          },
                          "osVersion": {
                            "type": "string",
                            "description": "Versão do sistema. No Windows, `10` ou `11` pelo\n`X-Device-OS-Version`; só pelo `User-Agent`, o 11 aparece como `10`.\n",
                            "examples": [
                              "14"
                            ]
                          },
                          "client": {
                            "type": "string",
                            "description": "Navegador, ou `App` para o aplicativo.",
                            "enum": [
                              "",
                              "App",
                              "Chrome",
                              "Edge",
                              "Firefox",
                              "Safari",
                              "Opera",
                              "Samsung Internet"
                            ],
                            "examples": [
                              "App"
                            ]
                          },
                          "clientVersion": {
                            "type": "string",
                            "description": "Versão principal do navegador, ou a versão do aplicativo.",
                            "examples": [
                              "1.4.0"
                            ]
                          },
                          "model": {
                            "type": "string",
                            "description": "Modelo informado pelo aplicativo ou pelo navegador no Android.",
                            "examples": [
                              "Galaxy S23 Ultra"
                            ]
                          },
                          "manufacturer": {
                            "type": "string",
                            "examples": [
                              "samsung"
                            ]
                          },
                          "lastIp": {
                            "type": "string",
                            "description": "IP do último uso.",
                            "examples": [
                              "203.0.113.7"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/security/devices/{id}": {
      "delete": {
        "tags": [
          "Segurança"
        ],
        "summary": "Remove um aparelho",
        "description": "Apaga o aparelho e encerra na hora todas as sessões abertas nele. O\naparelho que faz o pedido não pode ser removido (`400`): para isso\nexiste \"Sair\". Aparelho bloqueado também não (`409`): remover apagaria\no bloqueio, e só o atendimento o desfaz. No modo impor da instalação, o aparelho removido que\nvoltar a entrar é tratado como aparelho novo, e não recebe sessão até\nser habilitado. Fica no histórico de segurança (`LOGOUT`).\n\nPede o segundo fator: `X-Step-Up` com `purpose: devices`. Sem ele,\n`403 STEP_UP_REQUIRED`. A prova é conferida depois do `400`, do `404`\ne do `409`, que não a gastam: a tela pode mandar o pedido sem\n`X-Step-Up` e só pedir o código quando a resposta for `403`.\n\nSó com sessão de cliente: **não** aceita chave de API. Aparelho de\noutra pessoa responde `404`, como aparelho inexistente.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "forgetDevice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "O `id` (pseudônimo) devolvido por `GET /v1/security/devices`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          }
        ],
        "responses": {
          "204": {
            "description": "Aparelho removido e sessões dele encerradas"
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`), ou a credencial é chave de API (`FORBIDDEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O aparelho está bloqueado; só o atendimento o libera ou remove (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/preferences": {
      "get": {
        "tags": [
          "Segurança"
        ],
        "summary": "Preferências de segurança do titular",
        "description": "Os interruptores de Segurança, como valem agora: `blockPixAtNight`\n(recusa Pix, TED e pagamento entre 20h e 6h), `notifyNewDevice`\n(e-mail quando um aparelho novo conclui o acesso), `notifyTransactions`\n(intenção guardada; o envio por transação ainda não existe) e\n`birthdayEmail`. Quem nunca mexeu recebe o padrão: avisos ligados,\nbloqueio noturno desligado.\n\n`blockPixAtNightOffAt`, quando vem, é quando o desligamento agendado do\nbloqueio noturno passa a valer (RFC 3339); até lá `blockPixAtNight`\ncontinua `true` e o bloqueio continua valendo.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getSecurityPreferences",
        "responses": {
          "200": {
            "description": "Preferências em vigor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecurityPreferences"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "tags": [
          "Segurança"
        ],
        "summary": "Grava as preferências de segurança",
        "description": "Grava o conjunto inteiro: mande os três interruptores em toda troca.\n`birthdayEmail` ausente mantém o que está.\n\nLigar vale na hora e só pede a sessão. **Desligar pede o segundo\nfator**: `X-Step-Up` com `purpose: preferences`; sem ele,\n`403 STEP_UP_REQUIRED`. E o bloqueio noturno, que muda o que o dinheiro\npode fazer, não desliga na hora nem com a prova: fica **agendado para\n24 horas depois** (`blockPixAtNightOffAt` na resposta), continua\nvalendo até lá e pode ser cancelado por\n`DELETE /v1/security/preferences/pending`. É o mesmo desenho do aumento\nde limite: quem tomou a sessão não desliga a proteção e transfere no\nmesmo minuto.\n\nMandar `blockPixAtNight: true` enquanto há desligamento agendado não o\ncancela nem o adianta — é o que a tela manda ao mexer em outro\ninterruptor, porque o bloqueio ainda está ligado. Para cancelar, use a\nrota de cancelamento.\n\nO que muda o que a conta aceita fica no histórico de segurança\n(`LIMIT_CHANGE`): ligar, agendar e cancelar o bloqueio noturno, e\ndesligar um aviso.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "saveSecurityPreferences",
        "parameters": [
          {
            "$ref": "#/components/parameters/StepUpOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "blockPixAtNight",
                  "notifyNewDevice",
                  "notifyTransactions"
                ],
                "properties": {
                  "blockPixAtNight": {
                    "type": "boolean"
                  },
                  "notifyNewDevice": {
                    "type": "boolean"
                  },
                  "notifyTransactions": {
                    "type": "boolean"
                  },
                  "birthdayEmail": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preferências como ficaram (o bloqueio noturno, como vale agora)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecurityPreferences"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator para desligar uma proteção ou um aviso (`STEP_UP_REQUIRED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/preferences/pending": {
      "delete": {
        "tags": [
          "Segurança"
        ],
        "summary": "Cancela o desligamento agendado do bloqueio noturno",
        "description": "Desfaz o agendamento feito por `PATCH /v1/security/preferences`: o\nbloqueio noturno continua ligado, sem prazo. Só a sessão: cancelar é\nvoltar ao estado mais protegido. Sem agendamento, `404`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelPendingSecurityPreference",
        "responses": {
          "204": {
            "description": "Agendamento cancelado; o bloqueio noturno continua ligado"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/security/biometrics": {
      "get": {
        "tags": [
          "Segurança"
        ],
        "summary": "Situação da biometria facial do titular",
        "description": "Se a instalação oferece biometria, se o titular tem rosto cadastrado e\nacima de que valor a selfie passa a ser exigida. `pendingChangeAt` é\nquando vale a troca agendada (nulo sem troca agendada); vencido o\nprazo, o rosto novo já aparece em `enrolledAt`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getBiometrics",
        "responses": {
          "200": {
            "description": "Situação da biometria",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "boolean"
                    },
                    "simulated": {
                      "type": "boolean"
                    },
                    "enrolled": {
                      "type": "boolean"
                    },
                    "enrolledAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "requiredAboveCents": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "pendingChangeAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Segurança"
        ],
        "summary": "Cadastra ou troca a biometria facial",
        "description": "Envia a selfie que passa a confirmar as operações acima do limite.\nExige `X-Step-Up` com `purpose: biometrics`.\n\n- Sem rosto cadastrado: o código basta, e o rosto vale na hora (`201`).\n- Com rosto cadastrado, a troca na hora exige a prova do rosto atual:\n  o `X-Step-Up` emitido com `selfie` (`201`). Só com o código, a\n  troca fica **agendada para 24 horas depois** (`202`, com\n  `effectiveAt`): até lá vale o rosto cadastrado, e ela pode ser\n  cancelada por `DELETE /v1/security/biometrics/pending`; trocar ou\n  redefinir a senha também a desfaz. Outro pedido enquanto há um\n  agendado: `409`.\n\nO titular recebe um e-mail em toda troca, na hora ou agendada.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "enrollBiometrics",
        "parameters": [
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "selfie"
                ],
                "properties": {
                  "selfie": {
                    "type": "string",
                    "description": "Imagem em base64, com ou sem o prefixo `data:...;base64,`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Rosto cadastrado, valendo agora",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrolled": {
                      "type": "boolean"
                    },
                    "enrolledAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Troca agendada; o rosto cadastrado vale até `effectiveAt`",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrolled": {
                      "type": "boolean"
                    },
                    "pending": {
                      "type": "boolean"
                    },
                    "effectiveAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "409": {
            "description": "Já há uma troca agendada (`CONFLICT`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Biometria indisponível na instalação, imagem inválida ou sem prova de vida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Fornecedor de biometria fora do ar (`PROVIDER_ERROR`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/biometrics/pending": {
      "delete": {
        "tags": [
          "Segurança"
        ],
        "summary": "Cancela a troca agendada da biometria facial",
        "description": "Desfaz a troca agendada por `POST /v1/security/biometrics`: continua\nvalendo o rosto cadastrado. Só a sessão: cancelar é ficar com o rosto\nque já vale. Sem troca agendada, ou com o prazo já vencido, `404`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelPendingBiometricsChange",
        "responses": {
          "204": {
            "description": "Troca cancelada"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/notifications/whatsapp": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Aceite para receber avisos pelo WhatsApp",
        "description": "O aceite do titular para receber avisos da instituição pelo WhatsApp\n(lembrete de vencimento e aviso de atraso), o texto que a tela mostra\nao lado da caixa e se o aceite pode ser dado agora (`available`:\ncanal configurado e um módulo que o use ligado). A caixa nasce **desmarcada**: sem aceite, nenhuma\nmensagem de WhatsApp sai, e os avisos continuam por e-mail.\n\nO aceite vale para o celular do cadastro no momento em que foi dado.\n`phoneChanged: true` diz que o cadastro mudou de número depois: o\naceite não vale para o número novo até ser dado de novo. O celular sai\nmascarado.\n\nSó a sessão do próprio titular: o representante que opera a conta de\numa empresa recebe `403 FORBIDDEN`, e chave de API também.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getWhatsAppConsent",
        "responses": {
          "200": {
            "description": "Aceite em vigor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppConsent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "put": {
        "tags": [
          "Conta"
        ],
        "summary": "Aceita ou deixa de aceitar avisos pelo WhatsApp",
        "description": "`optIn: true` grava o aceite para o celular do cadastro, com a data e a\nversão do texto. `textVersion` é obrigatório e precisa ser o\n`consentTextVersion` em vigor (o texto que a tela mostrou); outro valor\ndevolve `422`. Aceitar de novo com o mesmo número e o mesmo texto não\nmuda a data do aceite.\n\n`optIn: false` sai: nenhuma mensagem de WhatsApp sai a partir daí. Sair\nnunca é recusado, nem com o canal desligado. A data e a versão do\núltimo aceite continuam na resposta, como prova do que foi aceito.\n\nCom o canal desligado nesta instalação, ou sem nenhum módulo que mande\nWhatsApp ligado (hoje, as assinaturas), aceitar devolve `409`\n(`available: false` no `GET`).\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "setWhatsAppConsent",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "optIn"
                ],
                "properties": {
                  "optIn": {
                    "type": "boolean"
                  },
                  "textVersion": {
                    "type": "string",
                    "description": "O `consentTextVersion` do texto mostrado. Obrigatório quando `optIn` é `true`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Aceite como ficou",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppConsent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "O WhatsApp não está disponível nesta instalação (`CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "O texto aceito não é o em vigor, ou o cadastro não tem celular\nválido (`INVALID_REQUEST`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/security/events": {
      "get": {
        "tags": [
          "Segurança"
        ],
        "summary": "Histórico de segurança da conta",
        "description": "Acessos, aparelhos novos, tentativas recusadas e confirmações de segundo\nfator. Até 50 eventos, do mais recente para o mais antigo.\n\nO `kind` diz do que se trata: a trilha de acesso (`LOGIN`, `LOGOUT`,\n`FAILED_LOGIN`, `NEW_DEVICE`, `STEP_UP`, `PASSWORD_CHANGE`), alterações\nde limite (`LIMIT_CHANGE`) e de chave Pix (`PIX_KEY`), mudança de\nsituação da conta (`ACCOUNT_STATUS`), cadastro da biometria facial\n(`BIOMETRICS`), envio do informe de rendimentos por e-mail\n(`INCOME_REPORT_EMAIL`), acesso de um representante à conta de uma\nempresa (`ACCOUNT_ACCESS`), cartões (`CARD_ISSUED`, `CARD_STATUS`,\n`CARD_REVEALED`, `CARD_TERMS`), pedidos de aprovação conjunta (`APPROVAL`) e o código\nde verificação da chave Pix de e-mail ou telefone (`CONTACT_CODE_SENT`,\n`CONTACT_VERIFIED`, `CONTACT_VERIFICATION_LOCKED`) e a abertura ou o\ncancelamento de uma contestação de Pix (`PIX_DISPUTE`, com o id da\ncontestação, nunca o relato) e o acesso recusado porque o aparelho não\nestá habilitado (`DEVICE_DENIED`) e a emissão ou a revogação de uma\nchave de API da conta (`API_KEY`, com o prefixo da chave e os escopos,\nnunca o segredo). O servidor pode\nregistrar tipos novos: ignore o que não conhecer.\n",
        "operationId": "listSecurityEvents",
        "responses": {
          "200": {
            "description": "Eventos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "LOGIN",
                              "LOGOUT",
                              "NEW_DEVICE",
                              "STEP_UP",
                              "PASSWORD_CHANGE",
                              "LIMIT_CHANGE",
                              "FAILED_LOGIN",
                              "PIX_KEY",
                              "ACCOUNT_STATUS",
                              "BIOMETRICS",
                              "INCOME_REPORT_EMAIL",
                              "ACCOUNT_ACCESS",
                              "CARD_ISSUED",
                              "CARD_STATUS",
                              "CARD_REVEALED",
                              "CARD_TERMS",
                              "APPROVAL",
                              "CONTACT_CODE_SENT",
                              "CONTACT_VERIFIED",
                              "CONTACT_VERIFICATION_LOCKED",
                              "PIX_LOOKUP_LIMIT",
                              "SIGNUP_ATTEMPT",
                              "PIX_DISPUTE",
                              "DEVICE_DENIED",
                              "API_KEY",
                              "TRANSACTION_HELD",
                              "WEBHOOK",
                              "SCHEDULE"
                            ]
                          },
                          "detail": {
                            "type": "string"
                          },
                          "device": {
                            "type": "string",
                            "examples": [
                              "Windows"
                            ]
                          },
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/webhooks/{provider}": {
      "post": {
        "tags": [
          "Notificações"
        ],
        "summary": "Recebe uma notificação do provedor financeiro",
        "description": "Chamado pelo provedor, e nunca pela sua integração. Documentado aqui\nporque quem instala o Lume precisa registrar esta URL no painel do\nprovedor e configurar o mesmo segredo dos dois lados.\n\n`{provider}` precisa ser o provedor configurado nesta instalação;\nqualquer outro nome responde `404`. Não há autenticação por sessão nem\npor chave de API: o que autentica é a assinatura da notificação.\n\n## Assinatura\n\nO esquema de assinatura e o formato da resposta são os do provedor\nconfigurado nesta instalação. Os provedores que assinam o corpo usam\nHMAC-SHA256 do corpo exatamente como trafegou, em hexadecimal, no\ncabeçalho de assinatura do provedor. O corpo entra byte a byte —\nreserializar o JSON antes de assinar muda espaços e ordem de campos, e\na assinatura deixa de bater por um motivo que nada tem a ver com\nautenticidade. O esquema da Azify, que é diferente, está na subseção\nabaixo.\n\nSem segredo configurado, toda notificação é recusada. É deliberado:\nausência de configuração não pode virar endereço aberto, porque esta\nrota altera o estado de transações financeiras.\n\n### Provedor `azify`\n\nCada entrega traz três cabeçalhos:\n\n- `x-nonce`: UUID, único por entrega;\n- `x-timestamp`: milissegundos desde a época, como texto;\n- `x-signature`: 64 caracteres hexadecimais.\n\nA assinatura é o HMAC-SHA256, em hexadecimal, de\n`JSON.stringify(corpo) + x-timestamp + x-nonce`, com `x-timestamp` e\n`x-nonce` no valor literal dos cabeçalhos. O Lume recalcula o\n`JSON.stringify` do corpo recebido, então espaços, ordem de chaves\nnuméricas, `1.0` e escapes unicode não derrubam uma entrega autêntica.\nA comparação é em tempo constante.\n\nUma entrega é recusada com `401` quando falta um dos cabeçalhos, o\n`x-timestamp` está a mais de 5 minutos do relógio do servidor (para\nmais ou para menos), a assinatura não confere, ou o `x-nonce` já foi\nusado, ou quando o corpo não é uma entrega (sem nome de evento, ou com\na chave `success` na raiz, que é o formato das respostas do Lume). O\nnonce só é registrado depois de a assinatura ser válida e fica\nlembrado por 10 minutos, entre instâncias e reinícios.\n\nA resposta também é assinada, pelo mesmo algoritmo e com o mesmo\nsegredo, sobre os bytes do corpo devolvido. Sem ela a Azify considera a\nentrega falha e reenvia. Em todas as respostas (`200`, `400`, `401`,\n`413` e `500`):\n\n- `x-webhook-version: webhook-version-2`;\n- `x-nonce` e `x-timestamp` novos, gerados pelo Lume;\n- `x-signature`.\n\nO corpo é `{\"success\":true,\"message\":\"ok\"}` no `200` (inclusive no\nevento repetido) e `{\"success\":false,\"message\":\"...\"}` nas recusas e\nerros. Falha ao gravar o evento responde `500` assinado, para a Azify\ntentar de novo.\n\nO caminho aceita query string sem efeito sobre a validação, o que\npermite registrar uma URL distinta por contexto\n(`/v1/webhooks/azify?canal=...`).\n\n## O que o `200` promete\n\nQue a notificação foi recebida, autenticada e gravada — e só isso. Se\nela foi aplicada é outra pergunta, respondida depois: o que falhar ao\nser aplicado fica pendente e é repetido. Reenviar o mesmo evento é\nseguro e devolve `duplicate` (nos provedores que respondem com o JSON\nabaixo; a Azify devolve o mesmo corpo de sucesso, assinado). Na Azify\na resposta sai antes de o evento ser aplicado.\n",
        "security": [],
        "operationId": "receiveWebhook",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Nome do provedor configurado na instalação.",
            "schema": {
              "type": "string",
              "examples": [
                "celcoin"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "O envelope de evento do provedor, no formato dele.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebida e guardada. `duplicate` quando o evento já tinha chegado —\nresponder `200` de novo é o que faz o provedor parar de reenviar.\n\nProvedor `azify`: corpo `{\"success\":true,\"message\":\"ok\"}`, com os\ncabeçalhos `x-webhook-version`, `x-nonce`, `x-timestamp` e\n`x-signature`, no evento novo e no repetido.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "received",
                        "duplicate"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Corpo ilegível ou em formato inesperado. Na Azify, o corpo é\n`{\"success\":false,\"message\":\"...\"}`, assinado.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Assinatura ausente, inválida, ou segredo não configurado nesta\ninstalação. Na Azify, também: cabeçalho ausente ou malformado,\n`x-timestamp` fora de ±5 minutos e `x-nonce` repetido. O corpo é\n`{\"success\":false,\"message\":\"...\"}`, assinado quando há segredo.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "O nome não corresponde ao provedor desta instalação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Corpo acima de 1 MiB (256 KiB na Azify).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Falha ao gravar a notificação. O provedor deve tentar de novo; na\nAzify, a resposta é assinada.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "O provedor configurado não sabe validar a própria assinatura. Um\nadaptador que não valida não recebe notificação — aceitar sem\nverificar seria pior do que não receber.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/schedules": {
      "get": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Lista os agendamentos da conta",
        "description": "Os agendamentos de PIX e de pagamento de contas da conta, do mais recente\npara o mais antigo (até 200), inclusive os cancelados e os que falharam:\n\"por que não saiu?\" se responde em `failureReason`.\n\nContinua respondendo com `schedules` desligada: o cliente precisa ver o\nagendamento que falhou e desistir do que ainda espera.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listSchedules",
        "responses": {
          "200": {
            "description": "Os agendamentos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "runHour",
                    "maxOpen"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ScheduledPayment"
                      }
                    },
                    "runHour": {
                      "type": "integer",
                      "description": "Hora local a partir da qual o Lume executa os agendamentos\ndele (os que não têm `providerManaged`).\n",
                      "examples": [
                        8
                      ]
                    },
                    "maxOpen": {
                      "type": "integer",
                      "description": "Teto de agendamentos em aberto por conta.",
                      "examples": [
                        50
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Chave de API (esta rota é só de sessão) (`FORBIDDEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/schedules/pix": {
      "post": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Agenda um PIX",
        "description": "Agenda um PIX por chave (`key`) ou por agência e conta (`bankAccount`,\nos mesmos campos do `POST /v1/pix/transfers`; exatamente um dos dois,\nsenão `422 INVALID_REQUEST`) para uma data futura (de amanhã até um\nano). O Lume o executa a partir de `runHour`, e na data o PIX passa por\ntudo o que um PIX de agora passa (saldo, limite, risco, conta ativa e\ntitular da chave). Nada é reservado até a data. **`Idempotency-Key` é\nobrigatório.**\n\nO Pix por agência e conta agendado só existe com as funcionalidades\n`schedules` e `pix_manual` ligadas (`403 FEATURE_DISABLED` sem elas), e\nna data a execução é a do Pix por agência e conta de agora, com as\ntravas do dia (tetos diário e noturno, conta ativa, `pix_manual` ligada,\nalçada, envio idêntico já em andamento). Se alguma recusar, o agendamento\nvira `failed` com o motivo, sem débito; o envio é no máximo uma vez e a\nrotina nunca o repete. Acima da alçada da empresa não se agenda (`422\nAPPROVAL_REQUIRED`; ao agendar vale só o valor da operação, e na data a\nexecução confere a soma daquele dia): não há aprovação conjunta do Pix\npor agência e conta. Nome e documento são os digitados, **não confirmados** pelo banco\nde destino; o documento inteiro fica guardado só até o agendamento sair\nde aberto (executado, falho ou cancelado) e **nunca** volta na resposta.\n\nConfirmação: segundo fator (`X-Step-Up` com `purpose: transfer`) a partir\nde R$ 1.000,00 e selfie quando a instalação a exige. Acima da alçada da\nempresa, agendar é recusado.\n\n**Tarifa.** Com a funcionalidade `service_fees`, o Pix agendado paga a\ntarifa do serviço vigente **no dia da execução** (tabela, cesta do\nplano daquele período e isenções), cobrada junto do envio; nada é\ncobrado ao agendar. Na data, o saldo precisa cobrir valor + tarifa: sem\nisso o agendamento vira `failed`, sem débito. A cotação de\n`GET /v1/fees/quote` no dia de agendar é só informativa.\n\nSó existe quando a funcionalidade `schedules` está ligada.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "schedulePix",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/DeviceFingerprint"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amountCents",
                  "date"
                ],
                "description": "Exatamente um entre `key` e `bankAccount`.",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "A chave PIX do destinatário."
                  },
                  "bankAccount": {
                    "$ref": "#/components/schemas/BankAccountInput"
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "examples": [
                      30000
                    ]
                  },
                  "description": {
                    "type": "string"
                  },
                  "date": {
                    "type": "string",
                    "format": "date",
                    "description": "O dia, `AAAA-MM-DD`, a partir de amanhã."
                  },
                  "expectedRecipientRef": {
                    "type": "string",
                    "description": "O `recipientRef` de `GET /v1/pix/lookup`, só com `key`: se\no titular de agora for outro, `409 CONFLICT` com\n`details.reason` `RECIPIENT_CHANGED` (ou\n`RECIPIENT_UNVERIFIED`), nada agendado, e a recusa não fica\ngravada sob a `Idempotency-Key`. Rota só de sessão: a\ntela web da plataforma sempre o envia quando a consulta o\ndevolve; ausente, não confere.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agendado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledPayment"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`); conta bloqueada; chave de API (`FORBIDDEN`);\nagendamento desligado nesta instalação (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A chave de idempotência foi usada com outro corpo\n(`IDEMPOTENCY_KEY_REUSED`); o pedido ainda está em andamento\n(`IDEMPOTENCY_IN_FLIGHT`); o titular da chave não é o de\n`expectedRecipientRef` (`CONFLICT`, `details.reason`\n`RECIPIENT_CHANGED` ou `RECIPIENT_UNVERIFIED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Data fora de \"amanhã até um ano\", chave inválida, destino bancário\ninválido, `key` e `bankAccount` juntos ou nenhum dos dois, conta de\ndestino que é a própria conta, teto de agendamentos em aberto\n(`INVALID_REQUEST`); acima do limite por transferência\n(`LIMIT_EXCEEDED`); acima da alçada (`APPROVAL_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/schedules/billet": {
      "post": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Agenda o pagamento de um boleto ou de uma conta",
        "description": "Agenda o pagamento de uma ficha de compensação ou de uma guia de\narrecadação para uma data futura (de amanhã até um ano e até o\nvencimento, quando ele existe). **`Idempotency-Key` é obrigatório.**\n\n**Quem paga na data depende da instalação.**\n\n- Com `schedules` ligada, o Lume executa o agendamento a partir de\n  `runHour`, refazendo a consulta e conferindo valor, limite, risco e conta\n  ativa na data. Nada é reservado até lá. Com `service_fees`, a tarifa\n  de pagamento de contas é a vigente **no dia da execução** (tabela,\n  cesta daquele período e isenções), cobrada junto do pagamento; na\n  data o saldo precisa cobrir valor + tarifa, senão o agendamento vira\n  `failed` sem débito. `expectedFeeCents` é ignorado nesse caso.\n- Com só `bill_schedule` ligada (a instalação cujo banco parceiro não\n  aceita que o Lume pague sem ninguém na tela), o agendamento é criado\n  **no banco parceiro**, com você presente, e é o banco quem paga na data\n  (`providerManaged: true`). O valor fica **reservado** na conta desde o\n  dia de agendar: o saldo disponível cai, e o valor volta se o agendamento\n  for cancelado ou falhar. O horário de pagamento do título não se aplica\n  ao agendar. O envio ao banco sai no máximo uma vez, e a mesma chave com o\n  mesmo corpo devolve o mesmo agendamento sem reservar de novo. Com\n  `service_fees`, a tarifa de pagamento de contas é decidida e\n  **reservada junto do valor, no dia de agendar** (tabela e cesta do\n  plano desse dia), porque é quando o dinheiro sai da conta disponível;\n  o cancelamento ou a falha a devolvem com o valor. Mande em\n  `expectedFeeCents` a tarifa de `GET /v1/fees/quote`: maior que ela,\n  `409` com `details.reason: FEE_CHANGED` e nada reservado.\n\nConfirmação: segundo fator (`X-Step-Up` com `purpose: transfer`), exigido\nem todo valor quando o banco parceiro o exige (a Azify), e selfie quando a\ninstalação a exige. Limite por boleto (`billetSingleCents`), conta ativa e\nalçada da empresa também valem ao agendar. No agendamento feito no banco\nparceiro, que reserva o valor na hora, valem também o limite diário de\npagamento de contas (`billetDailyCents`), o teto noturno e o bloqueio\nnoturno, como em `POST /v1/billets/pay`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "scheduleBillet",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpOptional"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "line",
                  "date"
                ],
                "properties": {
                  "line": {
                    "type": "string",
                    "description": "A linha digitável (47 ou 48 dígitos) ou o código de barras (44)."
                  },
                  "totalCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "O total que a consulta mostrou, em centavos. Se o servidor\nachar outro total agora, responde `409` e não agenda.\n"
                  },
                  "date": {
                    "type": "string",
                    "format": "date",
                    "description": "O dia do pagamento, `AAAA-MM-DD`, a partir de amanhã."
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa que a cotação mostrou (`GET /v1/fees/quote`, serviço\n`BILL_PAYMENT`). Conferida só no agendamento feito no banco\nparceiro, que reserva valor e tarifa ao agendar: a tarifa\nreal maior que ela responde `409 FEE_CHANGED`. Ausente aceita\na tarifa vigente. No agendamento que o Lume executa, a\ntarifa é a do dia da execução e o campo é ignorado.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agendado. No agendamento do banco parceiro, `status` é `scheduled` (o\nbanco confirmou) ou `processing` (sem resposta do banco: o valor\nfica reservado e a conciliação confirma).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledPayment"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`); conta bloqueada; chave de API (`FORBIDDEN`);\nnenhum agendamento de contas disponível nesta instalação\n(`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "O total mudou desde a consulta (`INVALID_REQUEST`); a chave foi usada\ncom outro corpo (`IDEMPOTENCY_KEY_REUSED`); o pedido ainda está em\nandamento (`IDEMPOTENCY_IN_FLIGHT`); a conta já tem um pagamento ou\nagendamento deste título sem desfecho (`CONFLICT`); no agendamento\ndo banco parceiro, a tarifa mudou desde a cotação (`CONFLICT` com\n`details.reason: FEE_CHANGED` e `details.feeCents`, nada reservado).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Data fora do prazo, depois do vencimento, linha inválida, teto de\nagendamentos em aberto, ou o banco parceiro não aceitou o agendamento\ne o valor voltou (`INVALID_REQUEST`); repetir com a mesma chave um\nagendamento que não foi aceito também responde `422`; acima do limite\npor boleto, do limite diário de pagamento de contas, do teto noturno\nou com o bloqueio noturno ligado (`LIMIT_EXCEEDED`; os três últimos\nsó no agendamento feito no banco parceiro, que reserva o valor na\nhora); acima da alçada (`APPROVAL_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "O banco parceiro estava indisponível e o pedido comprovadamente não\nsaiu (`PROVIDER_ERROR`): nenhum valor ficou reservado. Repetir com a\nmesma chave responde `422`; para tentar de novo, use outra chave.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/schedules/{id}/cancel": {
      "post": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Cancela um agendamento que ainda espera",
        "description": "Só vale para `status: scheduled`. É POST, e não DELETE, porque o\nagendamento não some: fica no histórico como `canceled`.\n\nNo agendamento do banco parceiro (`providerManaged: true`), o\ncancelamento é feito **no banco** e a reserva volta para a conta, uma vez\nsó. Sem a confirmação do banco (recusa ou queda), a reserva continua e a\nresposta não diz que cancelou. Se o banco já pagou, a reserva é liquidada\ne a resposta é `409`.\n\nAgendamento de outra conta responde `404`, como se não existisse. Não\nexige `Idempotency-Key`: repetir devolve `409`, e nada é devolvido duas\nvezes.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelSchedule",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledPayment"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Chave de API (esta rota é só de sessão) (`FORBIDDEN`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Não existe, ou é de outra conta (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "O agendamento já foi executado, já falhou, já foi cancelado, ou o banco\nparceiro não confirmou o cancelamento (`CONFLICT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "O banco parceiro não respondeu ao cancelamento (`PROVIDER_ERROR`). Não\nhá prova de que cancelou: **a reserva continua** e o agendamento segue\n`scheduled`. Tente de novo mais tarde.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/referrals": {
      "get": {
        "tags": [
          "Indicação"
        ],
        "summary": "Código, link e indicações de quem está na sessão",
        "description": "O código de indicação do titular e quem abriu conta por ele. O código é\ncriado na primeira leitura e não muda depois: 8 caracteres de\n`ABCDEFGHJKMNPQRSTUVWXYZ23456789`.\n\n`path` é o **caminho** do convite no internet banking\n(`/abrir-conta?convite=<code>`), relativo ao domínio de quem publica o\nfront; o servidor não devolve URL absoluta. Quem não tem esse domínio\nmostra só o código, que a abertura de conta aceita como\n`referralCode`.\n\n`rewardCents` é o cashback vigente por conta aberta e aprovada; zero\nquando a instalação não paga. `items` traz todas as indicações, da mais\nrecente para a mais antiga, sem paginação; o nome vem abreviado\n(primeiro nome e inicial do último). `rewardCents` de cada item é o\nque foi pago: zero enquanto não aprovada ou quando a instalação não\npaga. `approved` conta as aprovadas e `earnedCents` soma o que elas\npagaram.\n\nSó com sessão de cliente: **não** aceita chave de API. Com a indicação\n(`referrals`) desligada, `403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getReferrals",
        "responses": {
          "200": {
            "description": "Código e indicações",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "path",
                    "rewardCents",
                    "items",
                    "approved",
                    "earnedCents"
                  ],
                  "properties": {
                    "code": {
                      "type": "string",
                      "examples": [
                        "K7MZQ2PA"
                      ]
                    },
                    "path": {
                      "type": "string",
                      "examples": [
                        "/abrir-conta?convite=K7MZQ2PA"
                      ]
                    },
                    "rewardCents": {
                      "type": "integer",
                      "format": "int64",
                      "minimum": 0,
                      "description": "Cashback vigente por conta aberta e aprovada; zero quando a instalação não paga."
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "status",
                          "rewardCents",
                          "createdAt"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string",
                            "description": "Primeiro nome e inicial do último sobrenome.",
                            "examples": [
                              "Helena B."
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "approved",
                              "rejected"
                            ],
                            "description": "`pending`, em análise; `approved`; `rejected`, cadastro reprovado."
                          },
                          "rewardCents": {
                            "type": "integer",
                            "format": "int64",
                            "minimum": 0,
                            "description": "O que foi pago por esta indicação."
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "resolvedAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Quando a análise decidiu. Ausente enquanto `pending`."
                          }
                        }
                      }
                    },
                    "approved": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "earnedCents": {
                      "type": "integer",
                      "format": "int64",
                      "minimum": 0
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/referrals/network": {
      "get": {
        "tags": [
          "Indicação"
        ],
        "summary": "Resumo da rede de indicação",
        "description": "A rede de quem está na sessão, até dois níveis (`depth: 2`): os\nindicados diretos (que abriram conta pelo link) e os indicados por\neles (indiretos). Diretos vêm por situação da indicação (`pending`,\nem análise; `approved`; `rejected`); indiretos, **só em número**.\n\nNão há contagem de indicados \"ativos\" (nem `activeRule`) nesta\nresposta: com poucos indicados, ela revelaria a situação da conta de\numa pessoa que o cliente vê nomeada na lista. Essa contagem só existe\nno backoffice.\n\n`commissions` soma os ganhos de indicação: `receivedCents` (pagos),\n`toReceiveCents` (a receber; inclui o cashback das indicações em\nanálise, **estimado** pela oferta atual `rewardCents`) e\n`cancelledCents`. O link e o código de indicação estão em\n`GET /v1/referrals`.\n\nNada aqui credita saldo. Só com sessão de cliente: **não** aceita\nchave de API. Com a indicação (`referrals`) ou a rede\n(`affiliate_network`) desligada, `403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getReferralNetwork",
        "responses": {
          "200": {
            "description": "Resumo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "depth",
                    "direct",
                    "indirect",
                    "commissions",
                    "rewardCents"
                  ],
                  "properties": {
                    "depth": {
                      "type": "integer",
                      "examples": [
                        2
                      ]
                    },
                    "direct": {
                      "type": "object",
                      "required": [
                        "total",
                        "pending",
                        "approved",
                        "rejected"
                      ],
                      "properties": {
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "pending": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "approved": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "rejected": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "indirect": {
                      "type": "object",
                      "required": [
                        "total"
                      ],
                      "properties": {
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "commissions": {
                      "type": "object",
                      "required": [
                        "receivedCents",
                        "toReceiveCents",
                        "cancelledCents"
                      ],
                      "properties": {
                        "receivedCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        },
                        "toReceiveCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        },
                        "cancelledCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        }
                      }
                    },
                    "rewardCents": {
                      "type": "integer",
                      "format": "int64",
                      "minimum": 0,
                      "description": "Cashback vigente por conta aberta e aprovada; zero quando a instalação não paga."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/referrals/network/direct": {
      "get": {
        "tags": [
          "Indicação"
        ],
        "summary": "Indicados diretos",
        "description": "Quem abriu conta pelo link de quem está na sessão, da indicação mais\nrecente para a mais antiga. O nome vem abreviado (primeiro nome e\ninicial do último); documento, contato e a situação da conta da pessoa\nnão vêm (quantos estão ativos está no resumo). `indirectCount` é\nquantas pessoas aquele indicado indicou (só o número). `id` é o da\nindicação, não o da pessoa.\n\nPágina por `limit` (1 a 200, padrão 50) e `offset`; fora da faixa,\n`422 INVALID_REQUEST`. Só com sessão de cliente. Com `referrals` ou\n`affiliate_network` desligada, `403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listDirectReferrals",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de indicados diretos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "total",
                    "limit",
                    "offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "status",
                          "indirectCount",
                          "createdAt",
                          "resolvedAt"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string",
                            "examples": [
                              "Rosalina A."
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "approved",
                              "rejected"
                            ]
                          },
                          "indirectCount": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "resolvedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/v1/referrals/commissions": {
      "get": {
        "tags": [
          "Indicação"
        ],
        "summary": "Ganhos de indicação",
        "description": "Os ganhos de indicação de quem está na sessão, do mais recente para o\nmais antigo, e os totais por situação (`totals`, sem o filtro).\n\n`kind` é a origem: `cashback_abertura` (conta aberta e aprovada),\n`produto`, `lucro` ou `tpv`. Hoje só o cashback de abertura gera\nganho; as outras origens estão no contrato e ainda não têm cálculo.\n`status`: `a_receber`, `paga` ou `cancelada`. `estimated: true` marca\no cashback de uma indicação ainda em análise, estimado pela oferta\natual: só vira `paga` se o cadastro for aprovado, e pode não vir.\nIndicação reprovada ou aprovada sem valor não aparece.\n\n`originName` (abreviado) só vem quando quem gerou o ganho é indicado\ndireto. Nada aqui credita saldo: o cashback pago aparece no extrato\ncomo `CASHBACK`.\n\nPágina como em `GET /v1/referrals/network/direct`. Só com sessão de\ncliente. Com `referrals` ou `affiliate_network` desligada,\n`403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listReferralCommissions",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "a_receber",
                "paga",
                "cancelada"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de ganhos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "total",
                    "limit",
                    "offset",
                    "totals"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "kind",
                          "status",
                          "amountCents",
                          "estimated",
                          "createdAt",
                          "resolvedAt"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "cashback_abertura",
                              "produto",
                              "lucro",
                              "tpv"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "a_receber",
                              "paga",
                              "cancelada"
                            ]
                          },
                          "amountCents": {
                            "type": "integer",
                            "format": "int64",
                            "minimum": 0
                          },
                          "estimated": {
                            "type": "boolean"
                          },
                          "originName": {
                            "type": "string",
                            "examples": [
                              "Rosalina A."
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "resolvedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "totals": {
                      "type": "object",
                      "required": [
                        "receivedCents",
                        "toReceiveCents",
                        "cancelledCents"
                      ],
                      "properties": {
                        "receivedCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        },
                        "toReceiveCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        },
                        "cancelledCents": {
                          "type": "integer",
                          "format": "int64",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/InvalidRequest"
          }
        }
      }
    },
    "/v1/merchant/dashboard": {
      "get": {
        "tags": [
          "Estabelecimento"
        ],
        "summary": "Resumo do estabelecimento",
        "description": "Faturamento de hoje e do mês (só vendas aprovadas, `SUCCEEDED`), a\nprogressão de vendas dia a dia desde o dia 1º, a soma dos lançamentos\nfuturos (recebíveis agendados de hoje em diante) e o saldo da conta,\nque é o saldo do estabelecimento. \"Hoje\" e \"mês\" são no fuso da\noperação. `balance` vem nulo quando o saldo não pôde ser lido agora\n(nulo não é zero).\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getMerchantDashboard",
        "responses": {
          "200": {
            "description": "Resumo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantDashboard"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Esta conta não é um estabelecimento (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/merchant/sales": {
      "get": {
        "tags": [
          "Estabelecimento"
        ],
        "summary": "Vendas do estabelecimento",
        "description": "Da mais nova para a mais antiga, 50 por página. Sem `from` e `to`, o\nmês corrente até hoje. O período tem no máximo 93 dias.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listMerchantSales",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Primeiro dia (AAAA-MM-DD), no fuso da operação. Exige `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Último dia, inclusive (AAAA-MM-DD). Exige `from`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Situações separadas por vírgula (`SUCCEEDED,REFUNDED`). Vazio, todas.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de vendas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "total",
                    "page",
                    "pageSize",
                    "from",
                    "to"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantSale"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Esta conta não é um estabelecimento (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Data ilegível, período invertido ou longo demais, ou situação desconhecida (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/merchant/sales/{id}": {
      "get": {
        "tags": [
          "Estabelecimento"
        ],
        "summary": "Detalhe de uma venda",
        "description": "A venda e as parcelas a receber dela. Venda de outro estabelecimento responde como inexistente.",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getMerchantSale",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Venda",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/MerchantSale"
                    },
                    {
                      "type": "object",
                      "required": [
                        "receivables"
                      ],
                      "properties": {
                        "receivables": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MerchantReceivable"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Venda não encontrada, ou a conta não é um estabelecimento (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/merchant/receivables": {
      "get": {
        "tags": [
          "Estabelecimento"
        ],
        "summary": "Agenda de recebíveis",
        "description": "As parcelas a receber, pela data prevista (a mais próxima primeiro), 50\npor página, com os totais do filtro inteiro em `totals`. Sem `from` e\n`to`, de hoje a 90 dias. O período tem no máximo 93 dias.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listMerchantReceivables",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Primeira data prevista (AAAA-MM-DD). Exige `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Última data prevista, inclusive (AAAA-MM-DD). Exige `from`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Situações separadas por vírgula (`SCHEDULED,PAID`). Vazio, todas.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página da agenda",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "total",
                    "page",
                    "pageSize",
                    "from",
                    "to",
                    "totals"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantReceivable"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    },
                    "totals": {
                      "$ref": "#/components/schemas/MerchantReceivableTotals"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Esta conta não é um estabelecimento (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Data ilegível, período invertido ou longo demais, ou situação desconhecida (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription-plans": {
      "get": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Planos à venda para a conta",
        "description": "Os planos ativos para o público da conta (pessoa física vê os planos\n`PF`; empresa, os `PJ`), em ordem de exibição, com o preço em vigor\nhoje (`amountCents`, em centavos), o próximo preço já agendado\n(`scheduledPrice`), quando há, e a composição da cesta de serviços\nvigente hoje (`quotas`: um item por serviço do catálogo; `included`\n0 e `unlimited` falso é \"não incluído\"). Lista vazia é `200`: a tela\ndiz \"nenhum plano disponível\".\n\nSó de sessão: uma chave de API recebe `403 FORBIDDEN`. Com a\nfuncionalidade `subscriptions` desligada, `403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listSubscriptionPlans",
        "responses": {
          "200": {
            "description": "Os planos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubscriptionPlan"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/subscription": {
      "get": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "A assinatura da conta e se ela pode aderir",
        "description": "Devolve a assinatura viva ou, sem ela, a cancelada mais recente (para\na tela mostrar \"cancelado em …\"); `subscription: null` quando a conta\nnunca aderiu.\n\n`canSubscribe` diz se a conta pode aderir agora. Com assinatura viva é\n`false`, sem `blockedReason`. Sem ela, `blockedReason` diz por quê:\n`ACCOUNT_NOT_ACTIVE` (conta em análise, bloqueada ou encerrada),\n`UNCOLLECTED_CHARGES` (mensalidade não cobrada em aberto: falar com o\natendimento) ou `PAID_PERIOD_ACTIVE` (o período pago do plano\ncancelado ainda não terminou; `availableFrom` diz a partir de\nquando). `ACCOUNT_TYPE` (conta de empresa) não é mais emitido: a\nempresa adere aos planos `PJ`; servidores anteriores o mandavam.\n\nCom a funcionalidade `subscriptions` desligada esta rota **não** é\nrecusada: devolve a assinatura viva, se houver (com `canSubscribe:\nfalse`), para o cliente poder cancelá-la; sem assinatura viva, `404`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getSubscription",
        "responses": {
          "200": {
            "description": "A assinatura e a possibilidade de aderir.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "subscription",
                    "canSubscribe",
                    "blockedReason",
                    "availableFrom"
                  ],
                  "properties": {
                    "subscription": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Subscription"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "canSubscribe": {
                      "type": "boolean"
                    },
                    "blockedReason": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "enum": [
                        "ACCOUNT_NOT_ACTIVE",
                        "ACCOUNT_TYPE",
                        "UNCOLLECTED_CHARGES",
                        "PAID_PERIOD_ACTIVE",
                        null
                      ]
                    },
                    "availableFrom": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date",
                      "description": "Só com `PAID_PERIOD_ACTIVE`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Funcionalidade desligada e nenhuma assinatura viva (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Adere a um plano",
        "description": "Cria a assinatura e **debita a primeira mensalidade na hora**, do\nsaldo, numa operação só: sem saldo para o valor inteiro, responde `422\nINSUFFICIENT_FUNDS` e nada é criado. Não existe débito parcial.\n\nO vencimento é o dia da adesão, todo mês (no último dia do mês, nos\nmeses mais curtos). As mensalidades seguintes são debitadas do saldo no\nvencimento, a partir das 6h; sem saldo, o débito é tentado de novo, no\nmáximo uma vez por hora, até 15 dias depois (`retryUntil`). A\nmensalidade aparece no extrato como `TARIFF`, com a descrição\n`Mensalidade · <plano> · MM/AAAA`.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`: repetir a mesma chave com o mesmo corpo devolve a\nresposta original (`Idempotent-Replay: true`) e debita uma vez só.\n`expectedAmountCents` é o valor que a tela mostrou: se o preço em vigor\nfor outro, `409` com `details.reason: PRICE_CHANGED` e\n`details.currentAmountCents`; mostre o valor novo, peça confirmação e\nenvie com **outra** chave. `expectedPlanVersion` é a `planVersion` da\nvitrine (a vigência da composição da cesta mostrada; `null` quando o\nplano não tem composição → mande a string vazia): se a composição\nvigente for outra, `409` com `details.reason: PLAN_CHANGED` e\n`details.currentPlanVersion`. Ausente, a composição não é conferida\n(telas anteriores); a fotografia da adesão guarda a vigente, e também\na tabela de tarifas avulsas em vigor para o público da conta (`fees`,\no que o excedente da cesta paga; nula com `service_fees` desligada).\n\nTrocar de plano não é por aqui: é por `POST\n/v1/subscription/plan-change`, e vale na próxima mensalidade.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "subscribe",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          },
          {
            "$ref": "#/components/parameters/ClientLocation"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "planId",
                  "expectedAmountCents"
                ],
                "properties": {
                  "planId": {
                    "type": "string",
                    "examples": [
                      "pln_8f2c1a"
                    ]
                  },
                  "expectedAmountCents": {
                    "type": "integer",
                    "format": "int64",
                    "description": "O preço mostrado na tela, em centavos. 3992 são R$ 39,92.",
                    "examples": [
                      3992
                    ]
                  },
                  "expectedPlanVersion": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "A `planVersion` da vitrine (AAAA-MM-DD, ou vazia sem composição). Opcional; dada, outra vigente responde `409 PLAN_CHANGED`.",
                    "examples": [
                      "2026-09-05"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Assinatura criada; `currentCharge.status` é `PAID_DEBIT`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Falta `planId`, `expectedAmountCents` ou o cabeçalho `Idempotency-Key` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription`\n(`STEP_UP_REQUIRED`) ou a selfie, acima do limiar de biometria\n(`BIOMETRIC_REQUIRED`); a conta não está ativa (`FORBIDDEN`); a\nfuncionalidade está desligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Plano inexistente, inativo ou de outro público (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason`: `ALREADY_SUBSCRIBED` (a conta já\ntem plano), `PRICE_CHANGED` (com `details.currentAmountCents`),\n`PLAN_CHANGED` (a composição da cesta mudou; com\n`details.currentPlanVersion`), `UNCOLLECTED_CHARGES` (mensalidade\nnão cobrada em aberto) ou `PAID_PERIOD_ACTIVE` (com\n`details.availableFrom`). Ou a chave de\nidempotência já usada com outro corpo ou numa adesão que já foi\ncancelada (`IDEMPOTENCY_KEY_REUSED`), ou a operação anterior ainda\nem curso (`IDEMPOTENCY_IN_FLIGHT`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Saldo insuficiente para a primeira mensalidade\n(`INSUFFICIENT_FUNDS`); conta de empresa (`INVALID_REQUEST` com\n`details.reason: ACCOUNT_TYPE`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription/charges": {
      "get": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Mensalidades da conta",
        "description": "Da competência mais recente para a mais antiga, de todas as assinaturas da conta.",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listSubscriptionCharges",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "De 1 a 36; fora disso, `400`.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 36,
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "As mensalidades.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubscriptionCharge"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/subscription/cancel": {
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Cancela o plano",
        "description": "Cancela a assinatura viva da conta, na hora: as mensalidades em aberto\ndeixam de ser cobradas, e o valor já pago **não é devolvido**. Um plano\nnovo pode ser escolhido a partir do fim do período pago\n(`paidThrough`). O titular recebe um e-mail de aviso.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`. Conta bloqueada pode cancelar. Corpo vazio ou `{}`.\nVale também com a funcionalidade `subscriptions` desligada\n(Decreto 11.034/2022): cancelar não move dinheiro, e o plano vivo\nvoltaria a ser cobrado quando ela fosse religada.\n\nCom o boleto de uma mensalidade em aberto, o cancelamento espera a\nbaixa do título: `409` com `details.reason: BILLET_CANCEL_PENDING`;\ntente de novo mais tarde. Com a funcionalidade desligada e a emissão do\nboleto ainda sem resposta do banco, cancela na hora: o boleto, se sair,\né um depósito na sua própria conta, e a cobrança se resolve quando o\nserviço voltar.\n\nCancelar **não apaga dívida**: a mensalidade não cobrada continua\ndevida, e também a mensalidade cujo boleto já foi pago e ainda não\ndebitada (o pagamento caiu na sua própria conta). Enquanto houver\ndívida, um plano novo é recusado com `UNCOLLECTED_CHARGES`; o\natendimento pode emitir um boleto para quitá-la.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "cancelSubscription",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelada; `status` é `CANCELED`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription` (`STEP_UP_REQUIRED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Não há plano vivo para cancelar (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason: BILLET_CANCEL_PENDING`; ou\n`IDEMPOTENCY_KEY_REUSED` / `IDEMPOTENCY_IN_FLIGHT`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription/plan-change": {
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Pede a troca de plano",
        "description": "Agenda a troca do plano da assinatura viva para outro plano à venda,\nmais caro ou mais barato. A troca **vale na próxima mensalidade**\ngerada (`planChangeFrom` de `GET /v1/subscription` diz quando), sem\ncobrança proporcional e sem débito na hora; até lá, nada muda. A\nmensalidade já gerada (em aberto, inclusive em atraso) continua no\nplano dela. Pedir outro plano antes de a troca valer substitui a troca\nagendada. O titular recebe um e-mail de aviso.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`; com a mensalidade do plano novo (na data em que\nvale) acima do limiar de biometria, o step-up precisa incluir a selfie,\ncomo na adesão (`403 BIOMETRIC_REQUIRED`, que não fica gravado sob a\nchave). `expectedAmountCents` é a mensalidade do plano novo\n**na data em que a troca vale**, como a tela mostrou: se for outra,\n`409` com `details.reason: PRICE_CHANGED`,\n`details.currentAmountCents` e `details.effectiveFrom`; mostre o valor\nnovo, peça confirmação e envie com **outra** chave.\n`expectedPlanVersion` (opcional) é o `planVersion` do plano novo que a\nvitrine mostrou (a vigência da composição da cesta, ou `\"\"` sem\ncomposição), como na adesão: se a cesta mudou, `409` com\n`details.reason: PLAN_CHANGED` e `details.currentPlanVersion`.\n\nRecusada (`409 CONFLICT` com `details.reason`) com a assinatura\ncancelada (`SUBSCRIPTION_CANCELED`), inadimplente ou com mensalidade\nnão cobrada na conta (`UNCOLLECTED_CHARGES`), com a mensalidade da\ntroca agendada já gerada (`ALREADY_GENERATED`: uma nova mudança só\ndepois de ela ser paga), para plano inexistente, inativo ou sem preço\nna data (`PLAN_UNAVAILABLE`), para o plano atual (`SAME_PLAN`) ou para\no já agendado (`ALREADY_SCHEDULED`). A assinatura que mudou durante o\npedido (a mensalidade foi gerada no meio, por exemplo) responde\n`SUBSCRIPTION_CHANGED` e nada é gravado: releia e confirme de novo.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "requestSubscriptionPlanChange",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "planId",
                  "expectedAmountCents"
                ],
                "properties": {
                  "planId": {
                    "type": "string",
                    "maxLength": 64,
                    "examples": [
                      "pln_8f2c1a"
                    ]
                  },
                  "expectedAmountCents": {
                    "type": "integer",
                    "format": "int64",
                    "description": "A mensalidade do plano novo na data em que a troca vale, em centavos. 10392 são R$ 103,92.",
                    "examples": [
                      10392
                    ]
                  },
                  "expectedPlanVersion": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "O `planVersion` do plano novo que a vitrine mostrou (AAAA-MM-DD,\nou `\"\"` quando o plano não tem composição). Ausente não confere.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Troca agendada; `pendingPlan` traz o plano novo, a data e a mensalidade.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Falta `planId`, `expectedAmountCents` ou o cabeçalho `Idempotency-Key` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription`\n(`STEP_UP_REQUIRED`) ou a selfie, com a mensalidade acima do\nlimiar de biometria (`BIOMETRIC_REQUIRED`); a conta não está ativa\n(`FORBIDDEN` com `details.reason: ACCOUNT_NOT_ACTIVE`); a\nfuncionalidade está desligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A conta não tem plano (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason`: `PRICE_CHANGED` (com\n`details.currentAmountCents` e `details.effectiveFrom`),\n`PLAN_CHANGED` (com `details.currentPlanVersion`),\n`SUBSCRIPTION_CANCELED`, `UNCOLLECTED_CHARGES`, `ALREADY_GENERATED`,\n`PLAN_UNAVAILABLE`, `SAME_PLAN`, `ALREADY_SCHEDULED` ou\n`SUBSCRIPTION_CHANGED`. Ou `IDEMPOTENCY_KEY_REUSED` /\n`IDEMPOTENCY_IN_FLIGHT`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription/plan-change/cancel": {
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Desfaz a troca de plano agendada",
        "description": "Desfaz a troca agendada (pedida pelo cliente ou pelo atendimento): o\nplano atual continua valendo. Só enquanto a mensalidade em que a troca\nvale não foi gerada (ela é gerada 10 dias antes do vencimento);\ndepois, `409` com `details.reason: ALREADY_GENERATED`. O titular\nrecebe um e-mail de aviso.\n\nDesfazer muda o que se paga: `expectedAmountCents` é a mensalidade do\nplano atual na data em que a troca valeria, como a tela mostrou\n(`pendingPlan.undoAmountCents` de `GET /v1/subscription`); outra, `409`\ncom `details.reason: PRICE_CHANGED`, `details.currentAmountCents` e\n`details.effectiveFrom`, e o pedido de novo vai com **outra** chave.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`; com a mensalidade do plano atual (para onde se\nvolta) acima do limiar de biometria, também a selfie\n(`403 BIOMETRIC_REQUIRED`, que não fica gravado sob a chave).\n\n**Fora do portão da funcionalidade**, como o cancelamento: com\n`subscriptions` desligada, a conta não ativa ou mensalidade não\ncobrada, desfazer ainda vale quando voltar **não aumenta** a\nmensalidade (o plano atual custa o mesmo ou menos que o da troca\nagendada); quando aumenta, é recusado com o motivo da porta fechada\n(`FEATURE_DISABLED`, `FORBIDDEN` com `ACCOUNT_NOT_ACTIVE` ou `409\nUNCOLLECTED_CHARGES`). `pendingPlan.canUndo` diz se o desfazer é aceito\nagora.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "undoSubscriptionPlanChange",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expectedAmountCents"
                ],
                "properties": {
                  "expectedAmountCents": {
                    "type": "integer",
                    "format": "int64",
                    "description": "A mensalidade do plano atual na data em que a troca valeria, em centavos.",
                    "examples": [
                      10392
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Troca desfeita; `pendingPlan` é `null`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Falta `expectedAmountCents` ou o cabeçalho `Idempotency-Key` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription`\n(`STEP_UP_REQUIRED`) ou a selfie, com a mensalidade acima do\nlimiar de biometria (`BIOMETRIC_REQUIRED`); voltar aumentaria a\nmensalidade com a conta não ativa (`FORBIDDEN` com\n`details.reason: ACCOUNT_NOT_ACTIVE`) ou com a funcionalidade\ndesligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A conta não tem plano (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason`: `PRICE_CHANGED` (com\n`details.currentAmountCents` e `details.effectiveFrom`),\n`NO_PENDING_CHANGE`, `ALREADY_GENERATED`, `SUBSCRIPTION_CANCELED`,\n`UNCOLLECTED_CHARGES` (voltar aumentaria a mensalidade com dívida\nem aberto), `PLAN_UNAVAILABLE` (o plano atual sem preço na data) ou\n`SUBSCRIPTION_CHANGED`. Ou `IDEMPOTENCY_KEY_REUSED` /\n`IDEMPOTENCY_IN_FLIGHT`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marketplace": {
      "get": {
        "tags": [
          "Benefícios"
        ],
        "summary": "Vitrine de benefícios",
        "description": "O que está no ar hoje (data civil no fuso da operação, `today`) para a\nconta da sessão:\n\n- `banners`: na ordem de exibição, até 10 por lugar. `destination`\n  diz o que o toque faz: `nenhum` (só informativo), `interna` (abre a\n  tela da chave `route`) ou `externa` (abre `url`, sempre https, de um\n  domínio que a instituição permitiu). Chaves de `route`: `pix`,\n  `cobrar`, `pagar`, `ted`, `recarga`, `cartoes`, `indique`,\n  `informes`, `plano`, `vitrine`; banner para tela desligada não vem.\n  Ignore a chave que não conhecer.\n  `placements` diz em que telas o banner **aparece** (separado de para\n  onde ele leva): mostre em cada tela só os banners que a trazem. Vem\n  só com os lugares cuja tela está ligada nesta instalação. Sem\n  `placements=all` na consulta, vêm só os banners de `inicio` e\n  `beneficios`, até 10 (o que um cliente que não conhece o campo\n  mostra). `theme`, `backgroundColor` e `textColor` dão as cores do\n  banner (iguais no modo claro e no escuro; nulas, o visual padrão\n  do app), e `imageUrl`, quando há, é o caminho **relativo à API** da\n  imagem (`GET /v1/marketplace/banners/{id}/image`), decorativa, na\n  posição `imagePosition`.\n- `categories`: as ofertas elegíveis agrupadas pela categoria do\n  parceiro, na ordem `farmacia`, `mobilidade`, `saude`, `compras`,\n  `servicos`, `outros` (categoria sem oferta não vem). `couponCode`\n  só nas ofertas `cupom`. `url`, quando há, é https de domínio\n  permitido. `endsOn` é o último dia da oferta. `partner.logoUrl`,\n  quando o parceiro tem logo, é o caminho **relativo à API** da\n  imagem (`GET /v1/marketplace/partners/{id}/logo`); some o endereço\n  da API à frente. Nulo ou ausente: mostre as iniciais do nome.\n- `storeUrl`: o link da loja, ou nulo.\n\nListas vazias são 200. Só com sessão de cliente: **não** aceita chave\nde API.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getMarketplace",
        "parameters": [
          {
            "name": "placements",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all"
              ]
            },
            "description": "`all`: os banners de todos os lugares (até 10 por lugar). Ausente:\nsó os de `inicio` e `beneficios`, até 10 no total.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Vitrine",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Marketplace"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/marketplace/partners/{id}/logo": {
      "get": {
        "tags": [
          "Benefícios"
        ],
        "summary": "Logo do parceiro da vitrine",
        "description": "A imagem do logo de um parceiro, para usar direto num `<img>`. O\ncaminho vem pronto em `partner.logoUrl` de `GET /v1/marketplace`\n(com `?v=`, que muda quando o logo muda); não monte o endereço.\n\n**Sem sessão**: a tag `<img>` do navegador não envia o token de\nacesso, e o logo de um parceiro não é dado pessoal. Continua sujeita\nà funcionalidade `marketplace` (`403 FEATURE_DISABLED` desligada).\n\nSempre `Content-Type: image/png` (o servidor regrava a imagem\nenviada pelo operador em PNG), com `X-Content-Type-Options: nosniff`,\n`Cache-Control: public, max-age=300` e `ETag` (o sha256 do PNG):\ncom `If-None-Match` igual, `304` sem corpo. Parceiro inexistente ou\nsem logo: `404`.\n",
        "security": [],
        "operationId": "getMarketplacePartnerLogo",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "v",
            "in": "query",
            "required": false,
            "description": "Marca da versão do logo, ignorada pelo servidor (só muda o endereço no cache).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "O logo em PNG",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "O `ETag` de `If-None-Match` ainda vale; sem corpo."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/marketplace/banners/{id}/image": {
      "get": {
        "tags": [
          "Benefícios"
        ],
        "summary": "Imagem do banner da vitrine",
        "description": "A imagem de um banner, para usar direto num `<img>`. O caminho vem\npronto em `imageUrl` do banner em `GET /v1/marketplace` (com `?v=`,\nque muda quando a imagem muda); não monte o endereço. A imagem é\ndecorativa: o título do banner já a descreve.\n\n**Sem sessão**, como o logo do parceiro: a tag `<img>` não envia o\ntoken de acesso, e a imagem de um banner não é dado pessoal.\nContinua sujeita à funcionalidade `marketplace` (`403\nFEATURE_DISABLED` desligada).\n\nSempre `Content-Type: image/png` (o servidor regrava a imagem\nenviada pelo operador em PNG), com `X-Content-Type-Options: nosniff`,\n`Cache-Control: public, max-age=300` e `ETag` (o sha256 do PNG):\ncom `If-None-Match` igual, `304` sem corpo. Banner inexistente ou\nsem imagem: `404`. Se a imagem falhar, mostre o banner sem ela.\n",
        "security": [],
        "operationId": "getMarketplaceBannerImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "v",
            "in": "query",
            "required": false,
            "description": "Marca da versão da imagem, ignorada pelo servidor (só muda o endereço no cache).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A imagem em PNG",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "O `ETag` de `If-None-Match` ainda vale; sem corpo."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/fees": {
      "get": {
        "tags": [
          "Tarifas"
        ],
        "summary": "Tabela de tarifas da conta",
        "description": "A tabela vigente para o público da conta (`audience`: `PF` ou\n`PJ`): uma linha por serviço do catálogo, sempre as oito, com o preço\nem vigor hoje (`pricing: FREE` quando não há preço ou ele é zero), a\nisenção por norma quando houver (`exemptByRule`, `exemptionRule`,\n`freeFirstPerMonth`) e o próximo preço já agendado (`scheduled`, ou\nnulo). Sem preço cadastrado nada é cobrado.\n",
        "operationId": "listFees",
        "responses": {
          "200": {
            "description": "A tabela.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeTable"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/fees/quote": {
      "get": {
        "tags": [
          "Tarifas"
        ],
        "summary": "Cotação da tarifa de uma operação",
        "description": "Quanto a conta pagaria de tarifa por executar `service` com\n`amountCents` agora, na mesma ordem da cobrança: isenção por norma,\ncesta do plano (quando a conta tem plano com cota para o serviço),\ntabela, isento. **Não grava nada**: a cota consultada é devolvida. O\ncliente mostra `feeCents` e `totalCents` na confirmação e envia\n`feeCents` como `expectedFeeCents` na operação; se a tarifa real\nfor maior na hora (a cota acabou, o preço virou), a operação responde\n`409 FEE_CHANGED` e a tela refaz a cotação. Chave de API com\n`accounts:read` também cota (e a tabela, `GET /v1/fees`): nas saídas\npor chave com `service_fees` ligada, `expectedFeeCents` é obrigatório.\n",
        "operationId": "quoteFee",
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "PIX_OUT",
                "PIX_IN_QR",
                "TED_OUT",
                "INTERNAL_OUT",
                "BILLET_ISSUE",
                "BILLET_SETTLED",
                "BILL_PAYMENT",
                "TOPUP"
              ]
            }
          },
          {
            "name": "amountCents",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "O valor da operação, em centavos (base do preço percentual)."
          }
        ],
        "responses": {
          "200": {
            "description": "A cotação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeQuote"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Serviço desconhecido ou valor inválido (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approvals/{id}/approve": {
      "post": {
        "tags": [
          "Aprovação conjunta"
        ],
        "summary": "Aprova e executa um pedido da conta PJ",
        "description": "O segundo representante aprova o Pix ou o pagamento de boleto pedido\npor outro, e o servidor executa na hora, pelo mesmo caminho da\noperação imediata. A resposta `200` traz o pedido no desfecho:\nexecutado, executado e retido pela análise de risco, ou falho com o\nmotivo (sem débito).\n\n**Tarifa.** Com a funcionalidade `service_fees`, a execução cobra a\ntarifa do serviço (`PIX_OUT` no Pix, `BILL_PAYMENT` no boleto) vigente\n**na hora da aprovação**, sobre o valor do pedido. Quem aprova cota\nantes (`GET /v1/fees/quote`) e manda o que viu em\n`expectedFeeCents`. Se a tarifa da execução for maior que ela, ou se o\ncampo vier ausente e a tarifa for maior que zero, a resposta é `409`\ncom `details.reason: FEE_CHANGED` e `details.feeCents` (a tarifa\natual): nada é executado, o pedido continua esperando e a aprovação\npode ser refeita com a tarifa nova (e um novo segundo fator). Com a\nfuncionalidade desligada, o campo é ignorado.\n\nExige `X-Step-Up` com `purpose: transfer` e, quando a instalação a\nexige, selfie.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "approveApprovalRequest",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa que a cotação mostrou a quem aprova. Ausente vale\nzero: com tarifa, `409 FEE_CHANGED`.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O pedido no desfecho (executado, retido ou falho).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator (`STEP_UP_REQUIRED`) ou a selfie\n(`BIOMETRIC_REQUIRED`); quem aprova é quem pediu ou não representa\na empresa (`FORBIDDEN`); a funcionalidade está desligada\n(`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O pedido já foi decidido ou expirou (`CONFLICT`); a tarifa da\nexecução é maior que `expectedFeeCents` (`CONFLICT` com\n`details.reason: FEE_CHANGED`, `details.feeCents` e\n`details.expectedFeeCents`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`expectedFeeCents` negativo (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/topups": {
      "get": {
        "tags": [
          "Recarga"
        ],
        "summary": "Recargas recentes da conta",
        "description": "As últimas recargas, para repetir. `available: false` quando a\ninstalação não tem fornecedor de recarga (a tela diz \"ainda não\ndisponível\"); `simulated` diz se o fornecedor é o simulado.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "listTopUps",
        "responses": {
          "200": {
            "description": "As recargas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "available",
                    "simulated"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TopUp"
                      }
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "simulated": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Recarga"
        ],
        "summary": "Recarregar um celular",
        "description": "Debita o valor da recarga (e a tarifa `TOPUP`, quando há) e pede a\nrecarga à operadora. Exige `Idempotency-Key`; a partir do limiar de\nvalor, ou quando a soma das recargas do dia chega a ele, segundo fator\n(`purpose: transfer`). A resposta é `202`: a\nrecarga foi aceita e o desfecho vem pela consulta\n(`GET /v1/topups/{id}`). A transação da reserva está em `transaction`,\ncom a tarifa.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "createTopUp",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "operatorId",
                  "amountCents"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "Número com DDD",
                    "com ou sem pontuação.": null
                  },
                  "operatorId": {
                    "type": "string",
                    "description": "A operadora, de `POST /v1/topups/offers`."
                  },
                  "amountCents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Um dos valores oferecidos pela operadora."
                  },
                  "expectedFeeCents": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "A tarifa `TOPUP` que a tela mostrou (`GET /v1/fees/quote`),\nem centavos. Tarifa real MAIOR → `409 CONFLICT` com\n`details.reason: FEE_CHANGED` (`feeCents`, `expectedFeeCents`,\n`feeReason`), nada reservado e a `Idempotency-Key` livre para\nrepetir com a tarifa nova (novo segundo fator). Negativo é\n`422 INVALID_REQUEST`. Ausente aceita qualquer tarifa. Saldo\nque cobre o valor mas não a tarifa: `422 INSUFFICIENT_FUNDS`\ncom `details.amountCents/feeCents/totalCents` (e\n`details.pendingFeesCents`, tarifa de entrada pendente que a\nsaída tem de deixar no saldo).\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Recarga aceita.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopUp"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key`, ou o corpo é ilegível.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Segundo fator exigido (`STEP_UP_REQUIRED`), conta bloqueada, ou funcionalidade desligada (`FEATURE_DISABLED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Tarifa maior que a esperada (`CONFLICT`, `details.reason: FEE_CHANGED`), ou chave de idempotência reaproveitada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Número, operadora ou valor inválidos (`INVALID_REQUEST`); saldo insuficiente (`INSUFFICIENT_FUNDS`, com `details.amountCents/feeCents/totalCents`); limite excedido (`LIMIT_EXCEEDED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/topups/offers": {
      "post": {
        "tags": [
          "Recarga"
        ],
        "summary": "Operadoras e valores para um número",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "topUpOffers",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "As operadoras que atendem o número, com os valores em centavos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operators": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TopUpOperator"
                      }
                    },
                    "phoneMasked": {
                      "type": "string"
                    },
                    "phoneFormatted": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Número inválido (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/v1/topups/{id}": {
      "get": {
        "tags": [
          "Recarga"
        ],
        "summary": "Situação de uma recarga",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getTopUp",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "O id da recarga (o da transação da reserva)."
          }
        ],
        "responses": {
          "200": {
            "description": "A recarga, com a transação e a tarifa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopUp"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/subscription/usage": {
      "get": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "A cesta de serviços no período corrente",
        "description": "O período pago corrente da assinatura viva (de um vencimento ao\nseguinte) e, por serviço do catálogo, a cota incluída (`included`,\nou `unlimited`), o que já foi usado (`used`) e o que resta\n(`remaining`, nulo no ilimitado). A cota zera a cada período e não\nacumula. `status`: `ACTIVE` (a cesta vale), `SUSPENDED` (cesta\ndesativada: a mensalidade ficou sem pagamento depois da carência;\npaga, volta a valer até o fim do período) ou `NONE` (nenhum período\ncobre hoje; `periodStart`/`periodEnd` nulos e `items` vazio).\n\n`excessPrice` é a tarifa avulsa do excedente: a linha de `GET /v1/fees`\ndo serviço para o público da conta (preço vigente hoje; `FREE` sem\npreço ou isento por norma). `feesPaidInPeriodCents` é o total de\ntarifas avulsas cobradas da conta nos dias do período (as estornadas\nnão contam). Com a funcionalidade `service_fees` desligada,\n`excessPrice` vem nulo e `feesPaidInPeriodCents` zero.\n\nA assinatura cancelada mantém a cesta do período já pago até o fim\ndele, e as operações continuam consumindo a cota: enquanto esse\nperíodo vale, a resposta é `200` com ele e `subscriptionStatus:\nCANCELED`. `404` só quando não há período consumível (sem assinatura,\nou cancelada sem período pago em curso). Só de sessão; com a\nfuncionalidade `subscriptions` desligada, `403 FEATURE_DISABLED`.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "getSubscriptionUsage",
        "responses": {
          "200": {
            "description": "O período corrente e o uso.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionUsage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "A conta não tem período consumível da cesta (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription/billing-day": {
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Pede a troca do dia de cobrança",
        "description": "Agenda o dia de cobrança escolhido pelo titular (`day`, de 1 a 28).\nVale **na próxima mensalidade ainda não gerada**\n(`billingDayChangeFrom` da resposta e de `GET /v1/subscription`):\nessa mensalidade sai **proporcional**, vencendo no dia antigo (V1) e\ncobrindo até a primeira ocorrência do dia novo (V2), por\n`floor(preço × dias(V1, V2) / dias(V1, V1 + 1 mês))`\n(`billingDayChangeAmountCents`), com a cota da cesta proporcional\n(arredondada para cima); a seguinte, em V2, é mensal normal. Nunca há\ncobrança retroativa nem devolução, e nada é debitado agora. **Uma\ntroca por período**; desfazer (`/billing-day/cancel`) libera outra.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`. Fora de 1–28, `422 INVALID_REQUEST`. Recusada\n(`409 CONFLICT` com `details.reason`) com `SAME_DAY` (o dia atual),\n`ALREADY_CHANGED_THIS_PERIOD` (a cobrança proporcional da troca deste\nperíodo já foi gerada), `ALREADY_SCHEDULED` (há troca pendente:\ndesfaça-a antes), `SUBSCRIPTION_CANCELED`, `UNCOLLECTED_CHARGES`\n(assinatura inadimplente, mensalidade no boleto ou não cobrada na\nconta) ou `SUBSCRIPTION_CHANGED` (mudou durante o pedido: releia).\n`canChangeBillingDay` e `billingDayChangeBlockedReason` de\n`GET /v1/subscription` antecipam a resposta.\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "requestSubscriptionBillingDay",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "day"
                ],
                "properties": {
                  "day": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 28,
                    "examples": [
                      10
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Troca agendada; `pendingBillingDay`, `billingDayChangeFrom` e `billingDayChangeAmountCents` dizem o dia, quando e quanto.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Falta `day` ou o cabeçalho `Idempotency-Key` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription`\n(`STEP_UP_REQUIRED`); a conta não está ativa (`FORBIDDEN` com\n`details.reason: ACCOUNT_NOT_ACTIVE`); a funcionalidade está\ndesligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A conta não tem plano (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason`: `SAME_DAY`,\n`ALREADY_CHANGED_THIS_PERIOD`, `ALREADY_SCHEDULED`,\n`SUBSCRIPTION_CANCELED`, `UNCOLLECTED_CHARGES` ou\n`SUBSCRIPTION_CHANGED`. Ou `IDEMPOTENCY_KEY_REUSED` /\n`IDEMPOTENCY_IN_FLIGHT`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Dia fora de 1 a 28 (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscription/billing-day/cancel": {
      "post": {
        "tags": [
          "Assinaturas"
        ],
        "summary": "Desfaz a troca do dia de cobrança",
        "description": "Desfaz a troca agendada enquanto a cobrança proporcional com o dia\nnovo não foi gerada; depois disso, `409` com `details.reason:\nALREADY_GENERATED`. Sem troca agendada, `NO_PENDING_CHANGE`. Desfazer\nlibera uma nova troca no mesmo período.\n\nExige **sempre** `X-Step-Up` com `purpose: subscription` e\n`Idempotency-Key`. Corpo vazio (`{}`).\n",
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "undoSubscriptionBillingDay",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/StepUpRequired"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Troca desfeita; `pendingBillingDay` volta a ser nulo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Falta o cabeçalho `Idempotency-Key` (`INVALID_REQUEST`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Falta o segundo fator com `purpose: subscription`\n(`STEP_UP_REQUIRED`); a conta não está ativa; a funcionalidade\nestá desligada (`FEATURE_DISABLED`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A conta não tem plano (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`CONFLICT` com `details.reason`: `ALREADY_GENERATED`,\n`NO_PENDING_CHANGE`, `SUBSCRIPTION_CANCELED`,\n`UNCOLLECTED_CHARGES` ou `SUBSCRIPTION_CHANGED`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "transaction.updated": {
      "post": {
        "tags": [
          "Notificações"
        ],
        "summary": "Uma transação da conta mudou de estado",
        "description": "Enviado a cada mudança de estado de uma transação da conta, inclusive\na criação: Pix enviado, liquidado, falhou, retido ou devolvido; Pix e\nTED recebidos; QR Code pago; boleto liquidado; tarifa cobrada ou\nestornada; repasse de tarifa recebido ou estornado. O corpo é magro de\npropósito: id, tipo, estado e valor. Nome, documento e chave da\ncontraparte não vão — consulte `GET /v1/transactions/{id}` com a sua\nchave de API, que tem escopo e trilha.\n\n## Assinatura\n\nCada entrega leva quatro cabeçalhos:\n\n- `X-Lume-Event-Id`: o id do evento. O mesmo evento pode chegar mais de\n  uma vez (reenvio após falha, ou dois endereços na mesma conta):\n  **deduplique por ele**.\n- `X-Lume-Delivery-Id`: o id desta tentativa de entrega.\n- `X-Lume-Timestamp`: segundos desde a época, no momento do envio.\n- `X-Lume-Signature`: `v1=` seguido do HMAC-SHA256, em hexadecimal, de\n  `<X-Lume-Timestamp>.<corpo exatamente como trafegou>`, com o segredo\n  que o banco entregou no cadastro (`lume_whsec_…`).\n\nConfira em tempo constante, sobre os bytes crus do corpo (reserializar\no JSON muda espaços e ordem de campos), e recuse carimbo a mais de\n5 minutos do seu relógio.\n\n## mTLS\n\nA conexão é TLS 1.2 ou superior. Quando o banco tem certificado de\ncliente configurado, toda entrega o apresenta: o seu servidor pode\nexigir mTLS e aceitar só esse certificado (peça ao banco a cadeia\npública e a impressão SHA-256). Servidor que não pede certificado não\nrecebe nenhum.\n\nO seu servidor é conferido pelas CAs públicas. Se ele usa certificado\nde uma CA própria, entregue ao banco o certificado público dessa CA\nno cadastro: as entregas passam a aceitar só servidores emitidos por\nela. A CA faz parte do cadastro e passa pela mesma aprovação; trocar\nde CA é cadastrar um endereço novo.\n\n## Resposta e retentativa\n\nResponda qualquer `2xx` em até 10 segundos, sem redirecionar: o corpo\nda resposta é ignorado. Qualquer outra coisa — timeout, `3xx`, `4xx`,\n`5xx`, conexão recusada — agenda nova tentativa, com espera crescente\n(1 min, 5 min, 30 min, 2 h, 6 h, 24 h). Esgotadas as sete tentativas,\na entrega é abandonada e o banco pode reenviá-la a pedido. Enquanto\nisso, `GET /v1/transactions/{id}` continua sendo a fonte de verdade.\n\nConta que não está ativa não recebe webhook. Com a conta bloqueada (ou\nainda em análise), os eventos ficam guardados, sem envio e sem contar\ntentativa, e começam a sair em até 15 minutos depois que a conta volta\na ficar ativa, no ritmo normal da fila. Conta encerrada: os eventos são\nabandonados sem envio.\n\n**Não há ordem garantida** entre os eventos de uma mesma transação:\ncom retentativa, o `PROCESSING` pode chegar depois do `COMPLETED`.\nAntes de mudar o estado do seu lado, consulte\n`GET /v1/transactions/{id}`: ela é a fonte de verdade. O `createdAt`\ndo evento é o momento da mudança e ajuda a descartar um evento velho,\nmas não substitui a consulta.\n\nUm mesmo endereço recebe no máximo duas entregas por vez, em\nsequência; endereços diferentes recebem em paralelo. Um servidor lento\natrasa só a própria fila.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "examples": {
                "pixLiquidado": {
                  "summary": "Pix enviado, liquidado",
                  "value": {
                    "id": "evt_tx_01j9x_completed_8812",
                    "type": "transaction.updated",
                    "createdAt": "2026-10-12T14:03:27Z",
                    "account": {
                      "id": "acc_9f2c1"
                    },
                    "data": {
                      "transactionId": "tx_01j9x",
                      "kind": "TRANSFER_PIX",
                      "status": "COMPLETED",
                      "amountCents": -5000,
                      "createdAt": "2026-10-12T14:03:25Z",
                      "executedAt": "2026-10-12T14:03:27Z"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido. Qualquer `2xx` vale."
          }
        }
      }
    },
    "ping": {
      "post": {
        "tags": [
          "Notificações"
        ],
        "summary": "Evento de teste disparado pelo banco",
        "description": "Enviado pelo banco, a pedido, para confirmar que o seu endereço\nresponde e que a assinatura confere. Mesmos cabeçalhos e mesma\nassinatura de `transaction.updated`; `data` leva só uma mensagem e o\nid do endereço.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Credencial de integração servidor-a-servidor. No sandbox, peça a sua em\n`POST /v1/sandbox/keys`.\n\nA chave carrega escopos: `403` com `code: FORBIDDEN` significa que a\ncredencial é válida e não alcança esta operação — diferente de `401`,\nque significa que ela não vale mais.\n\nA chave de produção pode nascer restrita a endereços de origem (IPs\npúblicos ou redes públicas em CIDR, informados na emissão).\nApresentada de fora deles, a resposta é `403` com `code: FORBIDDEN`,\nmesmo com o segredo certo: é a defesa para a chave que vazou. Cada\nchave tem o seu limite de requisições por minuto (60 por padrão, até\n120); acima dele, `429` com `Retry-After`.\n"
      },
      "session": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Token de acesso de uma sessão interativa, obtido em\n`POST /v1/auth/mfa`. Vale 15 minutos.\n"
      },
      "onboardingToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Token de acompanhamento devolvido por `POST /v1/onboarding` quando o\nprovedor decide o cadastro (`hosted`, ou `own` com o provedor\ndecidindo). Vale uma hora e só nas rotas `/v1/onboarding` do próprio\ntitular (`GET /v1/onboarding`, `POST /v1/onboarding/hosted-link`,\n`PUT /v1/onboarding/profile`, `GET /v1/onboarding/terms`,\n`POST /v1/onboarding/complete` e\n`POST /v1/onboarding/biometrics/session`): não abre sessão. Enviado o\ncadastro, as rotas que o alteram respondem `409`; a leitura do estágio\nsegue valendo até o token vencer.\n"
      },
      "sdkToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Token-Bearer",
        "description": "Credencial do SDK de biometria, devolvida por\n`POST /v1/onboarding/biometrics/session` (o SDK a manda em\n`x-token-bearer`). Opaca, de 15 minutos e 30 chamadas, só em\n`POST /v1/onboarding/biometrics/process-request`.\n"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "De 8 a 128 caracteres, único por operação. Um UUID v4 serve.\n\nGuarde-o antes de enviar a requisição, não depois: a chave só protege se\nsobreviver ao processo que caiu no meio.\n\nA chave é lembrada por 90 dias a partir da primeira requisição. Repetir\ndepois disso conta como operação nova.\n",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128
        },
        "examples": {
          "uuid": {
            "value": "6f1e2a94-7c3d-4b58-9e21-0a8c5d3f7b62"
          }
        }
      },
      "StepUpRequired": {
        "name": "X-Step-Up",
        "in": "header",
        "required": true,
        "description": "Token de `POST /v1/auth/step-up`, com o `purpose` desta operação.",
        "schema": {
          "type": "string"
        },
        "examples": {
          "token": {
            "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        }
      },
      "StepUpOptional": {
        "name": "X-Step-Up",
        "in": "header",
        "required": false,
        "description": "Token de `POST /v1/auth/step-up`. Exigido acima do limite de step-up ou\npara liberar uma transferência retida; ignorado nos demais casos.\n",
        "schema": {
          "type": "string"
        },
        "examples": {
          "token": {
            "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        }
      },
      "ClientLocation": {
        "name": "X-Client-Location",
        "in": "header",
        "required": false,
        "description": "Localização do cliente, `latitude,longitude` em graus decimais, com\nponto e até sete casas. O provedor financeiro a exige em toda chamada\nfeita em nome da pessoa, para o antifraude dele. Ausente ou mal formada\nvale como \"sem localização\": não há `400` por causa deste cabeçalho, mas\na operação que precisa do provedor não sai e volta `PROVIDER_ERROR`\n(HTTP 503) com `details.reason: LOCATION_REQUIRED` — nada foi enviado\nao banco. A leitura que só complementa com o provedor (o saldo e as\nlistas que o atualizam) leva a localização quando o cliente já a tem\nsem perguntar (permissão concedida), e sem ela segue com o dado do\nLume. O servidor a arredonda a 4 casas decimais (cerca de 11 m,\nmetade para longe do zero) antes de usá-la ou repassá-la ao provedor: a\nprecisão acima disso não segue. O Lume guarda só a **última**\nlocalização de uma operação que saiu (não retida), por conta e pessoa\nque operou, arredondada a **1 casa decimal** (cerca de 11 km),\nsubstituída a cada operação e apagada depois de **90 dias** sem\noperação nova ou com a conta encerrada; usa-a apenas para o sinal\n`LOCATION` da análise de risco (pedido a mais de 300 km dela), e ela\nnão aparece em nenhuma resposta. Guarda também, com 4 casas, junto do\npedido que pode ser reenviado ao provedor, a localização da operação\npor até 30 minutos (ou até a conciliação decidir uma tentativa em\nenvio), e só para esse reenvio. No Pix retido para análise (por chave\nou por agência e conta), com o provedor que exige o contexto da pessoa,\nguarda com 4 casas a localização do pedido até a decisão da retida (no\nmáximo 72 h), para repassá-la ao provedor se o operador liberar o Pix.\n",
        "schema": {
          "type": "string",
          "pattern": "^-?[0-9]{1,2}(\\.[0-9]{1,7})?,-?[0-9]{1,3}(\\.[0-9]{1,7})?$"
        },
        "examples": {
          "brasilia": {
            "value": "-15.7939,-47.8828"
          }
        }
      },
      "DeviceFingerprint": {
        "name": "X-Device-Fingerprint",
        "in": "header",
        "required": false,
        "description": "Identifica o aparelho de origem. Entra na avaliação de risco: uma\ntransferência de madrugada, de um aparelho nunca visto, para uma chave\nnunca usada é um padrão diferente da mesma transferência de dia.\n",
        "schema": {
          "type": "string"
        },
        "examples": {
          "impressao": {
            "value": "b7f3c1e9a2d84f60"
          }
        }
      },
      "DeviceModel": {
        "name": "X-Device-Model",
        "in": "header",
        "required": false,
        "description": "Modelo do aparelho, para a lista de aparelhos da pessoa. Só exibição:\nnão decide acesso. Até 64 caracteres; fora de letras, dígitos, espaço e\n`. _ - ( ) ,` é descartado.\n",
        "schema": {
          "type": "string",
          "maxLength": 64
        },
        "examples": {
          "modelo": {
            "value": "Galaxy S23 Ultra"
          }
        }
      },
      "DeviceManufacturer": {
        "name": "X-Device-Manufacturer",
        "in": "header",
        "required": false,
        "description": "Fabricante do aparelho, nas mesmas regras de `X-Device-Model`.",
        "schema": {
          "type": "string",
          "maxLength": 64
        },
        "examples": {
          "fabricante": {
            "value": "samsung"
          }
        }
      },
      "DeviceOSVersion": {
        "name": "X-Device-OS-Version",
        "in": "header",
        "required": false,
        "description": "Versão do sistema, quando o `User-Agent` não a traz ou a traz congelada.\nNo Windows é a versão da plataforma informada pelo navegador\n(`navigator.userAgentData`): de 13 em diante é o Windows 11.\n",
        "schema": {
          "type": "string",
          "maxLength": 64
        },
        "examples": {
          "windows11": {
            "value": "15.0.0"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Credencial ausente, inválida, expirada ou de outro ambiente. No modo\nimpor da instalação, também a sessão cujo aparelho deixou de estar\nhabilitado (`DEVICE_NOT_ENABLED`, com `details.deviceStatus`): a tela\nde bloqueio substitui a volta ao login.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "DeviceNotEnabled": {
        "description": "Senha e código certos, mas o aparelho não está habilitado para a conta\n(`DEVICE_NOT_ENABLED`). Nenhuma sessão foi aberta. `details.deviceStatus`\ndiz por quê: `PENDING` (aguardando habilitação), `REVOKED`\n(desabilitado), `BLOCKED` (bloqueado; só o atendimento libera) ou\n`UNKNOWN` (o login não trouxe identificação do aparelho).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "pendente": {
                "value": {
                  "code": "DEVICE_NOT_ENABLED",
                  "message": "Este aparelho ainda não está habilitado para acessar a sua conta.",
                  "requestId": "07bc304002adf0d4",
                  "details": {
                    "deviceStatus": "PENDING"
                  }
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Autenticado, mas sem permissão para esta operação (`FORBIDDEN`), ou a\nfuncionalidade está desligada nesta instalação (`FEATURE_DISABLED`,\ncom `details.feature`).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "StepUpRequired": {
        "description": "Falta a confirmação de segundo fator (`STEP_UP_REQUIRED`). Chame\n`POST /v1/auth/step-up` e repita com o cabeçalho `X-Step-Up`.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Registro não encontrado",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InvalidRequest": {
        "description": "Requisição malformada ou com dado inválido",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Limite de requisições atingido. O cabeçalho `Retry-After` diz quantos\nsegundos esperar.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyAttempts": {
        "description": "Trava depois de cinco erros em quinze minutos (`TOO_MANY_ATTEMPTS`).\nOs erros são contados no servidor, e não só no processo — reiniciar a\nAPI não os zera —, e `Retry-After` diz quantos segundos faltam.\n\nNas etapas que exigem a posse de um fator (código do autenticador,\nconfirmação adicional, cadastro do autenticador, troca de senha) a\ntrava escalona por identidade: 5 erros, 15 minutos; 10 erros, 1 hora;\n20 erros, 24 horas — cerca de vinte palpites por ciclo de um dia e\nmeio; tentar de outro aparelho não adianta. Nas etapas por\nidentificador público (senha no login, recuperação de senha, login do\noperador) o documento ou o e-mail não trava: trava, e escalona, só a\ncombinação documento + origem (endereço) de quem insiste, para que\nsaber um CPF ou o e-mail de um operador não baste para trancar o dono,\nque entra de outro lugar. Os erros do documento ou do e-mail, somados\nde todas as origens, avisam o titular (ou o operador) por e-mail do\nsegundo degrau em diante. Um acesso concluído com senha e segundo\nfator, ou uma recuperação de senha concluída, zera as travas do\ndocumento; para o operador, também a redefinição da senha ou do\nautenticador por outro administrador.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProviderError": {
        "description": "O arranjo de pagamento está indisponível. Tente novamente.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "WebhookEvent": {
        "type": "object",
        "description": "O corpo de cada webhook que o Lume envia (seção `webhooks`). `data`\ndepende de `type`: em `transaction.updated` é a transação resumida;\nem `ping`, uma mensagem e o id do endereço.\n",
        "required": [
          "id",
          "type",
          "createdAt",
          "account",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do evento. O mesmo de `X-Lume-Event-Id`; deduplique por ele.",
            "examples": [
              "evt_tx_01j9x_completed_8812"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "transaction.updated",
              "ping"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "account": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "examples": [
                  "acc_9f2c1"
                ]
              }
            }
          },
          "data": {
            "oneOf": [
              {
                "type": "object",
                "title": "Transação resumida",
                "required": [
                  "transactionId",
                  "kind",
                  "status",
                  "amountCents",
                  "createdAt"
                ],
                "properties": {
                  "transactionId": {
                    "type": "string"
                  },
                  "kind": {
                    "$ref": "#/components/schemas/TransactionKind",
                    "description": "O mesmo `kind` de `GET /v1/transactions/{id}`: um valor de\n`TransactionKind`. Com a funcionalidade `service_fees`, a\ntarifa cobrada (`TARIFF`) e o seu estorno (`REVERSAL`) chegam\nà conta que pagou; o repasse de tarifa ou de mensalidade\n(`FEE_SHARE_RECEIVED`) e o estorno dele\n(`FEE_SHARE_REVERSED`) chegam à conta de destino do split.\nO evento do repasse não identifica o pagador: leva só id,\ntipo, estado e valor do lançamento do próprio destino.\n"
                  },
                  "status": {
                    "type": "string",
                    "description": "O mesmo `status` de `GET /v1/transactions/{id}`."
                  },
                  "amountCents": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Em centavos, com o mesmo sinal de `GET /v1/transactions/{id}`:\npositivo na entrada (Pix e TED recebidos, devolução de Pix\nrecebida, cobrança recebida, estorno, liberação de bloqueio,\ncashback, repasse de tarifa recebido) e negativo em todos\nos outros tipos (Pix e TED enviados, boleto pago, bloqueio,\ntarifa, estorno de repasse).\n"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "executedAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              {
                "type": "object",
                "title": "Teste",
                "required": [
                  "message",
                  "endpointId"
                ],
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "endpointId": {
                    "type": "string"
                  }
                }
              }
            ]
          }
        }
      },
      "SecurityPreferences": {
        "type": "object",
        "description": "Os interruptores de Segurança, como valem agora.",
        "properties": {
          "blockPixAtNight": {
            "type": "boolean",
            "description": "Em vigor agora; continua `true` enquanto um desligamento agendado não chega."
          },
          "blockPixAtNightOffAt": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o desligamento agendado passa a valer. Ausente sem agendamento."
          },
          "notifyNewDevice": {
            "type": "boolean"
          },
          "notifyTransactions": {
            "type": "boolean"
          },
          "birthdayEmail": {
            "type": "boolean"
          }
        }
      },
      "WhatsAppConsent": {
        "type": "object",
        "description": "O aceite do titular para receber avisos pelo WhatsApp.",
        "required": [
          "available",
          "optedIn",
          "phoneChanged",
          "consentText",
          "consentTextVersion"
        ],
        "properties": {
          "available": {
            "type": "boolean",
            "description": "O aceite pode ser dado agora: o canal está configurado nesta\ninstalação **e** algum módulo que manda WhatsApp está ligado (hoje,\nas assinaturas). Falso, a tela esconde a caixa, e aceitar responde\n`409`. Sair vale sempre.\n"
          },
          "optedIn": {
            "type": "boolean"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "O celular do cadastro, mascarado (\"(21) 9****-4408\"). Nulo sem celular válido."
          },
          "phoneChanged": {
            "type": "boolean",
            "description": "O cadastro mudou de número depois do aceite; o aceite não vale para o número novo."
          },
          "textVersion": {
            "type": [
              "string",
              "null"
            ],
            "description": "A versão do texto do último aceite."
          },
          "optedInAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando foi dado o último aceite."
          },
          "optedOutAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando o titular saiu, se saiu depois do último aceite."
          },
          "consentText": {
            "type": "string",
            "description": "O texto que a tela mostra ao lado da caixa, com o nome da instituição."
          },
          "consentTextVersion": {
            "type": "string",
            "description": "A versão desse texto, que o `PUT` devolve em `textVersion`."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Toda resposta de erro tem esta forma, para que exista um único caminho\nde tratamento. Decida pelo `code`, nunca comparando o texto da\n`message` — ela muda com a revisão de texto e com a tradução.\n\n`FEATURE_DISABLED` (HTTP 403) é a funcionalidade desligada nesta\ninstalação, com `details.feature` dizendo qual (`pix_transfer`,\n`billet_issue`, `billet_issue_pix`…). Não é falta de permissão: repetir\ncom outra credencial não muda a resposta. O que está ligado se consulta\nem `GET /v1/features`.\n\n`BIOMETRIC_REQUIRED` (HTTP 403) é a operação de valor alto confirmada\nsó com o código: a instalação exige também a selfie conferida contra a\nbiometria cadastrada. Emita de novo a prova em `POST /v1/auth/step-up`,\nagora com o campo `selfie`, e repita a operação com o novo\n`X-Step-Up`. A mensagem diz a partir de que valor a regra vale.\n\n`APPROVAL_REQUIRED` (HTTP 422) é a operação que alcança a alçada de\naprovação conjunta da conta da empresa: ela não é executada por quem\npediu. A alçada se confere pela SOMA — o valor da operação mais o que\njá saiu da conta no dia (Pix, boleto, TED, devolução e recarga de\ncelular) sem a segunda assinatura; o que saiu por aprovação conjunta\nnão conta de novo. Por isso um Pix pequeno pode receber esta resposta\ndepois de outras saídas no mesmo dia; a mensagem diz o valor da alçada\ne, quando foi a soma que a alcançou, quanto já saiu. A recarga de\ncelular também recebe esta resposta, e não tem pedido de aprovação.\nRepetir a mesma operação com a mesma `Idempotency-Key` devolve a\ntransação anterior, e não esta recusa — no Pix por chave, no Pix por\nagência e conta, na TED, na devolução de Pix e no pagamento de contas.\n\n`PROVIDER_ERROR` (HTTP 503) com `details.reason: DEVICE_REQUIRED` é a\noperação de uma sessão aberta sem a identificação do aparelho. Vale a\nimpressão (`X-Device-Fingerprint`) enviada no login, gravada na sessão,\ne não a da requisição: mandar o cabeçalho na repetição não resolve. É\npreciso sair e entrar de novo, com o armazenamento do site ou do app\npermitido. Nada foi enviado ao banco. Não é código novo: sem `details.reason`, o\n`PROVIDER_ERROR` continua sendo falha do banco ou do canal.\n\n`PROVIDER_ERROR` (HTTP 503) com `details.reason: LOCATION_REQUIRED` é a\noperação enviada sem a localização do cliente (`X-Client-Location`),\nque o provedor exige. Nada foi enviado ao banco: basta permitir o\nacesso à localização e repetir.\n\n`DEVICE_NOT_ENABLED` é o aparelho que não está habilitado para a conta.\nEm `POST /v1/auth/mfa` e `POST /v1/auth/mfa/enroll` vem com HTTP 403:\nsenha e código estavam certos, e nenhuma sessão foi aberta. Na\nrenovação e nas rotas autenticadas vem com HTTP 401, só no modo impor\nda instalação: a sessão deixou de valer porque o aparelho dela deixou\nde estar habilitado. `details.deviceStatus` é `PENDING`, `REVOKED`,\n`BLOCKED` ou `UNKNOWN`. Repetir não resolve, e entrar de novo pelo\nmesmo aparelho também não: a tela mostra o bloqueio, e não o login.\n",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "INVALID_REQUEST",
              "UNAUTHORIZED",
              "FORBIDDEN",
              "NOT_FOUND",
              "CONFLICT",
              "IDEMPOTENCY_KEY_REUSED",
              "IDEMPOTENCY_IN_FLIGHT",
              "INSUFFICIENT_FUNDS",
              "LIMIT_EXCEEDED",
              "STEP_UP_REQUIRED",
              "BIOMETRIC_REQUIRED",
              "APPROVAL_REQUIRED",
              "RATE_LIMITED",
              "TOO_MANY_ATTEMPTS",
              "PROVIDER_ERROR",
              "FEATURE_DISABLED",
              "DEVICE_NOT_ENABLED",
              "CARD_TERMS_REQUIRED",
              "CARD_ELIGIBILITY_REQUIRED",
              "CARD_KIND_UNAVAILABLE",
              "CARD_BLOCKED_BY_INSTITUTION",
              "CARD_NOT_ACTIVE",
              "CARD_USE_REVEAL",
              "CARD_USE_SECURE_VIEW",
              "INTERNAL_ERROR"
            ]
          },
          "message": {
            "type": "string",
            "description": "Segura para exibir ao usuário final.",
            "examples": [
              "Saldo insuficiente para esta operação."
            ]
          },
          "requestId": {
            "type": "string",
            "description": "Identificador desta requisição, repetido no cabeçalho\n`X-Request-Id`. É o que o suporte pede — ele liga a resposta ao log\ndo servidor.\n"
          },
          "details": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Card": {
        "type": "object",
        "description": "Um cartão da conta, sem número completo nem código de segurança.\n`limitCents` e `invoice` só no `CREDIT`; no `PREPAID` a validade\n(`expiry`) vem vazia: ela aparece só na página segura do emissor.\n",
        "required": [
          "id",
          "kind",
          "status",
          "label",
          "holder",
          "last4",
          "masked",
          "brand",
          "createdAt",
          "statusAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "VIRTUAL",
              "PHYSICAL"
            ]
          },
          "status": {
            "type": "string",
            "description": "`REQUESTED` é o pedido aceito que o emissor ainda não confirmou\n(só no `PREPAID`); `CANCELED` não tem volta.\n",
            "enum": [
              "REQUESTED",
              "ACTIVE",
              "BLOCKED",
              "CANCELED"
            ]
          },
          "label": {
            "type": "string"
          },
          "holder": {
            "type": "string",
            "description": "Nome como sai impresso."
          },
          "last4": {
            "type": "string",
            "description": "Vazio enquanto `REQUESTED`."
          },
          "masked": {
            "type": "string"
          },
          "brand": {
            "type": "string"
          },
          "expiry": {
            "type": "string",
            "description": "MM/AA no `CREDIT`; vazio no `PREPAID`."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "statusAt": {
            "type": "string",
            "format": "date-time"
          },
          "blockedBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "Quem bloqueou. O cliente só desbloqueia o que ele bloqueou.",
            "enum": [
              "CUSTOMER",
              "INSTITUTION",
              null
            ]
          },
          "cancelReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "USER_REQUEST",
              "LOST",
              "STOLEN",
              "BROKEN",
              "INSTITUTION",
              null
            ]
          },
          "limitCents": {
            "type": "integer",
            "description": "Só no `CREDIT`."
          },
          "invoice": {
            "type": "object",
            "description": "Só no `CREDIT`: a fatura aberta (`since`, `totalCents`, `availableCents`, `limitCents`, `entries`)."
          }
        }
      },
      "CardOnboarding": {
        "type": "object",
        "description": "O que falta para pedir um cartão pré-pago. `canRequestVirtual` é o\nresumo: termo aceito, análise `VALIDATED`, conta ativa e abaixo do teto.\n",
        "required": [
          "terms",
          "termsAccepted",
          "eligibility",
          "canRequestVirtual"
        ],
        "properties": {
          "terms": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "version",
                "providerTerms",
                "clientTerms",
                "accepted"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "version": {
                  "type": "string"
                },
                "providerTerms": {
                  "type": "array",
                  "description": "Endereços públicos dos documentos do emissor.",
                  "items": {
                    "type": "string"
                  }
                },
                "clientTerms": {
                  "type": "array",
                  "description": "Endereços públicos dos documentos do banco.",
                  "items": {
                    "type": "string"
                  }
                },
                "accepted": {
                  "type": "boolean"
                }
              }
            }
          },
          "termsAccepted": {
            "type": "boolean"
          },
          "eligibility": {
            "type": "string",
            "enum": [
              "NOT_STARTED",
              "PENDING",
              "IN_PROGRESS",
              "VALIDATED",
              "MANUAL_REVIEW",
              "REJECTED"
            ]
          },
          "eligibilityDetail": {
            "type": [
              "string",
              "null"
            ]
          },
          "canRequestVirtual": {
            "type": "boolean"
          }
        }
      },
      "CardTransaction": {
        "type": "object",
        "description": "Uma transação do cartão pré-pago como o emissor avisou. A recusada\n(`approved: false`) não mexeu no saldo e não tem `transactionId`.\nValores em centavos.\n",
        "required": [
          "id",
          "type",
          "approved",
          "amountCents",
          "feeCents",
          "occurredAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "Tipo do emissor: `PURCHASE`, `WITHDRAWAL`, `EXTRACASH`, `PAYMENT`,\n`REFUND`, `BALANCE_INQUIRY` e as reversões (`REVERSAL_*`). Tipo novo\npode aparecer: trate o desconhecido como informativo.\n"
          },
          "approved": {
            "type": "boolean"
          },
          "statusDetail": {
            "type": "string",
            "description": "Motivo da recusa",
            "quando houver.": null
          },
          "amountCents": {
            "type": "integer"
          },
          "feeCents": {
            "type": "integer",
            "description": "Tarifa de saque",
            "lançada à parte (`CARD_FEE`).": null
          },
          "merchant": {
            "type": "string",
            "maxLength": 60
          },
          "city": {
            "type": "string"
          },
          "entryMode": {
            "type": "string"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "transactionId": {
            "type": [
              "string",
              "null"
            ],
            "description": "A transação do extrato da conta",
            "quando houve lançamento.": null
          }
        }
      },
      "OnboardingDocument": {
        "type": "object",
        "description": "Um documento da abertura própria de empresa, sem o arquivo (o Lume não o guarda).",
        "required": [
          "kind",
          "contentType",
          "sizeBytes",
          "profileSent"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "LEGAL_DOCUMENTS"
            ]
          },
          "contentType": {
            "type": "string",
            "examples": [
              "application/pdf"
            ]
          },
          "sizeBytes": {
            "type": "integer",
            "minimum": 1
          },
          "uploadedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o provedor aceitou o documento."
          },
          "profileSent": {
            "type": "boolean",
            "description": "Se os dados do cadastro já foram aceitos pelo provedor com este documento."
          }
        }
      },
      "OnboardingDocumentUpload": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OnboardingDocument"
          },
          {
            "type": "object",
            "required": [
              "reused"
            ],
            "properties": {
              "reused": {
                "type": "boolean",
                "description": "Verdadeiro quando o mesmo arquivo já tinha sido aceito e nada foi ao provedor."
              }
            }
          }
        ]
      },
      "Features": {
        "type": "object",
        "required": [
          "features",
          "accountOpening"
        ],
        "properties": {
          "features": {
            "type": "array",
            "description": "As funcionalidades ligadas, na ordem do catálogo. A ausência de uma\nchave é a funcionalidade desligada. Chave nova pode aparecer sem\naviso: ignore a que não conhecer.\n\n| Chave | Funcionalidade |\n| --- | --- |\n| `account_opening` | Abertura de conta PF e PJ |\n| `business_opening_own` | Abertura de conta de empresa MEI e EI na tela própria, com o documento enviado ao provedor. Em construção, desligada por padrão. Só existe com `account_opening` ligada |\n| `face_biometrics` | Biometria facial (selfie na abertura e confirmação de valor alto) |\n| `pix_transfer` | PIX por chave |\n| `pix_manual` | PIX por agência e conta |\n| `pix_keys` | Chaves PIX |\n| `pix_key_claims` | Portabilidade e reivindicação de chave PIX pelo titular: trazer chave de outra instituição e responder ao pedido de outra instituição. Em construção, desligada por padrão. Só existe com `pix_keys` ligada |\n| `pix_qr` | QR estático e copia e cola |\n| `pix_qr_pay` | Pagamento de QR Code Pix (copia e cola e câmera) |\n| `schedules` | Agendamento de PIX e de pagamento de boleto. Desligada na instalação cujo provedor não aceita executar na data, sem ninguém na tela (a Azify, por ora) |\n| `bill_schedule` | Agendamento de pagamento de contas no banco parceiro, que paga na data com o valor reservado. Existe onde `schedules` está desligada e o banco parceiro agenda por conta própria. Desligada na instalação Azify por decisão: bloquear ou encerrar a conta já cancela lá os agendamentos dela, mas o banco parceiro ainda não aceita esse cancelamento sem ninguém na tela |\n| `billet_issue` | Emissão de boleto |\n| `billet_issue_pix` | Boleto com PIX |\n| `bill_payment` | Pagamento de contas |\n| `approvals` | Aprovação conjunta da conta PJ |\n| `cards` | Cartões |\n| `topup` | Recarga de celular |\n| `crypto` | Cripto |\n| `pool` | Conta bolsão |\n| `internal_transfer` | Transferência interna entre contas |\n| `referrals` | Indicação |\n| `income_reports` | Informe de rendimentos |\n| `subscriptions` | Planos e assinaturas: o cliente escolhe um plano mensal, pago com o saldo da conta. Em construção. Só existe onde há transferência interna (`internal_transfer`): desligada na instalação Azify |\n| `merchant_portal` | Portal do estabelecimento: vendas e recebíveis de quem recebe por maquininha. Em construção |\n| `affiliate_network` | Rede de indicação: indicados diretos e indiretos e comissões. Em construção. Só existe com `referrals` ligada |\n| `marketplace` | Benefícios e vitrine: cesta de benefícios do plano, banners e parceiros. Em construção |\n| `service_fees` | Tarifas por serviço: tabela de preços por operação (Pix, TED, boleto, pagamento, recarga), cobrada junto da operação. Em construção. Só existe onde há transferência interna (`internal_transfer`): desligada na instalação Azify |\n",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "pix_transfer",
                "pix_keys",
                "pix_qr",
                "billet_issue",
                "bill_payment"
              ]
            ]
          },
          "accountOpening": {
            "type": "object",
            "required": [
              "enabled",
              "mode"
            ],
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "Se a instalação abre conta. O mesmo que `account_opening` em `features`."
              },
              "mode": {
                "type": "string",
                "examples": [
                  "own",
                  "hosted"
                ],
                "description": "Quem conduz a abertura. `own` é a tela do próprio Lume, por\n`POST /v1/onboarding` (com o provedor que decide o cadastro, os\ndados seguem por `PUT /v1/onboarding/profile` e a conclusão; a\nresposta da abertura traz `onboardingToken` em vez de\n`account`). `hosted` é o provedor financeiro: o\nmesmo `POST /v1/onboarding` cria o cadastro e devolve a tela\nde cadastro do provedor (`url`), para onde a pessoa é levada.\nOutros modos podem surgir; trate o desconhecido como abertura\nindisponível.\n"
              },
              "emailConfirmation": {
                "type": "boolean",
                "description": "Se a abertura pela porta pública exige o código do e-mail\n(`emailVerificationId`). Falso só em instalação de teste com a\nconfirmação desligada (PROVISÓRIO): a tela pula o código.\nAusente é verdadeiro.\n"
              }
            }
          },
          "institution": {
            "type": "object",
            "required": [
              "name"
            ],
            "description": "A instituição prestadora desta instalação: a autorizada pelo Banco\nCentral que presta o serviço, e não a marca da tela. É o que vai na\nlinha \"Instituição\" dos comprovantes. Ausente quando a instalação\nnão configurou o nome: a tela não mostra instituição.\n",
            "properties": {
              "name": {
                "type": "string",
                "description": "Nome da instituição, como configurado em `LUME_INSTITUTION_NAME`.",
                "maxLength": 120,
                "examples": [
                  "Azimut Brasil DTVM"
                ]
              },
              "cnpj": {
                "type": "string",
                "description": "CNPJ da instituição, sem pontuação (`LUME_INSTITUTION_CNPJ`; pode\nser alfanumérico). Ausente quando não configurado.\n"
              }
            }
          }
        }
      },
      "AppVersions": {
        "type": "object",
        "description": "Uma chave por plataforma anunciada (`ios`, `android`). Plataforma nova\npode aparecer sem aviso: ignore a que não conhecer.\n",
        "properties": {
          "ios": {
            "$ref": "#/components/schemas/AppVersion"
          },
          "android": {
            "$ref": "#/components/schemas/AppVersion"
          }
        }
      },
      "AppVersion": {
        "type": "object",
        "required": [
          "latestVersion"
        ],
        "properties": {
          "latestVersion": {
            "type": "string",
            "pattern": "^[0-9]{1,6}(\\.[0-9]{1,6}){0,2}$",
            "description": "A versão mais nova publicada, de uma a três partes numéricas\n(`0.2.0`). Parte ausente vale zero na comparação (`1.2` = `1.2.0`).\n",
            "examples": [
              "0.2.0"
            ]
          },
          "minimumVersion": {
            "type": "string",
            "pattern": "^[0-9]{1,6}(\\.[0-9]{1,6}){0,2}$",
            "description": "A menor versão que ainda pode operar; nunca maior que\n`latestVersion`. Ausente: nenhuma versão é bloqueada.\n",
            "examples": [
              "0.1.0"
            ]
          },
          "updateUrl": {
            "type": "string",
            "description": "Para onde o botão \"Atualizar\" leva: `https://` (loja ou página do\nAPK) ou `itms-beta://` (o app do TestFlight). Ausente: o app pede\npara atualizar sem oferecer o link.\n",
            "examples": [
              "itms-beta://beta.itunes.apple.com/v1/app/6828064942"
            ]
          }
        }
      },
      "OnboardingStage": {
        "type": "string",
        "description": "Estágio da abertura decidida pelo provedor financeiro. Começa em\n`CUSTOMER_CREATED` (o cadastro existe no provedor e a pessoa ainda não\nterminou a tela dele). Estágio novo pode aparecer: trate o desconhecido\ncomo \"em análise\".\n",
        "enum": [
          "CUSTOMER_CREATED",
          "IN_PROGRESS",
          "SUBMITTED",
          "IN_ANALYSIS",
          "PENDING_ISSUES",
          "DOCUMENT_REQUESTED",
          "APPROVED",
          "REPROVED",
          "FAILED"
        ]
      },
      "TransactionKind": {
        "type": "string",
        "description": "`PIX_RECEIVED` é o Pix que outra instituição (ou outro cliente) enviou à\nconta: entrada, `COMPLETED`, com `amountCents` positivo. Traz o pagador em\n`counterparty` (nome, documento mascarado e banco) e o `endToEndId`; a\ndescrição é \"Pix recebido\" e, quando o pagador escreveu, o texto dele.\nCada Pix recebido aparece uma única vez, mesmo que o aviso do provedor\nseja reenviado.\n\n`TED_RECEIVED` é a TED que outra instituição enviou à conta: entrada,\n`COMPLETED`, com `amountCents` positivo, descrição \"TED recebida\" e o\nremetente em `counterparty` (nome, banco e documento mascarado). Não tem\n`endToEndId`. O valor só entra na conta quando o provedor libera a TED,\ne cada TED recebida aparece uma única vez, mesmo que o aviso seja\nreenviado ou chegue mais de uma vez.\n\n`PIX_REFUND_SENT` é a devolução, total ou parcial, de um Pix recebido:\nsaída (`amountCents` negativo), com `refundOf` apontando para o Pix\nrecebido e a descrição \"Devolução de Pix\". Nasce `PROCESSING` e fecha por\naviso do banco parceiro. `PIX_REFUND_RECEIVED` é a devolução que a conta\nrecebeu de um Pix que ela enviou: entrada (`amountCents` positivo),\n`COMPLETED`, creditada uma única vez; `refundOf` aponta para o Pix enviado\nquando o banco informa qual foi e ele é desta conta, e vem ausente quando\nnão há vínculo.\n\n`BLOCKAGE` é o valor que uma ordem externa (judicial ou cautelar do MED)\nbloqueou na conta: sai do saldo disponível, mas não é gasto do cliente\ne não conta para limites. `amountCents` é negativo, a `description` é o\nmotivo genérico (\"Bloqueio judicial\" ou \"Bloqueio cautelar (MED)\") e o\nlançamento é `COMPLETED`, sem `counterparty`, `endToEndId` nem\n`providerId`. O número do processo e a origem da ordem nunca aparecem\naqui. `BLOCKAGE_RELEASE` é o valor que voltou ao saldo com o\ndesbloqueio: entrada, `amountCents` positivo, \"Desbloqueio judicial\" ou\n\"Desbloqueio cautelar (MED)\". Quando o bloqueio é maior do que o saldo\nque o Lume tinha, só o que coube no saldo vira lançamento; o total\nbloqueado no provedor continua em `blockedCents` de\n`GET /v1/accounts/balance`.\n\n`TOPUP_MOBILE` é a recarga de celular pré-pago: saída (`amountCents`\nnegativo), com a operadora em `counterparty` e a descrição \"Recarga\",\no nome da operadora e o número mascarado (\"(21) 9****-4408\"). O\nnúmero completo nunca aparece aqui. Nasce `PROCESSING`, ou\n`HELD_FOR_REVIEW` quando o antifraude retém, e só existe na\ninstalação com a funcionalidade `topup` ligada.\n\n`CASHBACK` é a recompensa por indicação que o banco pagou à conta:\nentrada, `amountCents` positivo, `COMPLETED`, descrição \"Cashback de\nindicação\" e, em `counterparty`, só o primeiro nome e a inicial de quem\nabriu a conta indicada. Só existe na instalação que paga recompensa\nde indicação (funcionalidade `referrals` com valor configurado).\n\n`FEE_SHARE_RECEIVED` é o repasse de uma tarifa ou mensalidade que a\ninstituição rateia com esta conta (split, funcionalidade\n`service_fees`): entrada, `amountCents` positivo, `COMPLETED`,\ndescrição \"Repasse · <descrição da regra>\", sem `counterparty` — o\ncliente pagador nunca aparece. Na operação de saída o repasse só\nentra quando ela conclui (repasse só de tarifa final). O estorno da\ntarifa de origem debita o repasse de volta como `FEE_SHARE_REVERSED`\n(\"Estorno · Repasse · …\"): saída que não conta como gasto do destino.\n\n`CARD_PURCHASE`, `CARD_REFUND` e `CARD_FEE` são do cartão pré-pago, todos\n`COMPLETED` e lançados uma única vez por transação do emissor, mesmo\nque o aviso se repita. `CARD_PURCHASE` é a compra, o saque ou o\npagamento feito com o cartão: saída, com o estabelecimento em\n`counterparty.name` e a descrição \"Compra no cartão final 4417 ·\nMERCADO EXEMPLO\" (no saque, \"Saque no cartão final 4417\"). O emissor\njá autorizou e debitou do lado dele: o lançamento entra mesmo que o\nsaldo fique negativo. `CARD_REFUND` é o estorno de uma compra:\nentrada. `CARD_FEE` é a tarifa de saque do emissor, à parte da\ncompra: saída. A compra recusada não vira lançamento; aparece só em\n`GET /v1/cards/{id}/transactions`.\n",
        "enum": [
          "TRANSFER_INTERNAL",
          "TRANSFER_EXTERNAL",
          "TRANSFER_PIX",
          "PAYMENT_PIX",
          "PAYMENT_QRCODE",
          "PAYMENT_BILLET",
          "BILLING_RECEIVED",
          "PIX_RECEIVED",
          "TARIFF",
          "REVERSAL",
          "CASHBACK",
          "TOPUP_MOBILE",
          "BLOCKAGE",
          "BLOCKAGE_RELEASE",
          "TED_RECEIVED",
          "PIX_REFUND_SENT",
          "PIX_REFUND_RECEIVED",
          "FEE_SHARE_RECEIVED",
          "FEE_SHARE_REVERSED",
          "CARD_PURCHASE",
          "CARD_REFUND",
          "CARD_FEE"
        ]
      },
      "TransactionStatus": {
        "type": "string",
        "description": "`PROCESSING` não é estado de repouso: ou o provedor confirma, ou a\nconciliação resolve. Uma transação parada nele é defeito, não espera.\n",
        "enum": [
          "PENDING",
          "PROCESSING",
          "COMPLETED",
          "FAILED",
          "REVERSED",
          "HELD_FOR_REVIEW",
          "BLOCKED"
        ]
      },
      "BilletLookup": {
        "type": "object",
        "description": "Um boleto ou uma guia de arrecadação como o banco o conhece agora. Nenhum\nidentificador do banco parceiro sai aqui.\n",
        "required": [
          "beneficiary",
          "dueDate",
          "amountCents",
          "fineCents",
          "interestCents",
          "discountCents",
          "totalCents",
          "kind",
          "allowChangeValue"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "BILL",
              "UTILITY"
            ],
            "description": "`BILL` é a ficha de compensação (boleto); `UTILITY` é a guia de\narrecadação (concessionária ou tributo).\n"
          },
          "beneficiary": {
            "type": "string"
          },
          "beneficiaryDocument": {
            "type": "string",
            "description": "Documento do beneficiário, mascarado. Ausente se o banco não informa."
          },
          "finalBeneficiary": {
            "type": "object",
            "description": "O beneficiário final, quando o título o tem.",
            "properties": {
              "name": {
                "type": "string"
              },
              "document": {
                "type": "string",
                "description": "Mascarado."
              }
            }
          },
          "dueDate": {
            "type": "string",
            "description": "`date-time`, ou **vazio** quando o título não tem vencimento (a guia de\narrecadação em alguns casos).\n"
          },
          "amountCents": {
            "type": "integer",
            "description": "Valor de face",
            "sem encargos.": null
          },
          "fineCents": {
            "type": "integer"
          },
          "interestCents": {
            "type": "integer"
          },
          "discountCents": {
            "type": "integer"
          },
          "totalCents": {
            "type": "integer",
            "description": "O que sai da conta se o pagamento acontecer agora."
          },
          "allowChangeValue": {
            "type": "boolean",
            "description": "O cliente escolhe o valor, entre `minCents` e `maxCents`."
          },
          "minCents": {
            "type": "integer",
            "description": "Só com `allowChangeValue`."
          },
          "maxCents": {
            "type": "integer",
            "description": "Só com `allowChangeValue`."
          },
          "cutoff": {
            "type": "object",
            "description": "O horário em que o banco aceita o pagamento, no fuso da operação. Fora\ndele o pagamento é recusado. Ausente se o banco não informa.\n",
            "properties": {
              "start": {
                "type": "string",
                "examples": [
                  "07:00"
                ]
              },
              "end": {
                "type": "string",
                "examples": [
                  "20:00"
                ]
              }
            }
          },
          "settleDate": {
            "type": "string",
            "format": "date",
            "description": "Data prevista de liquidação, quando o banco informa."
          },
          "nextBusinessDay": {
            "type": "string",
            "format": "date",
            "description": "O próximo dia útil, quando o banco informa."
          }
        }
      },
      "BilletReceipt": {
        "type": "object",
        "description": "O comprovante do pagamento de contas que o banco parceiro devolveu: só o\nnecessário. O texto do comprovante dele, que traz CPF e CNPJ completos, não\né guardado nem devolvido; o beneficiário (com documento mascarado) está em\n`counterparty` da transação.\n",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "BILL",
              "UTILITY"
            ]
          },
          "authentication": {
            "type": "string",
            "description": "A autenticação do pagamento."
          },
          "settleDate": {
            "type": "string",
            "format": "date",
            "description": "Data de liquidação."
          },
          "convenant": {
            "type": "string",
            "description": "O convênio ou banco de liquidação",
            "como o banco o escreve.": null
          }
        }
      },
      "ScheduledPayment": {
        "type": "object",
        "description": "Um PIX ou pagamento de boleto marcado para uma data. Não é uma transação:\no extrato só ganha linha quando ele é executado (no agendamento do banco\nparceiro, a transação que reserva o valor existe desde o agendamento).\n",
        "required": [
          "id",
          "kind",
          "status",
          "amountCents",
          "scheduledFor",
          "runAt",
          "counterparty",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "pix",
              "billet"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "processing",
              "executed",
              "failed",
              "canceled"
            ],
            "description": "`scheduled` espera a data (e se cancela); `processing` está sendo\nexecutado ou, no agendamento do banco parceiro, aguarda a confirmação\ndele; `executed` foi pago; `failed` não foi pago (veja\n`failureReason`) e o valor voltou, se estava reservado; `canceled`.\n"
          },
          "amountCents": {
            "type": "integer",
            "minimum": 1
          },
          "scheduledFor": {
            "type": "string",
            "format": "date"
          },
          "runAt": {
            "type": "string",
            "format": "date-time",
            "description": "O instante a partir do qual o pagamento pode sair (às `runHour` no\nagendamento do Lume; à meia-noite do dia no do banco parceiro).\n"
          },
          "counterparty": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "document": {
                "type": "string",
                "description": "Sempre mascarado."
              },
              "bank": {
                "type": "string"
              },
              "pixKey": {
                "type": "string",
                "description": "Sempre mascarada."
              }
            }
          },
          "bankAccount": {
            "description": "Destino de um Pix por agência e conta agendado (sem `pixKey`), com\nconta e documento mascarados. Nome e documento são os informados\npelo cliente, **não confirmados** pelo banco de destino. O documento\ninteiro nunca volta.\n",
            "type": "object",
            "properties": {
              "bankIspb": {
                "type": "string",
                "examples": [
                  "18236120"
                ]
              },
              "branch": {
                "type": "string",
                "examples": [
                  "0001"
                ]
              },
              "accountMasked": {
                "type": "string",
                "examples": [
                  "••••••977"
                ]
              },
              "accountType": {
                "type": "string",
                "enum": [
                  "CHECKING",
                  "SAVINGS",
                  "PAYMENT",
                  "SALARY"
                ]
              },
              "holderName": {
                "type": "string",
                "description": "Como o cliente digitou."
              },
              "holderDocumentMasked": {
                "type": "string"
              }
            }
          },
          "description": {
            "type": "string"
          },
          "billetDueDate": {
            "type": "string",
            "format": "date"
          },
          "providerManaged": {
            "type": "boolean",
            "description": "Presente e verdadeiro quando o agendamento foi criado no banco\nparceiro, que paga na data; o valor fica reservado desde o dia de\nagendar.\n"
          },
          "transactionId": {
            "type": "string"
          },
          "transactionStatus": {
            "type": "string",
            "description": "O desfecho da transação ligada."
          },
          "failureReason": {
            "type": "string",
            "description": "Frase pronta para o cliente."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "executedAt": {
            "type": "string",
            "format": "date-time"
          },
          "canceledAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "tx_9f2c1a7b"
            ]
          },
          "kind": {
            "$ref": "#/components/schemas/TransactionKind"
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "amountCents": {
            "type": "integer",
            "description": "Negativo saiu, positivo entrou.",
            "examples": [
              -5000
            ]
          },
          "description": {
            "type": "string"
          },
          "counterparty": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "document": {
                "type": "string",
                "description": "Mascarado."
              },
              "bank": {
                "type": "string"
              },
              "pixKey": {
                "type": "string",
                "description": "Sempre mascarada, na mesma forma da consulta de chave; vazia quando o destino não foi uma chave Pix (TED, QR dinâmico, Pix por agência e conta, boleto)."
              }
            }
          },
          "endToEndId": {
            "type": "string",
            "description": "Identificador do arranjo PIX."
          },
          "providerTransactionId": {
            "type": "string"
          },
          "riskScore": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "riskSignals": {
            "type": "array",
            "description": "O que o motor de risco avaliou — inclusive o que conferiu e está\nnormal. Uma tela que só mostra o que deu errado não deixa o titular\ndecidir nada.\n",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "examples": [
                    "NIGHT_WINDOW"
                  ]
                },
                "label": {
                  "type": "string",
                  "examples": [
                    "Fora do horário habitual"
                  ]
                },
                "detail": {
                  "type": "string"
                },
                "triggered": {
                  "type": "boolean"
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "executedAt": {
            "type": "string",
            "format": "date-time"
          },
          "refundOf": {
            "type": "string",
            "description": "Id da transação que esta devolve: na devolução enviada\n(`PIX_REFUND_SENT`), o Pix recebido; na recebida\n(`PIX_REFUND_RECEIVED`), o Pix enviado, quando houve vínculo.\n"
          },
          "refundableCents": {
            "type": "integer",
            "minimum": 0,
            "description": "Quanto ainda pode ser devolvido, no Pix recebido concluído\n(`PIX_RECEIVED`): o valor original menos as devoluções que não\nfalharam. `0` é \"já devolvido por inteiro\". Ausente nas demais\ntransações e quando o banco desta instalação não devolve Pix.\n"
          },
          "billetReceipt": {
            "description": "O comprovante de um pagamento de contas (`kind: PAYMENT_BILLET`): só a\nautenticação, a data de liquidação e o convênio. Só no detalhe, e só\nquando o banco já devolveu a confirmação.\n",
            "allOf": [
              {
                "$ref": "#/components/schemas/BilletReceipt"
              }
            ]
          },
          "disputableUntil": {
            "type": "string",
            "format": "date-time",
            "description": "Até quando o Pix enviado ainda pode ser contestado (MED, 80 dias): o\nprazo é do servidor. Ausente quando não se aplica (não é Pix enviado e\nconcluído deste banco, o prazo passou, ou já há contestação que\nvale). Só no detalhe, e só na sessão do cliente: a chave de API não o vê.\n"
          },
          "disputeId": {
            "type": "string",
            "description": "A contestação mais recente da transação, de qualquer status\n(`GET /v1/pix/disputes/{id}`). Só no detalhe, e só na sessão do cliente.\n"
          },
          "ted": {
            "description": "Destino e controle de uma TED de saída (`kind: TRANSFER_EXTERNAL`),\ncom conta e documento mascarados. Só no detalhe e na resposta do envio.\n",
            "type": "object",
            "properties": {
              "bankIspb": {
                "type": "string",
                "examples": [
                  "18236120"
                ]
              },
              "branch": {
                "type": "string",
                "examples": [
                  "0001"
                ]
              },
              "accountMasked": {
                "type": "string",
                "examples": [
                  "••••••977"
                ]
              },
              "accountType": {
                "type": "string",
                "enum": [
                  "CHECKING",
                  "SAVINGS",
                  "PAYMENT",
                  "SALARY"
                ]
              },
              "holderName": {
                "type": "string",
                "description": "Como o cliente digitou."
              },
              "holderDocumentMasked": {
                "type": "string"
              },
              "movementId": {
                "type": "string",
                "description": "Identificador do movimento no banco parceiro; vazio até o aceite."
              },
              "strControl": {
                "type": "string",
                "description": "Controle do SPB (`originStr`), quando o aviso de liquidação o trouxer."
              }
            }
          },
          "bankAccount": {
            "description": "Destino de um Pix por agência e conta (`kind: TRANSFER_PIX`, sem\nchave), com conta e documento mascarados. Nome e documento são os\ninformados pelo cliente, **não confirmados** pelo banco de destino.\nSó no detalhe e na resposta do envio.\n",
            "type": "object",
            "properties": {
              "bankIspb": {
                "type": "string",
                "examples": [
                  "18236120"
                ]
              },
              "branch": {
                "type": "string",
                "examples": [
                  "0001"
                ]
              },
              "accountMasked": {
                "type": "string",
                "examples": [
                  "••••••977"
                ]
              },
              "accountType": {
                "type": "string",
                "enum": [
                  "CHECKING",
                  "SAVINGS",
                  "PAYMENT",
                  "SALARY"
                ]
              },
              "holderName": {
                "type": "string",
                "description": "Como o cliente digitou."
              },
              "holderDocumentMasked": {
                "type": "string"
              },
              "movementId": {
                "type": "string",
                "description": "Identificador do movimento no banco parceiro; vazio até o aceite."
              }
            }
          },
          "feeCents": {
            "type": "integer",
            "minimum": 0,
            "description": "A tarifa decidida para esta operação (funcionalidade\n`service_fees`), em centavos. `0` é \"isenta\" (por norma, pela\ncesta do plano ou pela tabela); ausente é \"sem decisão de tarifa\"\n(operação sem serviço tarifável, ou anterior à funcionalidade).\nNo extrato e na resposta do envio.\n"
          },
          "feeTransactionId": {
            "type": "string",
            "description": "O lançamento da tarifa (`kind: TARIFF`), quando `feeCents` > 0 e já cobrada."
          },
          "feeReason": {
            "type": "string",
            "enum": [
              "EXEMPT_BY_RULE",
              "COVERED_BY_PLAN",
              "TABLE",
              "FREE"
            ],
            "description": "Por que a tarifa é o que é: isenção por norma, coberta pela cesta\ndo plano, preço da tabela, ou sem preço.\n"
          },
          "feeStatus": {
            "type": "string",
            "enum": [
              "CHARGED",
              "PENDING",
              "REVERSED",
              "FAILED"
            ],
            "description": "A situação da tarifa. No extrato, `REVERSED` (operação estornada,\ntarifa devolvida) e `FAILED` (tarifa de entrada que não coube no\nsaldo e foi para a fila) vêm SEM `feeCents`/`feeTransactionId`:\na linha não mostra tarifa que não vale.\n"
          },
          "fee": {
            "description": "A tarifa desta operação, só no detalhe (`GET /v1/transactions/{id}`).",
            "allOf": [
              {
                "$ref": "#/components/schemas/TransactionFee"
              }
            ]
          },
          "feeOf": {
            "type": "string",
            "description": "No lançamento da tarifa (`kind: TARIFF`) e no estorno dela\n(`kind: REVERSAL`): o id da operação que a gerou (ou do boleto, na\ntarifa de registro e de liquidação).\n"
          }
        }
      },
      "BankDirectory": {
        "type": "object",
        "description": "A lista oficial de instituições de destino (`GET /v1/banks`).",
        "required": [
          "sources",
          "items"
        ],
        "properties": {
          "sources": {
            "type": "array",
            "description": "Os arquivos do Banco Central de onde a lista saiu.",
            "items": {
              "type": "object",
              "required": [
                "name",
                "asOf"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "enum": [
                    "str",
                    "pix"
                  ],
                  "description": "`str`: participantes do STR. `pix`: participantes do Pix."
                },
                "asOf": {
                  "type": "string",
                  "format": "date",
                  "description": "Data do arquivo.",
                  "examples": [
                    "2026-10-06"
                  ]
                }
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "ispb",
                "shortName",
                "fullName",
                "ted",
                "pix"
              ],
              "properties": {
                "ispb": {
                  "type": "string",
                  "description": "ISPB, 8 dígitos.",
                  "examples": [
                    "60701190"
                  ]
                },
                "code": {
                  "type": "string",
                  "description": "Código de compensação (COMPE), 3 dígitos. Ausente na\ninstituição que só está no Pix.\n",
                  "examples": [
                    "341"
                  ]
                },
                "shortName": {
                  "type": "string",
                  "description": "Nome reduzido, como o Banco Central publica (o do STR, se estiver nele).",
                  "examples": [
                    "ITAÚ UNIBANCO S.A."
                  ]
                },
                "fullName": {
                  "type": "string",
                  "description": "Nome extenso do STR, ou o nome do Pix quando só está no Pix.",
                  "examples": [
                    "ITAÚ UNIBANCO S.A."
                  ]
                },
                "ted": {
                  "type": "boolean",
                  "description": "Participante do STR, recebe TED."
                },
                "pix": {
                  "type": "boolean",
                  "description": "Participante ativo do Pix que mantém conta, recebe Pix por agência e conta."
                }
              }
            }
          }
        }
      },
      "BankAccountInput": {
        "type": "object",
        "description": "Conta bancária de destino, digitada pelo cliente (Pix por agência e\nconta e favorecido por conta bancária). As regras são as da TED.\n",
        "required": [
          "ispb",
          "branch",
          "account",
          "accountType",
          "holderName",
          "holderDocument"
        ],
        "properties": {
          "ispb": {
            "type": "string",
            "description": "ISPB do banco de destino, 8 dígitos, de uma instituição de\n`GET /v1/banks`: com `pix: true` no Pix por agência e conta, com\n`ted` ou `pix` no favorecido. Fora da lista: `422`, `details.field:\nbankIspb`.\n",
            "examples": [
              "18236120"
            ]
          },
          "branch": {
            "type": "string",
            "description": "Agência, de 1 a 5 dígitos.",
            "examples": [
              "0001"
            ]
          },
          "account": {
            "type": "string",
            "description": "Conta com o dígito verificador concatenado, de 1 a 20 dígitos.\nPontuação (`91926197-7`) é aceita e retirada; letra é recusada.\n",
            "examples": [
              "919261977"
            ]
          },
          "accountType": {
            "type": "string",
            "description": "`ALIAS` fica fora até o banco parceiro dizer o que é.",
            "enum": [
              "CHECKING",
              "SAVINGS",
              "PAYMENT",
              "SALARY"
            ]
          },
          "holderName": {
            "type": "string",
            "minLength": 2,
            "maxLength": 140,
            "description": "Nome do titular, como digitado. Não é confirmado pelo banco de destino."
          },
          "holderDocument": {
            "type": "string",
            "description": "CPF ou CNPJ do titular, com ou sem pontuação. Nunca volta inteiro."
          }
        }
      },
      "PixKeyType": {
        "type": "string",
        "description": "Nomenclatura do arranjo. `EVP` e a chave aleatoria - o nome nao e\n`RANDOM`.\n",
        "enum": [
          "CPF",
          "CNPJ",
          "EMAIL",
          "PHONE",
          "EVP"
        ]
      },
      "PixKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/PixKeyType"
          },
          "value": {
            "type": "string"
          },
          "masked": {
            "type": "string",
            "description": "Forma segura de exibir em tela."
          },
          "status": {
            "$ref": "#/components/schemas/PixKeyStatus"
          },
          "statusDetail": {
            "type": "string",
            "description": "Texto seguro para exibir ao titular junto do status. Ausente na\nchave `ACTIVE`.\n"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "claimActions": {
            "type": "array",
            "description": "Só com a portabilidade pelo titular ligada (`pix_key_claims`) e na\nsessão do cliente: as ações que cabem à chave agora em\n`POST /v1/pix/keys/{id}/claim`. Vazia quando não há o que fazer.\n",
            "items": {
              "$ref": "#/components/schemas/PixKeyClaimAction"
            }
          },
          "claimRequest": {
            "type": "object",
            "description": "Só com `pix_key_claims` e na sessão: o pedido do titular ainda sem\ndesfecho sobre esta chave. `UNKNOWN`: a outra ponta não respondeu a\ntempo; não repita, o andamento chega pela lista.\n",
            "properties": {
              "id": {
                "type": "string"
              },
              "action": {
                "type": "string",
                "enum": [
                  "START",
                  "OPEN",
                  "ACCEPT",
                  "REJECT",
                  "CANCEL",
                  "COMPLETE"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "RESERVED",
                  "SENT",
                  "UNKNOWN"
                ]
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "PixKeyClaimAction": {
        "type": "string",
        "enum": [
          "OPEN",
          "ACCEPT",
          "REJECT",
          "CANCEL",
          "COMPLETE"
        ],
        "description": "Ação sobre uma portabilidade ou reivindicação em andamento:\n`ACCEPT` e `REJECT` respondem ao pedido de outra instituição (liberar\nou manter a chave); `OPEN`, `CANCEL` e `COMPLETE` são de quem pediu a\nchave aqui (abrir o pedido de reivindicação, desistir, confirmar a\nchave liberada).\n"
      },
      "PixKeyClaimRequest": {
        "type": "object",
        "description": "Um pedido do titular sobre portabilidade ou reivindicação. A chave vai\nsó mascarada.\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "keyId": {
            "type": "string",
            "description": "A chave da conta sobre a qual o pedido age."
          },
          "action": {
            "type": "string",
            "enum": [
              "START",
              "OPEN",
              "ACCEPT",
              "REJECT",
              "CANCEL",
              "COMPLETE"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "RESERVED",
              "SENT",
              "UNKNOWN",
              "DONE"
            ],
            "description": "`SENT`: a outra ponta aceitou o pedido; o resultado vem pela lista.\n`UNKNOWN`: sem resposta a tempo; não repita. `DONE`: o andamento já\nmudou depois do pedido.\n"
          },
          "masked": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "Texto seguro para exibir."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PixKeyStatus": {
        "type": "string",
        "enum": [
          "ACTIVE",
          "PENDING",
          "ACTION_REQUIRED",
          "REMOVED"
        ],
        "description": "Estado da chave na linguagem do Lume, e não a do provedor.\n\n- `ACTIVE`: registrada e em uso.\n- `PENDING`: à espera do arranjo (portabilidade ou reivindicação em\n  curso, ou criação ainda não confirmada). Um estado que o Lume não\n  reconhece também é `PENDING`, nunca `ACTIVE`.\n- `ACTION_REQUIRED`: à espera de uma ação do titular numa portabilidade\n  ou reivindicação. Com `pix_key_claims` ligada, a ação vem em\n  `claimActions` e se faz em `POST /v1/pix/keys/{id}/claim`; desligada\n  (o padrão), quando outra instituição pede a chave do titular, o\n  `statusDetail` o orienta a procurar o atendimento, dentro do prazo de\n  7 dias do arranjo.\n- `REMOVED`: saiu do titular (doada por reivindicação ou portabilidade,\n  cancelada, ou excluída fora do Lume). Libera o valor e não conta para\n  o teto. A doação concluída, avisada pelo provedor, apaga a chave da\n  conta: ela deixa de aparecer na lista e não serve mais a QR Code nem a\n  cobrança, e o titular a vê no histórico de segurança (`PIX_KEY`).\n"
      },
      "PixQRCode": {
        "type": "object",
        "description": "Cobrança por QR Code Pix. O `payload` traz a chave e o nome do\nrecebedor e sai só para o dono da cobrança. O `qrCodeId` e o\nidentificador de conciliação do provedor não saem.\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "STATIC",
              "DYNAMIC"
            ]
          },
          "payload": {
            "type": "string",
            "description": "O copia e cola (BR Code, padrão EMV)."
          },
          "paymentLink": {
            "type": "string",
            "format": "uri",
            "description": "URL pública da cobrança dinâmica. Ausente na estática."
          },
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Zero na estática de valor livre."
          },
          "amountEditable": {
            "type": "boolean"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Só na dinâmica."
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "PAID",
              "EXPIRED",
              "DELETED"
            ],
            "description": "`EXPIRED` é a aberta cujo vencimento passou, lida da data. Só o\ncódigo de uso único fecha como `PAID`: o estático de valor livre\nsegue `OPEN` depois de pago.\n"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "paidTransactionId": {
            "type": "string",
            "description": "O crédito (Pix recebido) que pagou a cobrança. Só quando\n`PAID`, e só nas cobranças que o provedor liga ao crédito.\n"
          },
          "paidCents": {
            "type": "integer",
            "format": "int64",
            "description": "O valor que o Pix pago de fato trouxe, em centavos. Só quando\n`PAID`. Difere de `amountCents` na cobrança de valor editável (o\npagador pôde mudá-lo): é este o número a mostrar como pago. Campo\nfora da proposta da tarefa.\n"
          }
        }
      },
      "Billet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "REGISTERING",
              "REGISTERED",
              "PAID",
              "CANCELED",
              "EXPIRED",
              "FAILED"
            ],
            "description": "`REGISTERING` é o pedido aceito e ainda não confirmado: o título\n**não é pagável** e não tem linha digitável. `REGISTERED` é pagável.\nOs quatro restantes são terminais.\n\nO pagamento não cria status: `PAID` só quando o título é quitado\n(liquidado por inteiro na conta). Um título pago em parte, ou\npago pelo pagador e ainda não liquidado, segue `REGISTERED`; o que\njá entrou está em `paidAmountCents` e `settlements`, e o que está a\ncaminho, em `compensatedAt`. Título baixado ou expirado que recebe\num pagamento mesmo assim é creditado, e o `status` não muda.\n"
          },
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Valor de face."
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "examples": [
              "2026-10-20"
            ]
          },
          "limitDate": {
            "type": "string",
            "format": "date",
            "description": "Último dia de aceitação. Distinto do vencimento porque um boleto\nvencido continua pagável, com encargos, até este prazo.\n"
          },
          "payer": {
            "$ref": "#/components/schemas/BilletPayer"
          },
          "description": {
            "type": "string"
          },
          "instructions": {
            "type": "string"
          },
          "fees": {
            "$ref": "#/components/schemas/BilletFees"
          },
          "paymentType": {
            "type": "string",
            "enum": [
              "TOTAL",
              "PARTIAL"
            ],
            "description": "`TOTAL` aceita só o valor cheio. `PARTIAL` aceita pagamentos entre\n`minAmountCents` e `maxAmountCents`, até `maxPayments` vezes.\n"
          },
          "minAmountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Só em `PARTIAL`."
          },
          "maxAmountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Só em `PARTIAL`."
          },
          "maxPayments": {
            "type": "integer",
            "minimum": 1,
            "maximum": 999,
            "description": "Só em `PARTIAL`."
          },
          "kind": {
            "$ref": "#/components/schemas/BilletKind"
          },
          "finalBeneficiary": {
            "$ref": "#/components/schemas/BilletFinalBeneficiary"
          },
          "ourNumber": {
            "type": "string",
            "description": "Nosso número — o identificador do título dentro do banco emissor."
          },
          "barcode": {
            "type": "string",
            "description": "44 dígitos. Só com `status` = `REGISTERED`."
          },
          "digitableLine": {
            "type": "string",
            "description": "47 dígitos, sem máscara. Só com `status` = `REGISTERED`."
          },
          "formattedLine": {
            "type": "string",
            "description": "A linha com a máscara de impressão. Vem pronta do servidor porque a\nmáscara é regra do layout, não estilo de tela — três clientes\nformatando por conta própria são três formatos, e um deles errado.\nSó com `status` = `REGISTERED`.\n"
          },
          "pixPayload": {
            "type": "string",
            "description": "BR Code do boleto híbrido. É o mesmo título: pagar pelo QR dá baixa\nno boleto. Só com `status` = `REGISTERED`.\n"
          },
          "url": {
            "type": "string",
            "description": "PDF hospedado pelo provedor, quando ele hospeda (a instalação Azify\nnão hospeda: o PDF é gerado pelo Lume a partir do título). Só com\n`status` = `REGISTERED`.\n"
          },
          "paidAmountCents": {
            "type": "integer",
            "format": "int64",
            "description": "O **acumulado** recebido e creditado na conta: a soma de todos os\npagamentos já liquidados, que difere do valor de face sempre que\nhouve multa, juros, desconto ou pagamento parcial. Para os títulos\ncreditados a partir desta versão, é a soma de\n`settlements[].amountCents`; os pagos antes dela têm o valor e não\ntêm `settlements`. Ausente até o primeiro crédito.\n"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "description": "O momento do último crédito. Ausente até o primeiro."
          },
          "transactionId": {
            "type": "string",
            "description": "O último crédito `BILLING_RECEIVED` gerado na liquidação (o de\n`settlements[]` mais recente). Vazio até o primeiro crédito.\n"
          },
          "compensatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o pagador pagou e o pagamento foi compensado, e o dinheiro\n**ainda não foi liquidado na conta** (24 h em dia útil, até 48 h\ndepois do horário de corte, 72 h ou mais em fim de semana e feriado\nprolongado). Nada foi creditado ainda. Some quando o crédito é\nlançado; ausente quando não há pagamento nesse estado.\n"
          },
          "settlements": {
            "type": "array",
            "description": "Cada pagamento creditado, do mais antigo ao mais novo. Pagamento\nparcial gera um item por pagamento liquidado. Ausente até o primeiro\ncrédito.\n",
            "items": {
              "$ref": "#/components/schemas/BilletSettlement"
            }
          },
          "failureReason": {
            "type": "string",
            "description": "A recusa do arranjo, quando o registro falhou."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "canceledAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BilletSettlement": {
        "type": "object",
        "description": "Um pagamento do título creditado na conta.",
        "properties": {
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "O valor creditado neste pagamento."
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "Quando foi creditado."
          },
          "transactionId": {
            "type": "string",
            "description": "O crédito `BILLING_RECEIVED` deste pagamento, no extrato."
          }
        }
      },
      "BilletPayer": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "type": "string",
            "description": "CPF ou CNPJ formatado, **completo**. Contraria o padrão das outras\nrotas, que mascaram documento de terceiro, e a diferença é de onde o\ndado veio: este foi digitado pelo próprio cliente, para este boleto.\nMascará-lo esconderia dele o que acabou de escrever. O CNPJ\nalfanumérico vem com as letras (`12.ABC.345/01DE-35`).\n"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "BilletFees": {
        "type": "object",
        "description": "Percentuais em **centésimos de por cento** — 250 é 2,5%. Nunca float: um\njuro escrito como fração vira centavo faltando quando multiplicado por um\nsaldo. Quem apura o encargo no pagamento é o arranjo; isto é a instrução\ngravada no registro.\n",
        "properties": {
          "fineBasisPoints": {
            "type": "integer",
            "maximum": 200,
            "description": "Multa por atraso, cobrada uma vez. Teto legal de 2%."
          },
          "interestMonthlyBasisPoints": {
            "type": "integer",
            "maximum": 100,
            "description": "Juro ao mês, proporcional aos dias. Teto legal de 1%. Na instalação\nAzify, juro acima de zero ainda é recusado com `422` (a unidade do\ncampo de lá não está confirmada).\n"
          },
          "discountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Um desconto em valor até `discountUntil` (vazio: até o vencimento).\nNo pedido, é o jeito antigo de informar um desconto só, e não vai\njunto com `discounts`. Na resposta, repete o primeiro desconto\n`FIXED_DATE` de `discounts`.\n"
          },
          "discountUntil": {
            "type": "string",
            "format": "date"
          },
          "discounts": {
            "type": "array",
            "maxItems": 3,
            "description": "Até três descontos, todos do mesmo tipo. `FIXED_DATE`: até três,\ncada um com `amountCents` (abaixo do valor do boleto) e `until` (até\no vencimento), com as datas crescentes. `CALENDAR_DAY` e\n`BUSINESS_DAY`: um só, com `basisPoints` (de 1 a 9999) por dia de\nantecipação, sem data.\n",
            "items": {
              "$ref": "#/components/schemas/BilletDiscount"
            }
          },
          "deductionCents": {
            "type": "integer",
            "format": "int64",
            "description": "Abatimento fixo, concedido em qualquer data. O maior desconto em\nvalor mais o abatimento ficam abaixo do valor do boleto.\n"
          }
        }
      },
      "BilletDiscount": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "FIXED_DATE",
              "CALENDAR_DAY",
              "BUSINESS_DAY"
            ]
          },
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "Só em `FIXED_DATE`."
          },
          "basisPoints": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9999,
            "description": "Só em `CALENDAR_DAY` e `BUSINESS_DAY`."
          },
          "until": {
            "type": "string",
            "format": "date",
            "description": "Só em `FIXED_DATE`",
            "e obrigatório nele.": null
          }
        }
      },
      "BilletKind": {
        "type": "string",
        "enum": [
          "MERCHANT_DUPLICATE",
          "SERVICE_DUPLICATE",
          "OTHER",
          "DEPOSIT_CONTRIBUTION"
        ],
        "description": "Espécie do título. Omitida, é `OTHER`: duplicata exige uma venda ou um\nserviço de verdade por trás.\n"
      },
      "BilletFinalBeneficiary": {
        "type": "object",
        "required": [
          "name",
          "document"
        ],
        "description": "O beneficiário final, quando o crédito é de um terceiro. Na resposta,\no documento sai completo e formatado, como o do pagador.\n",
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "type": "string",
            "description": "CPF ou CNPJ",
            "com ou sem pontuação.": null
          }
        }
      },
      "BilletRequest": {
        "type": "object",
        "required": [
          "amountCents",
          "dueDate",
          "payer"
        ],
        "properties": {
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "examples": [
              125000
            ]
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "description": "Data pura, sem hora e sem fuso, porque é isso que vencimento é: um\ndia do calendário. Vencer hoje é aceito; no passado, não.\n",
            "examples": [
              "2026-10-20"
            ]
          },
          "limitDate": {
            "type": "string",
            "format": "date",
            "description": "Vazio usa 30 dias após o vencimento."
          },
          "payer": {
            "type": "object",
            "required": [
              "name",
              "document"
            ],
            "properties": {
              "name": {
                "type": "string",
                "examples": [
                  "Construtora Vale Verde LTDA"
                ]
              },
              "document": {
                "type": "string",
                "description": "CPF ou CNPJ, com ou sem pontuação. O CNPJ pode ter letras\n(CNPJ alfanumérico, que a Receita Federal atribui às novas\ninscrições a partir de julho de 2026): minúsculas são aceitas,\ne a forma gravada é em maiúsculas, sem pontuação.\n",
                "examples": [
                  "11.222.333/0001-81"
                ]
              },
              "email": {
                "type": "string"
              },
              "address": {
                "type": "object",
                "description": "Exigido por parte dos arranjos no registro. Guardamos o que for\ninformado, e é o adaptador do provedor que recusa o que falta\npara aquele parceiro.\n",
                "properties": {
                  "street": {
                    "type": "string"
                  },
                  "number": {
                    "type": "string"
                  },
                  "complement": {
                    "type": "string"
                  },
                  "district": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "state": {
                    "type": "string"
                  },
                  "zip": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "description": {
            "type": "string",
            "description": "O demonstrativo que o pagador lê."
          },
          "instructions": {
            "type": "string",
            "description": "O texto para o caixa."
          },
          "fees": {
            "$ref": "#/components/schemas/BilletFees"
          },
          "pixEnabled": {
            "type": "boolean",
            "description": "Pede o boleto híbrido, com BR Code no mesmo título. Vale a pena\nsempre que o arranjo souber emitir: é como a maior parte dos boletos\né paga hoje, e o QR liquida no mesmo dia.\n\nNuma instalação com o boleto com Pix desligado, `true` é recusado\ncom 403 `FEATURE_DISABLED` e `details.feature` = `billet_issue_pix`;\no mesmo pedido com `false` emite normalmente.\n"
          },
          "paymentType": {
            "type": "string",
            "enum": [
              "TOTAL",
              "PARTIAL"
            ],
            "default": "TOTAL",
            "description": "`PARTIAL` exige `minAmountCents` (acima de zero e até o máximo),\n`maxAmountCents` e `maxPayments` (de 1 a 999); `TOTAL` não aceita\nnenhum dos três. Na instalação Azify, o máximo precisa ser maior que\no mínimo.\n"
          },
          "minAmountCents": {
            "type": "integer",
            "format": "int64"
          },
          "maxAmountCents": {
            "type": "integer",
            "format": "int64"
          },
          "maxPayments": {
            "type": "integer",
            "minimum": 1,
            "maximum": 999
          },
          "kind": {
            "$ref": "#/components/schemas/BilletKind"
          },
          "finalBeneficiary": {
            "$ref": "#/components/schemas/BilletFinalBeneficiary"
          }
        }
      },
      "Beneficiary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "pix_key",
              "bank_account"
            ],
            "description": "Favorecido por chave Pix ou por conta bancária."
          },
          "name": {
            "type": "string",
            "description": "Por chave, o nome do DICT; por conta bancária, o nome digitado\n(não confirmado). Com o apelido como prefixo quando houver.\n"
          },
          "document": {
            "type": "string",
            "description": "Mascarado."
          },
          "bank": {
            "type": "string",
            "description": "Na conta bancária",
            "o ISPB.": null
          },
          "pixKey": {
            "type": "string",
            "description": "Vazio na conta bancária."
          },
          "masked": {
            "type": "string",
            "description": "Vazio na conta bancária."
          },
          "bankAccount": {
            "type": "object",
            "description": "Só no `kind: bank_account`. A conta sai mascarada e o documento\nnunca sai inteiro: para enviar, use o `beneficiaryId`.\n",
            "properties": {
              "ispb": {
                "type": "string",
                "examples": [
                  "18236120"
                ]
              },
              "branch": {
                "type": "string",
                "examples": [
                  "0001"
                ]
              },
              "accountMasked": {
                "type": "string",
                "examples": [
                  "••••••977"
                ]
              },
              "accountType": {
                "type": "string",
                "enum": [
                  "CHECKING",
                  "SAVINGS",
                  "PAYMENT",
                  "SALARY"
                ]
              },
              "holderName": {
                "type": "string",
                "description": "Como o cliente digitou."
              }
            }
          }
        }
      },
      "Dispute": {
        "type": "object",
        "description": "Contestação de Pix pelo cliente (MED, Mecanismo Especial de Devolução).\nNunca traz nome, documento nem conta do recebedor.\n",
        "required": [
          "id",
          "transactionId",
          "status",
          "fraudType",
          "amountCents",
          "openedAt",
          "updatedAt",
          "refundedCents",
          "refundAnnouncedCents",
          "canCancel"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "transactionId": {
            "type": "string",
            "description": "Id do Pix enviado contestado."
          },
          "protocol": {
            "type": "string",
            "description": "Protocolo da contestação no banco parceiro. Ausente enquanto a\nabertura não foi confirmada.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "OPENING",
              "IN_ANALYSIS",
              "REFUND_ANALYSIS",
              "CONCLUDED_REFUNDED",
              "CONCLUDED_PARTIAL",
              "CONCLUDED_NO_REFUND",
              "CONCLUDED_NOT_RECOGNIZED",
              "CONCLUDED",
              "CANCELLING",
              "CANCELLED",
              "NOT_REGISTERED"
            ],
            "description": "`OPENING` em abertura; `IN_ANALYSIS` o banco do recebedor analisa;\n`REFUND_ANALYSIS` a infração foi aceita e a devolução está em análise;\n`CONCLUDED_REFUNDED` e `CONCLUDED_PARTIAL` devolução total e parcial\naceitas; `CONCLUDED_NO_REFUND` infração aceita e devolução recusada\n(veja `refundRejectionReason`); `CONCLUDED_NOT_RECOGNIZED` o banco do\nrecebedor não reconheceu a fraude; `CONCLUDED` concluída sem\nresultado que se leia; `CANCELLING` e `CANCELLED` cancelamento;\n`NOT_REGISTERED` o banco não registrou (pode contestar de novo). O\nservidor pode acrescentar valores: ignore o que não conhecer.\n"
          },
          "fraudType": {
            "type": "string",
            "enum": [
              "SCAM",
              "ACCOUNT_TAKEOVER",
              "COERCION",
              "FRAUDULENT_ACCESS",
              "OTHER",
              "UNKNOWN"
            ]
          },
          "details": {
            "type": "string",
            "description": "O relato do cliente."
          },
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "O valor da transação contestada."
          },
          "openedAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "A última mudança do registro."
          },
          "analysisDeadline": {
            "type": "string",
            "format": "date-time",
            "description": "Prazo do banco do recebedor para analisar (7 dias corridos)."
          },
          "refundDeadline": {
            "type": "string",
            "format": "date-time",
            "description": "Prazo da análise da devolução, quando a infração foi aceita."
          },
          "infractionResult": {
            "type": "string",
            "enum": [
              "AGREED",
              "DISAGREED"
            ],
            "description": "Ausente enquanto não há análise."
          },
          "refundResult": {
            "type": "string",
            "enum": [
              "TOTALLY_ACCEPTED",
              "PARTIALLY_ACCEPTED",
              "REJECTED"
            ],
            "description": "Ausente enquanto não há análise da devolução."
          },
          "refundRejectionReason": {
            "type": "string",
            "enum": [
              "NO_BALANCE",
              "ACCOUNT_CLOSURE",
              "OTHER",
              "INVALID_REQUEST"
            ]
          },
          "refundedCents": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "O que de fato entrou na conta: a soma das devoluções recebidas\nligadas ao Pix desde a abertura.\n"
          },
          "refundAnnouncedCents": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "O total que o banco parceiro anunciou; pode estar à frente de `refundedCents`."
          },
          "canCancel": {
            "type": "boolean"
          },
          "cancelledAt": {
            "type": "string",
            "format": "date-time"
          },
          "concludedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MerchantSalesTotal": {
        "type": "object",
        "required": [
          "salesCount",
          "grossCents",
          "netCents"
        ],
        "properties": {
          "salesCount": {
            "type": "integer"
          },
          "grossCents": {
            "type": "integer",
            "format": "int64",
            "description": "Valor bruto das vendas."
          },
          "netCents": {
            "type": "integer",
            "format": "int64",
            "description": "Valor depois da taxa do adquirente."
          }
        }
      },
      "MerchantReceivableTotals": {
        "type": "object",
        "required": [
          "count",
          "grossCents",
          "netCents"
        ],
        "properties": {
          "count": {
            "type": "integer"
          },
          "grossCents": {
            "type": "integer",
            "format": "int64"
          },
          "netCents": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "MerchantDashboard": {
        "type": "object",
        "required": [
          "merchant",
          "today",
          "month",
          "dailySales",
          "futureReceivables",
          "balance",
          "asOf"
        ],
        "properties": {
          "merchant": {
            "type": "object",
            "required": [
              "id",
              "tradeName",
              "status",
              "cardPresentEnabled",
              "onlineEnabled",
              "lastSyncAt"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "tradeName": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "REGISTERING",
                  "PENDING",
                  "ACTIVE",
                  "DENIED",
                  "DISABLED"
                ],
                "description": "Credenciamento no adquirente. Só `ACTIVE` vende. `DISABLED` é o\ndescredenciado pelo banco: não vende mais, e as vendas e a\nagenda mostradas são as copiadas até o descredenciamento.\n"
              },
              "cardPresentEnabled": {
                "type": "boolean",
                "description": "Venda presencial habilitada."
              },
              "onlineEnabled": {
                "type": "boolean",
                "description": "Venda online habilitada."
              },
              "lastSyncAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Última cópia concluída do adquirente, pelo instante em que ela\ncomeçou: tudo o que aparece foi lido depois dele. Nula antes da\nprimeira. Quem vende muito pode ter a cópia feita em várias\nvoltas da rotina; enquanto ela não termina, este valor fica na\ncópia anterior (as vendas novas entram mesmo assim).\n"
              }
            }
          },
          "today": {
            "$ref": "#/components/schemas/MerchantSalesTotal"
          },
          "month": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantSalesTotal"
              },
              {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string",
                    "format": "date"
                  },
                  "to": {
                    "type": "string",
                    "format": "date"
                  }
                }
              }
            ]
          },
          "dailySales": {
            "type": "array",
            "description": "Um item por dia, do dia 1º do mês até hoje.",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/MerchantSalesTotal"
                },
                {
                  "type": "object",
                  "required": [
                    "date"
                  ],
                  "properties": {
                    "date": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              ]
            }
          },
          "futureReceivables": {
            "$ref": "#/components/schemas/MerchantReceivableTotals"
          },
          "balance": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "balanceCents": {
                "type": "integer",
                "format": "int64"
              },
              "heldCents": {
                "type": "integer",
                "format": "int64"
              },
              "asOf": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "asOf": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MerchantSale": {
        "type": "object",
        "required": [
          "id",
          "status",
          "method",
          "channel",
          "grossCents",
          "feeCents",
          "netCents",
          "installments",
          "authorizedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "AUTHORIZED",
              "SUCCEEDED",
              "FAILED",
              "CANCELED",
              "REFUNDED",
              "PARTIALLY_REFUNDED",
              "DISPUTED",
              "CHARGED_BACK"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "CREDIT",
              "DEBIT",
              "PIX",
              "BOLETO"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "CARD_PRESENT",
              "ONLINE"
            ],
            "description": "Presencial (maquininha) ou online (link)."
          },
          "grossCents": {
            "type": "integer",
            "format": "int64"
          },
          "feeCents": {
            "type": "integer",
            "format": "int64",
            "description": "Taxa do adquirente."
          },
          "netCents": {
            "type": "integer",
            "format": "int64"
          },
          "installments": {
            "type": "integer",
            "minimum": 1
          },
          "cardBrand": {
            "type": "string",
            "description": "Bandeira",
            "quando o adquirente informa.": null
          },
          "cardLast4": {
            "type": "string",
            "pattern": "^[0-9]{4}$",
            "description": "Os quatro últimos dígitos, quando o adquirente informa. Nunca o número do cartão."
          },
          "authorizedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MerchantReceivable": {
        "type": "object",
        "required": [
          "id",
          "status",
          "installment",
          "grossCents",
          "netCents",
          "expectedOn",
          "paidAt",
          "anticipated"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "SCHEDULED",
              "PAID",
              "CANCELED",
              "REFUNDED"
            ]
          },
          "installment": {
            "type": "integer",
            "minimum": 1
          },
          "grossCents": {
            "type": "integer",
            "format": "int64"
          },
          "netCents": {
            "type": "integer",
            "format": "int64"
          },
          "expectedOn": {
            "type": "string",
            "format": "date"
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "anticipated": {
            "type": "boolean"
          }
        }
      },
      "PlanQuota": {
        "type": "object",
        "description": "A cota de um serviço na cesta: `included` unidades por período pago\n(0 = não incluído) ou `unlimited`. `service` é o código do catálogo\n(`PIX_OUT`, `PIX_IN_QR`, `TED_OUT`, `INTERNAL_OUT`, `BILLET_ISSUE`,\n`BILLET_SETTLED`, `BILL_PAYMENT`, `TOPUP`; o servidor pode acrescentar\ncódigos: ignore o que não conhecer) e `label` o rótulo em português.\n",
        "required": [
          "service",
          "label",
          "included",
          "unlimited"
        ],
        "properties": {
          "service": {
            "type": "string",
            "examples": [
              "PIX_OUT"
            ]
          },
          "label": {
            "type": "string",
            "examples": [
              "Pix enviado"
            ]
          },
          "included": {
            "type": "integer",
            "minimum": 0
          },
          "unlimited": {
            "type": "boolean"
          }
        }
      },
      "SubscriptionUsage": {
        "type": "object",
        "description": "O período pago corrente e o uso da cesta por serviço.",
        "required": [
          "subscriptionId",
          "subscriptionStatus",
          "periodStart",
          "periodEnd",
          "status",
          "items",
          "feesPaidInPeriodCents"
        ],
        "properties": {
          "subscriptionId": {
            "type": "string"
          },
          "subscriptionStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "PAST_DUE",
              "DELINQUENT",
              "CANCELED"
            ],
            "description": "A situação da assinatura. `CANCELED` aparece enquanto o período já\npago da assinatura cancelada vale (a cesta segue até o fim dele).\n"
          },
          "periodStart": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "periodEnd": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Exclusivo (o próximo vencimento)."
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SUSPENDED",
              "NONE"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "service",
                "label",
                "included",
                "unlimited",
                "used",
                "remaining",
                "excessPrice"
              ],
              "properties": {
                "service": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "included": {
                  "type": "integer"
                },
                "unlimited": {
                  "type": "boolean"
                },
                "used": {
                  "type": "integer"
                },
                "remaining": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Nulo no ilimitado."
                },
                "excessPrice": {
                  "description": "A tarifa avulsa do excedente (a linha de `GET /v1/fees` do serviço). Nulo com `service_fees` desligada.",
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceFee"
                    }
                  ]
                }
              }
            }
          },
          "feesPaidInPeriodCents": {
            "type": "integer",
            "format": "int64",
            "description": "Tarifas avulsas cobradas da conta nos dias do período",
            "em centavos; zero com `service_fees` desligada.": null
          }
        }
      },
      "SubscriptionPlan": {
        "type": "object",
        "description": "Um plano à venda. Valores em centavos.",
        "required": [
          "id",
          "name",
          "description",
          "benefits",
          "period",
          "amountCents",
          "scheduledPrice",
          "quotas"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "examples": [
              "S4 Standard individual"
            ]
          },
          "description": {
            "type": "string"
          },
          "benefits": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "A cesta de benefícios, em frases curtas."
          },
          "period": {
            "type": "string",
            "enum": [
              "MONTHLY"
            ]
          },
          "amountCents": {
            "type": "integer",
            "format": "int64",
            "description": "O preço em vigor hoje."
          },
          "scheduledPrice": {
            "description": "O próximo preço já agendado, ou `null`.",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "amountCents",
                  "effectiveFrom"
                ],
                "properties": {
                  "amountCents": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  }
                }
              }
            ]
          },
          "quotas": {
            "type": "array",
            "description": "A composição da cesta vigente hoje, um item por serviço do catálogo.",
            "items": {
              "$ref": "#/components/schemas/PlanQuota"
            }
          },
          "planVersion": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "A vigência da composição mostrada em `quotas`: é o\n`expectedPlanVersion` da adesão. Nula quando o plano não tem\ncomposição (mande a string vazia). Servidores anteriores não a\nmandam (nem `scheduledQuotas`).\n"
          },
          "scheduledQuotas": {
            "type": "array",
            "description": "As composições já agendadas (a partir de DD/MM), da mais próxima à mais distante.",
            "items": {
              "type": "object",
              "required": [
                "effectiveFrom",
                "items"
              ],
              "properties": {
                "effectiveFrom": {
                  "type": "string",
                  "format": "date"
                },
                "items": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PlanQuota"
                  }
                }
              }
            }
          }
        }
      },
      "Subscription": {
        "type": "object",
        "description": "A assinatura da conta. `status`: `ACTIVE` (em dia), `PAST_DUE`\n(mensalidade vencida, ainda debitada até `retryUntil`), `DELINQUENT`\n(mensalidade não cobrada: o ciclo fica suspenso até o atendimento\nregularizar) ou `CANCELED`.\n",
        "required": [
          "id",
          "status",
          "plan",
          "billingDay",
          "startedAt",
          "nextDueDate",
          "nextAmountCents",
          "paidThrough",
          "pendingPlan",
          "currentCharge",
          "canceledAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "PAST_DUE",
              "DELINQUENT",
              "CANCELED"
            ]
          },
          "plan": {
            "type": "object",
            "required": [
              "id",
              "name",
              "description",
              "benefits"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "benefits": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "quotas": {
                "type": "array",
                "description": "A composição da cesta do plano vigente hoje (servidores anteriores não a mandam).",
                "items": {
                  "$ref": "#/components/schemas/PlanQuota"
                }
              }
            }
          },
          "billingDay": {
            "type": "integer",
            "minimum": 1,
            "maximum": 31,
            "description": "O dia do vencimento (o último do mês",
            "nos meses mais curtos). Nas adesões novas": null,
            "de 1 a 28.": null
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextDueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "O próximo vencimento. Nulo na cancelada e na `DELINQUENT`: com a\nmensalidade não cobrada o ciclo fica suspenso e nenhuma mensalidade\nnova é gerada até regularizar (a dívida está em `currentCharge`).\n"
          },
          "nextAmountCents": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "O valor que o próximo vencimento cobra: com troca de dia pendente,\na cobrança proporcional (igual a `billingDayChangeAmountCents`),\nnão a mensal cheia. Nulo na cancelada e na `DELINQUENT`.\n"
          },
          "paidThrough": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Até quando a última mensalidade paga cobre."
          },
          "pendingPlan": {
            "description": "A troca de plano agendada (pelo cliente ou pelo atendimento), ou\n`null`. `effectiveFrom` é o vencimento da primeira mensalidade no\nplano novo; `amountCents`, essa mensalidade (nulo sem preço na\ndata). `canUndo` diz se o desfazer é aceito agora e\n`undoAmountCents`, para quanto a mensalidade volta nessa data (o\n`expectedAmountCents` do desfazer). Servidores anteriores não\nmandam os três.\n",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "id",
                  "name",
                  "effectiveFrom"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "amountCents": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "format": "int64"
                  },
                  "canUndo": {
                    "type": "boolean"
                  },
                  "undoAmountCents": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "format": "int64"
                  }
                }
              }
            ]
          },
          "currentCharge": {
            "description": "A mensalidade em aberto ou, sem ela, a mais recente.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/SubscriptionCharge"
              },
              {
                "type": "null"
              }
            ]
          },
          "canceledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "planChangeFrom": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Quando uma troca de plano pedida agora (`POST\n/v1/subscription/plan-change`) passaria a valer. Nulo quando a troca\nnão é aceita agora (`planChangeBlockedReason` diz por quê); o\ndesfazer tem a sua própria resposta, `pendingPlan.canUndo`. Não\nestá em `required`: servidores anteriores não o mandam.\n"
          },
          "planChangeBlockedReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "FEATURE_DISABLED",
              "ACCOUNT_NOT_ACTIVE",
              "SUBSCRIPTION_CANCELED",
              "UNCOLLECTED_CHARGES",
              "ALREADY_GENERATED",
              null
            ],
            "description": "Por que a troca não é aceita agora: a funcionalidade desligada, a\nconta não ativa, a assinatura cancelada, mensalidade não cobrada\n(ou a assinatura inadimplente), ou a mensalidade da troca agendada\njá gerada. O servidor pode acrescentar valores: trate o\ndesconhecido como \"não disponível agora\".\n"
          },
          "pendingBillingDay": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 28,
            "description": "O dia de cobrança pedido pelo cliente (`POST\n/v1/subscription/billing-day`), ainda não aplicado; nulo sem\ntroca pendente. Não está em `required`: servidores anteriores não\no mandam (nem os três campos seguintes).\n"
          },
          "billingDayChangeFrom": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Quando a troca de dia pendente vale (V1, o vencimento da cobrança proporcional)."
          },
          "billingDayChangeAmountCents": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "A cobrança proporcional da troca de dia pendente, em centavos; nulo sem preço na data."
          },
          "graceUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Com a assinatura `PAST_DUE`: o último dia em que a cesta ainda vale\ne o débito é tentado (D + carência − 1). No dia seguinte sem\npagamento a mensalidade fica não cobrada e a cesta é desativada.\n"
          },
          "canChangeBillingDay": {
            "type": "boolean",
            "description": "Se uma troca de dia pedida agora (`POST /v1/subscription/billing-day`)\né aceita. Falso com `billingDayChangeBlockedReason`. Servidores\nanteriores não o mandam (trate ausente como falso).\n"
          },
          "billingDayChangeBlockedReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "FEATURE_DISABLED",
              "ACCOUNT_NOT_ACTIVE",
              "SUBSCRIPTION_CANCELED",
              "UNCOLLECTED_CHARGES",
              "ALREADY_CHANGED_THIS_PERIOD",
              "ALREADY_SCHEDULED",
              null
            ],
            "description": "Por que a troca de dia não é aceita agora: a funcionalidade\ndesligada, a conta não ativa, a assinatura cancelada, mensalidade\nnão cobrada, no boleto ou a assinatura inadimplente, o dia já\nalterado neste período (a cobrança proporcional já gerada), ou\numa troca ainda pendente (`pendingBillingDay`; desfaça-a antes\nde pedir outra). O servidor pode acrescentar valores: trate o\ndesconhecido como \"não disponível agora\".\n"
          }
        }
      },
      "SubscriptionCharge": {
        "type": "object",
        "description": "A mensalidade de uma competência. `status`: `PENDING` (a vencer ou\nvencendo hoje), `OVERDUE` (vencida, ainda debitada até `retryUntil`),\n`BILLET_ISSUING` e `BILLET_ISSUED` (boleto de depósito na própria conta\nem emissão ou emitido: o pagamento cai na conta e quita a mensalidade),\n`PAID_DEBIT`, `PAID_BILLET`, `UNCOLLECTED` (não cobrada), `WAIVED`\n(dispensada pelo atendimento) ou `CANCELED` (a assinatura foi cancelada,\nou a mensalidade foi pulada sem cobrança porque o prazo de débito passou\nsem o cliente poder pagar — conta bloqueada ou serviço indisponível). O\nservidor pode acrescentar valores: ignore o que não conhecer.\n",
        "required": [
          "id",
          "competence",
          "dueDate",
          "amountCents",
          "status",
          "retryUntil",
          "paidAt",
          "transactionId",
          "billet"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "competence": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}$",
            "examples": [
              "2026-11"
            ]
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "amountCents": {
            "type": "integer",
            "format": "int64"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "OVERDUE",
              "BILLET_ISSUING",
              "BILLET_ISSUED",
              "PAID_DEBIT",
              "PAID_BILLET",
              "UNCOLLECTED",
              "WAIVED",
              "CANCELED"
            ]
          },
          "retryUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Até quando o débito é tentado de novo (D + carência − 1); só em `PENDING` e `OVERDUE`."
          },
          "kind": {
            "type": "string",
            "enum": [
              "MONTHLY",
              "PRORATA"
            ],
            "description": "`PRORATA` é a cobrança proporcional da troca de dia: vence no dia\nantigo e cobre de `periodStart` até o dia novo. Não está em\n`required`: servidores anteriores não o mandam (nem `periodStart`).\n"
          },
          "periodStart": {
            "type": "string",
            "format": "date",
            "description": "O início do período coberto (o vencimento",
            "na mensal).": null
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "transactionId": {
            "type": [
              "string",
              "null"
            ],
            "description": "A transação `TARIFF` da mensalidade paga."
          },
          "billet": {
            "description": "O boleto da mensalidade, ou `null`. A linha digitável e o Pix copia\ne cola só vêm com o título `REGISTERED` e até o vencimento\n(`dueDate`, no fuso da operação): depois dele o boleto não se paga.\nSem pagamento, ele é baixado alguns dias úteis depois do vencimento\ne a mensalidade volta ao débito do saldo.\n",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "id",
                  "status",
                  "amountCents",
                  "dueDate",
                  "digitableLine",
                  "pixPayload"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string"
                  },
                  "amountCents": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "dueDate": {
                    "type": "string",
                    "format": "date"
                  },
                  "digitableLine": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "pixPayload": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            ]
          }
        }
      },
      "MarketplaceBanner": {
        "type": "object",
        "required": [
          "id",
          "title",
          "text",
          "destination",
          "route",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "maxLength": 60
          },
          "text": {
            "type": "string",
            "maxLength": 200
          },
          "destination": {
            "type": "string",
            "enum": [
              "nenhum",
              "interna",
              "externa"
            ]
          },
          "route": {
            "type": [
              "string",
              "null"
            ],
            "description": "Chave da tela (destino `interna`); nulo nos outros."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link https (destino `externa`); nulo nos outros."
          },
          "placements": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "inicio",
                "beneficios",
                "cartoes",
                "indique",
                "pix",
                "extrato",
                "plano"
              ]
            },
            "description": "Telas em que o banner aparece: `inicio` (Início), `beneficios`\n(Benefícios), `cartoes` (Cartões), `indique` (Indique amigos),\n`pix` (área Pix), `extrato` (Extrato), `plano` (Meu plano). Só os\nlugares cuja tela está ligada. Ignore o lugar que não conhecer.\nServidor anterior a este campo não o manda: trate ausente como\n`[inicio, beneficios]`, onde todo banner aparecia.\n"
          },
          "theme": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "marca",
              "escuro",
              "claro",
              "verde",
              "laranja",
              "roxo",
              "custom",
              null
            ],
            "description": "O estilo do banner: uma paleta pronta (`marca`, `escuro`,\n`claro`, `verde`, `laranja`, `roxo`) ou `custom` (cores escolhidas\npela instituição). Nulo: sem estilo, o visual padrão do app. Use\nsempre `backgroundColor` e `textColor`, que já vêm resolvidas\n(inclusive as da paleta); ignore o nome que não conhecer.\nServidor anterior a este campo não o manda: trate ausente como\nnulo.\n"
          },
          "backgroundColor": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-F]{6}$",
            "description": "Cor de fundo, `#RRGGBB`, igual no modo claro e no escuro; nula sem\nestilo. Tem contraste de pelo menos 4,5:1 (WCAG AA) com\n`textColor`.\n"
          },
          "textColor": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-F]{6}$",
            "description": "Cor do título e do texto, `#RRGGBB`; nula sem estilo."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Caminho relativo à API da imagem do banner\n(`/v1/marketplace/banners/{id}/image?v=…`), ou nulo sem imagem.\nSome o endereço da API à frente. A imagem é decorativa (texto\nalternativo vazio). Servidor anterior a este campo não o manda:\ntrate ausente como nulo.\n"
          },
          "imagePosition": {
            "type": "string",
            "enum": [
              "lado",
              "fundo"
            ],
            "description": "Onde a imagem fica: `lado` (ao lado do texto; em telas estreitas,\nem cima) ou `fundo` (cobrindo o banner, sob uma camada que garante\na leitura do texto). Sem imagem, não tem efeito. Ausente: `lado`.\n"
          }
        }
      },
      "MarketplaceOffer": {
        "type": "object",
        "required": [
          "id",
          "title",
          "description",
          "kind",
          "benefit",
          "couponCode",
          "url",
          "endsOn",
          "partner"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "kind": {
            "type": "string",
            "enum": [
              "desconto",
              "cupom",
              "cashback_informativo"
            ],
            "description": "`cashback_informativo` é o cashback do parceiro, só informado: o\nbanco não credita valor.\n"
          },
          "benefit": {
            "type": "string",
            "maxLength": 120,
            "description": "Texto curto do benefício (\"15% de desconto\")."
          },
          "couponCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Código do cupom (só no tipo `cupom`)."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link https do parceiro",
            "quando há.": null
          },
          "endsOn": {
            "type": "string",
            "format": "date",
            "description": "Último dia da oferta",
            "inclusive.": null
          },
          "partner": {
            "type": "object",
            "required": [
              "id",
              "name",
              "description"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "logoUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Caminho relativo à API do logo do parceiro\n(`/v1/marketplace/partners/{id}/logo?v=…`), ou nulo sem logo.\nServidor anterior a este campo não o manda: trate ausente\ncomo nulo.\n"
              }
            }
          }
        }
      },
      "Marketplace": {
        "type": "object",
        "required": [
          "today",
          "storeUrl",
          "banners",
          "categories"
        ],
        "properties": {
          "today": {
            "type": "string",
            "format": "date",
            "description": "Data de referência da vigência",
            "no fuso da operação.": null
          },
          "storeUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link da loja",
            "ou nulo.": null
          },
          "banners": {
            "type": "array",
            "maxItems": 70,
            "description": "Até 10 por lugar (`placements`).",
            "items": {
              "$ref": "#/components/schemas/MarketplaceBanner"
            }
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "category",
                "offers"
              ],
              "properties": {
                "category": {
                  "type": "string",
                  "enum": [
                    "farmacia",
                    "mobilidade",
                    "saude",
                    "compras",
                    "servicos",
                    "outros"
                  ]
                },
                "offers": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MarketplaceOffer"
                  }
                }
              }
            }
          }
        }
      },
      "FeePrice": {
        "type": "object",
        "description": "Um preço da tabela de tarifas, com a vigência.",
        "required": [
          "pricing",
          "amountCents",
          "percentBps",
          "minCents",
          "maxCents",
          "effectiveFrom"
        ],
        "properties": {
          "pricing": {
            "type": "string",
            "enum": [
              "FIXED",
              "PERCENT"
            ]
          },
          "amountCents": {
            "type": "integer",
            "minimum": 0,
            "description": "Preço fixo, em centavos (`FIXED`); 0 é isento."
          },
          "percentBps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "description": "Pontos-base (`PERCENT`): 99 = 0,99 %."
          },
          "minCents": {
            "type": "integer",
            "minimum": 0,
            "description": "Piso do percentual, em centavos; 0 = sem piso."
          },
          "maxCents": {
            "type": "integer",
            "minimum": 0,
            "description": "Teto do percentual, em centavos; 0 = sem teto."
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "ServiceFee": {
        "type": "object",
        "description": "Uma linha da tabela de tarifas para o público da conta.",
        "required": [
          "service",
          "name",
          "label",
          "audience",
          "pricing",
          "amountCents",
          "percentBps",
          "minCents",
          "maxCents",
          "exemptByRule",
          "scheduled"
        ],
        "properties": {
          "service": {
            "type": "string",
            "enum": [
              "PIX_OUT",
              "PIX_IN_QR",
              "TED_OUT",
              "INTERNAL_OUT",
              "BILLET_ISSUE",
              "BILLET_SETTLED",
              "BILL_PAYMENT",
              "TOPUP"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Pix enviado"
            ]
          },
          "label": {
            "type": "string",
            "description": "A linha do extrato.",
            "examples": [
              "Tarifa · Pix enviado"
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "PF",
              "PJ"
            ]
          },
          "pricing": {
            "type": "string",
            "enum": [
              "FIXED",
              "PERCENT",
              "FREE"
            ],
            "description": "`FREE`: sem preço cadastrado, preço zero ou isento por norma."
          },
          "amountCents": {
            "type": "integer",
            "minimum": 0
          },
          "percentBps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "minCents": {
            "type": "integer",
            "minimum": 0
          },
          "maxCents": {
            "type": "integer",
            "minimum": 0
          },
          "exemptByRule": {
            "type": "boolean",
            "description": "Isento por norma para este público (sempre, ou as `freeFirstPerMonth` primeiras do mês civil)."
          },
          "freeFirstPerMonth": {
            "type": "integer",
            "minimum": 0
          },
          "exemptionRule": {
            "type": "string",
            "examples": [
              "Res. BCB 19/2020, art. 3º, I"
            ]
          },
          "scheduled": {
            "description": "O próximo preço já agendado, ou nulo.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/FeePrice"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "FeeTable": {
        "type": "object",
        "required": [
          "audience",
          "items"
        ],
        "properties": {
          "audience": {
            "type": "string",
            "enum": [
              "PF",
              "PJ"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceFee"
            }
          }
        }
      },
      "FeeQuote": {
        "type": "object",
        "required": [
          "service",
          "label",
          "amountCents",
          "feeCents",
          "reason",
          "coveredByPlan",
          "planUsage",
          "totalCents"
        ],
        "properties": {
          "service": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "amountCents": {
            "type": "integer",
            "minimum": 1
          },
          "feeCents": {
            "type": "integer",
            "minimum": 0
          },
          "reason": {
            "type": "string",
            "enum": [
              "EXEMPT_BY_RULE",
              "COVERED_BY_PLAN",
              "TABLE",
              "FREE"
            ]
          },
          "coveredByPlan": {
            "type": "boolean"
          },
          "planUsage": {
            "description": "O consumo da cesta. Com `coveredByPlan: true`, é o consumo se esta\nexecução for coberta (\"7 de 10\"). Com `coveredByPlan: false`, vem\nsó quando a conta tem cota para o serviço no período e ela acabou\n(`used == included`): a franquia está esgotada e a tabela decide.\nNulo fora da cesta (sem plano, sem cota para o serviço, isento por\nnorma).\n",
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "used",
                  "included",
                  "unlimited"
                ],
                "properties": {
                  "used": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "included": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "unlimited": {
                    "type": "boolean"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "totalCents": {
            "type": "integer",
            "minimum": 1,
            "description": "`amountCents` + `feeCents`: o total debitado."
          }
        }
      },
      "TransactionFee": {
        "type": "object",
        "description": "A tarifa de uma operação, no detalhe.",
        "required": [
          "service",
          "label",
          "feeCents",
          "reason",
          "status"
        ],
        "properties": {
          "service": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "feeCents": {
            "type": "integer",
            "minimum": 0
          },
          "reason": {
            "type": "string",
            "enum": [
              "EXEMPT_BY_RULE",
              "COVERED_BY_PLAN",
              "TABLE",
              "FREE"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "CHARGED",
              "PENDING",
              "REVERSED",
              "FAILED"
            ],
            "description": "`PENDING`: tarifa de entrada ainda não cobrada (sem saldo, ou com\na conta bloqueada: é cobrada quando a conta volta a ficar ativa,\npelo preço e pela cesta do dia da operação). `REVERSED`: estornada\ncom a operação (ou pelo backoffice). `FAILED`: não cobrada e na\nfila do operador (sem saldo depois de 7 dias de tentativas, conta\nencerrada, ou operação liquidada depois do estorno).\n`CHARGED`: cobrada (ou coberta pela cesta, com `feeCents` 0).\n"
          },
          "transactionId": {
            "type": "string",
            "description": "O lançamento da tarifa",
            "quando houve.": null
          }
        }
      },
      "TopUpOperator": {
        "type": "object",
        "required": [
          "id",
          "name",
          "valuesCents"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "valuesCents": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            }
          }
        }
      },
      "TopUp": {
        "type": "object",
        "description": "Uma recarga de celular. `transaction` é a reserva do valor, com a tarifa `TOPUP` quando houve.",
        "required": [
          "id",
          "status",
          "amountCents",
          "phone",
          "phoneMasked",
          "phoneFormatted",
          "operatorId",
          "operatorName",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "O id da transação da reserva."
          },
          "status": {
            "type": "string",
            "description": "A situação da transação da reserva (`PROCESSING`, `COMPLETED`, `FAILED`, `HELD_FOR_REVIEW`…)."
          },
          "amountCents": {
            "type": "integer",
            "minimum": 1
          },
          "phone": {
            "type": "string",
            "description": "Inteiro",
            "porque quem lê é o titular que o digitou.": null
          },
          "phoneMasked": {
            "type": "string"
          },
          "phoneFormatted": {
            "type": "string"
          },
          "operatorId": {
            "type": "string"
          },
          "operatorName": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "transaction": {
            "description": "A transação da reserva, com `feeCents`, `feeTransactionId`, `feeReason` e `feeStatus` quando a tarifa `TOPUP` foi decidida.",
            "allOf": [
              {
                "$ref": "#/components/schemas/Transaction"
              }
            ]
          }
        }
      }
    }
  }
}