{
  "api_version": "v1",
  "contract_date": "2026-08-29",
  "notice_days": 90,
  "coexistence_months": 12,
  "kinds": [
    "quebra",
    "compativel",
    "correcao",
    "depreciacao"
  ],
  "changes": [
    {
      "date": "2026-08-29",
      "kind": "compativel",
      "area": "api",
      "title": "impedidos_apostar: a base entrou no ar, e a cobertura anunciada encolheu para o que ela é",
      "detail": "O módulo deixou de sair pendente por falta de base: a lista de agentes públicos do setor de apostas do Portal da Transparência está carregada e o módulo passa a devolver veredito, com coverage e dataset_versions como prova de diligência. Na mesma entrega a cobertura anunciada encolheu, e é a parte que importa para quem já integrou: atleta e árbitro SAÍRAM. A CBF não publica base para conferência automática, e as duas fontes dela passam a viajar em dataset_versions como disabled, ou seja, declaradamente não consultadas, em vez de aparecerem como cobertura. Preferimos dizer o que não cobrimos a listar uma fonte que não conseguimos conferir. Nada muda no formato do payload nem no preço; muda o que a resposta afirma.",
      "sunset_on": null
    },
    {
      "date": "2026-08-28",
      "kind": "compativel",
      "area": "api",
      "title": "Quatro módulos novos: forense de documento, rede de fraude, documento de viagem e cadeia societária",
      "detail": "Quatro módulos novos no catálogo, cada um com bloco próprio em check_details. Forense de documento pericia o ARQUIVO que a verificação de identidade já capturou (ferramenta que o gerou, datas, revisões de PDF, recaptura de tela, recorte e colagem), sem pedir foto nova. Detecção de rede de fraude mostra se quem está se verificando está ligado a outras contas SUAS, por aparelho, rede, e-mail, telefone, rosto ou pela conta que você informa, e a rede é sempre a sua. Documento de viagem lê a zona de leitura mecânica (MRZ) de passaportes e carteiras estrangeiras no padrão ICAO 9303, com todos os dígitos verificadores, e ocupa o lugar da verificação de identidade no mesmo flow. Cadeia societária sobe o quadro nível a nível quando um sócio é outra empresa, até as pessoas naturais, com o percentual acumulado. Nenhum dos quatro reprova sozinho: um achado leva a verificação para revisão humana com a evidência. A cadeia societária é cobrada por empresa efetivamente subida, e o teto por verificação é definido por você no flow.",
      "sunset_on": null
    },
    {
      "date": "2026-08-28",
      "kind": "compativel",
      "area": "api",
      "title": "policy.ubo_max_paid_nodes na criação de sessão, e dois códigos de erro novos",
      "detail": "A política por sessão ganhou a chave ubo_max_paid_nodes, o teto de empresas da cadeia societária que aquela verificação pode subir. Ela só APERTA o teto do flow: pedir mais que ele responde 422 policy_ubo_cap_above_flow, nunca um corte silencioso, porque gasto só sobe por decisão registrada e auditada no flow. Na mesma passada entrou no spec o 422 policy_module_not_in_flow, que já existia e não estava publicado: política para um módulo que o flow não tem é recusada com nome, nunca aceita e ignorada. As duas regras valem igual na criação de sessão e na emissão de link hospedado.",
      "sunset_on": null
    },
    {
      "date": "2026-08-28",
      "kind": "compativel",
      "area": "api",
      "title": "Link de verificação hospedado publicado no contrato",
      "detail": "POST /v1/verification-links passa a constar do spec OpenAPI e da documentação. A rota já estava no ar e é a quarta da superfície da chave secreta: para quem não vai montar o widget, nós hospedamos a página e você entrega a url ao titular. O link vive horas, a sessão só nasce no resgate e vive 15 minutos, o token do link volta em claro uma vez só e não é reexibido, e emitir link não cobra nada: quem cobra é a verificação que nascer do resgate. Nada mudou no comportamento; o que mudou é que o contrato público parou de omitir a rota.",
      "sunset_on": null
    },
    {
      "date": "2026-08-27",
      "kind": "compativel",
      "area": "api",
      "title": "Módulo midia_adversa: mídia adversa (negative news)",
      "detail": "Módulo novo no catálogo (40 centavos), com bloco próprio em check_details. Confere o nome lido do documento contra um corpus aberto de notícias mantido por nós. Todo casamento é por nome e a resposta sempre declara o grau de confiança (name_match forte ou possível homônimo) e a similaridade. Um hit forte nunca reprova sozinho: leva a verificação para revisão humana com a evidência (fonte, data e link). Exige Verificação de Identidade, Face Match e Liveness no mesmo flow, e desliga a renovação de sessão pelo widget. Enquanto o corpus não estiver carregado, toda checagem sai pendente (indeterminado), vai a review e não é cobrada, nunca um falso nada consta.",
      "sunset_on": null
    },
    {
      "date": "2026-08-25",
      "kind": "compativel",
      "area": "api",
      "title": "Módulo impedidos_apostar: vedação de apostas (Lei 14.790/2023)",
      "detail": "Módulo novo no catálogo (45 centavos), com bloco próprio em check_details e a chave opcional policy.allow_betting_ban na criação da sessão com sk_. Confere o CPF e o nome do documento contra as listas públicas de impedidos (CBF BID, arbitragem, servidores do setor) e devolve coverage e dataset_versions como prova de diligência. Exige Verificação de Identidade, Face Match e Liveness no mesmo flow, e desliga a renovação de sessão pelo widget. Nunca reprova sozinho: um sinal leva a verificação para review. Enquanto o feed das fontes não estiver carregado, toda checagem sai pendente, vai a review e não é cobrada.",
      "sunset_on": null
    },
    {
      "date": "2026-08-22",
      "kind": "compativel",
      "area": "api",
      "title": "schema_version no corpo do webhook, e a garantia de entrega publicada",
      "detail": "O envelope de todo evento passa a carregar schema_version, a versão da FORMA do corpo. Hoje ela vale 1 e não sobe por adição compatível: campo novo, módulo novo em check_details e valor novo em enum de saída continuam com schema_version 1. Ela só muda se um campo mudar de tipo ou de significado, e isso já exigiria uma versão nova de caminho. É campo NOVO na resposta, ou seja, adição compatível pela regra desta mesma política. Junto vai para a documentação, por escrito, a garantia de entrega que já praticávamos: pelo menos uma vez, com repetição possível, e o id do evento como chave de deduplicação do seu lado.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "api",
      "title": "Módulo pep_sancoes: PEP e listas restritivas",
      "detail": "Módulo novo no catálogo (40 centavos), com bloco próprio em check_details e a chave opcional policy.allow_pep na criação da sessão com sk_. Exige Verificação de Identidade, Face Match e Liveness no mesmo flow. Nunca reprova sozinho: um sinal leva a verificação para review. Chave de política desconhecida é 400, que é a validação nova valendo só para um parâmetro novo.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "api",
      "title": "Módulo email_otp e as duas rotas de OTP da sessão",
      "detail": "Validação de e-mail por código (10 centavos). Entram POST /v1/verification-sessions/{id}/otp e POST /v1/verification-sessions/{id}/otp/verify, autenticadas pela própria sessão do widget, e o bloco email_otp em check_details. Nenhuma rota nova de sk_.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "api",
      "title": "Módulos telefone e sms_otp",
      "detail": "Validação de telefone contra a base oficial de numeração, sem envio (15 centavos), e validação por SMS (115 centavos). O sms_otp nasce INATIVO no catálogo, à espera do contrato de SMS: até o flip por migration nova ele não aparece no catálogo público nem entra em flow.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "correcao",
      "area": "api",
      "title": "Cobrança do sms_otp unificada no fecho da verificação",
      "detail": "O sms_otp debitava no despacho da mensagem, fora do caminho por onde o resto do catálogo cobra. Passou a cobrar no fecho, como todos os outros módulos. Nenhum campo de resposta mudou: o que muda é o momento do débito no seu saldo.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "widget",
      "title": "Widget em português, inglês e espanhol",
      "detail": "O texto que a pessoa vê passa a existir em pt-BR, en-US e es-ES. O idioma sai da dica do host (mount({ locale: 'en' }) ou data-locale), senão do navegador, senão pt-BR. O widget já tolera um campo de idioma vindo do servidor, que ainda NÃO existe na API: quando ele nascer, o bundle antigo continua funcionando, e idioma desconhecido mantém a língua que já estava.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "painel",
      "title": "Recuperação de senha do painel",
      "detail": "Telas /esqueci-senha e /redefinir-senha, com link por e-mail de uso único e validade curta. A resposta é sempre a mesma, exista a conta ou não, para a tela não virar consulta de quem tem cadastro. Nada muda na API REST nem no widget.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "compativel",
      "area": "site",
      "title": "Preços e comparativos públicos",
      "detail": "As páginas /precos e /comparar passam a existir, servidas do catálogo público de preços, não de uma tabela escrita à mão. Módulo que não está ativo no catálogo aparece como Em breve, nunca com preço de venda.",
      "sunset_on": null
    },
    {
      "date": "2026-08-21",
      "kind": "depreciacao",
      "area": "api",
      "title": "Venda do módulo idade pausada",
      "detail": "O módulo idade saiu do catálogo público e deixou de ser vendável, à espera de decisão do dono sobre religar a venda. A saída foi no mesmo dia do aviso, sem os 90 dias que a política de versão passa a exigir daqui em diante, porque não havia nenhuma integração ativa para avisar. A partir desta publicação, saída de módulo do catálogo cumpre o prazo.",
      "sunset_on": "2026-08-21"
    },
    {
      "date": "2026-08-09",
      "kind": "quebra",
      "area": "api",
      "title": "Módulo ocr renomeado para cpf_ocr",
      "detail": "A leitura de documento de pessoa passou a se chamar cpf_ocr, por simetria com o cnpj_ocr do KYB. Quem lia check_details procurando module igual a ocr deixou de encontrar o bloco. O histórico foi renomeado junto, então verificações antigas também aparecem como cpf_ocr.",
      "sunset_on": null
    },
    {
      "date": "2026-08-06",
      "kind": "compativel",
      "area": "api",
      "title": "Módulos idade e face_unica",
      "detail": "Verificação de idade e detecção de múltiplas contas, ambos rodando sobre a selfie já capturada: zero passo novo no widget. Entram como valores novos no catálogo de módulos e como blocos novos em check_details.",
      "sunset_on": null
    },
    {
      "date": "2026-08-05",
      "kind": "quebra",
      "area": "api",
      "title": "Módulo socios extinto, cnpj vira cnpj_socios",
      "detail": "O quadro societário já vinha na mesma resposta do módulo de CNPJ, então o módulo socios era redundante e foi removido; o módulo cnpj passou a se chamar cnpj_socios. Flow que pedia socios perdeu o módulo, e o preço do flow foi recalculado pela soma dos módulos restantes.",
      "sunset_on": null
    },
    {
      "date": "2026-08-02",
      "kind": "quebra",
      "area": "api",
      "title": "Módulos de validação de CPF renomeados",
      "detail": "cpf virou cpf_contatos, cpf_k virou cpf_enderecos, cpf_e virou cpf_receita e cpf_i virou cpf_empresas. Os nomes antigos eram códigos de pacote do nosso fornecedor de dados, sem significado para quem integra. O histórico de verificações foi renomeado junto.",
      "sunset_on": null
    },
    {
      "date": "2026-07-28",
      "kind": "quebra",
      "area": "api",
      "title": "Combos de preço removidos: preço do flow é a soma dos módulos",
      "detail": "As tabelas de combo saíram e o preço de um flow passou a ser sempre a soma dos preços unitários dos módulos escolhidos. Além de simplificar, isso corrigiu preço gravado errado: quando a lista de módulos casava inteira com uma chave de combo, o combo vencia a soma e cobrava mais caro. Os preços dos flows existentes foram recalculados.",
      "sunset_on": null
    },
    {
      "date": "2026-07-27",
      "kind": "compativel",
      "area": "api",
      "title": "Módulo cnpj_ocr: leitura do documento da empresa",
      "detail": "OCR do comprovante de CNPJ, separado do OCR de documento de pessoa. KYC e KYB viram módulos independentes e podem conviver no mesmo flow.",
      "sunset_on": null
    },
    {
      "date": "2026-07-01",
      "kind": "compativel",
      "area": "api",
      "title": "Status review nas verificações",
      "detail": "O enum de status ganhou review, para separar revisão humana de pending, que significa em processamento. Este é o exemplo canônico de adição compatível: valor novo em enum de saída. Integração que tratava status desconhecido como erro precisou tolerar o valor novo.",
      "sunset_on": null
    },
    {
      "date": "2026-06-29",
      "kind": "quebra",
      "area": "api",
      "title": "Widget autentica por client_secret da sessão, fim da chave publicável",
      "detail": "O widget passou a autenticar pelo segredo escopado a UMA sessão, em vez de uma chave pública de conta. A chave publicável pk_ saiu do produto: hoje ela não parseia e devolve 401. Trocar o segredo global pelo segredo de sessão foi a mudança que tirou credencial de conta do navegador.",
      "sunset_on": null
    },
    {
      "date": "2026-06-24",
      "kind": "compativel",
      "area": "api",
      "title": "Publicação da API sob /v1",
      "detail": "Primeira versão do contrato HTTP, montada no caminho /v1. É o marco zero deste changelog: tudo acima aconteceu dentro desta mesma versão de caminho.",
      "sunset_on": null
    }
  ]
}