Documentação

Changelog

Todas as mudanças da API e do Servidor MCP em ordem cronológica: o que entrou, o que mudou de comportamento e o que foi descontinuado.

Política de versionamento

Mudanças não-breaking (novos campos opcionais, novos eventos de webhook) não incrementam a versão principal. Mudanças breaking são comunicadas por e-mail aos administradores das organizações com pelo menos 30 dias de antecedência.

2026-08-11 — O toxicológico deixa de afirmar exame que não existe

Três naturezas diferentes numa entrada só. A terceira é a que pode exigir mudança do seu lado.

Mudança de valor em campo publicado

  • hasExam era sempre true — em details.toxicological, diagnosis.toxicological e normalized.toxicological, que carregam o mesmo valor. Se você integrou antes de details existir, a mudança te alcança pelo diagnosis.

    O campo não era calculado: toda resposta afirmava que existia exame toxicológico, inclusive quando a própria resposta dizia que a categoria não exige um, ou que a consulta falhou. Agora ele segue a evidência — uma data de coleta, uma data de validade, ou uma situação que julgue um exame.

    Se você trata hasExam como "o condutor fez o exame", isso passa a ser verdade agora e não era antes. Se você o usa como condição para não alertar alguém, revise: para condutor de categoria C/D/E o campo era o que suprimia o alerta.

  • status da seção toxicológica passa a ser unknown (era success) quando não há exame e a categoria não exige um. Vale nos três caminhos acima: eles reportavam success porque o hasExam fixo mantinha o caso fora do ramo que já existia para tratá-lo.

  • details.personal.status era success fixo, mesmo num relatório sem CNH emitida com todos os campos vazios. Agora reflete a cobertura real: unknown quando a consulta não trouxe campo nenhum.

Aditivo

  • details.toxicological.situationStatus ganha not_applicable e unavailable, que antes caíam em unknown. O primeiro é "a categoria não exige o exame"; o segundo é "a consulta falhou". A distinção só existe aqui: no veredito, um exame exigido e não comprovado continua resolvendo para BLOCKED — errar para o lado de sinalizar o condutor, e não para o de liberá-lo. O campo é string no contrato, então valores novos não quebram nada; trate o desconhecido com um default.

  • Duas correções de leitura no mesmo campo: uma situação irregular era classificada como regular, e a grafia real de pendência ("com pendências") nunca casava e virava unknown.

Correção de documentação — a API já respondia assim

  • details e diagnosis não são null "enquanto PROCESSING", como esta página e outras três afirmavam. Eles acompanham a existência do dado. Um relatório parcialmente entregue reporta status: "PROCESSING" e já traz os dois preenchidos — é o corpo do webhook report.partially_delivered. Um handler com if (status !== "DELIVERED") return; está descartando relatório com dado. Teste diagnosis !== null / details !== null.
  • wait_for_report: a entrega leva cerca de 190s na mediana, então o padrão de 120s retorna PROCESSING na maioria das chamadas. Isso é o resultado correto da ferramenta, não uma falha.
  • O 202 de criação devolve os sete campos de driver, não só id e cpf.

2026-08-06 — details: paridade de campos com o relatório

  • Novo campo opcional details, com as seções que o diagnosis nunca carregou. O diagnosis responde qual é o veredito; details traz o registro por trás dele, para quem precisa reconstruir a tela e não só a conclusão:

    SeçãoO que passa a vir
    details.personalnome, CPF, nascimento, filiação, documento de identidade, naturalidade, nacionalidade, gênero
    details.licenseos 14 campos da habilitação — RENACH, nº do formulário, 1ª habilitação, emissão, local, situação, EAR
    details.medicalExamsos 11 campos de cada exame, incluindo categoria permitida, cidade e UF
    details.toxicologicalacrescenta data de uso, situação e o status parseado
    details.coursesa seção inteira: cursos com período, carga horária, modalidade, validade e local
    details.stateLookupsaldo oficial de pontos (com inferredPoints e hasPointsDiscrepancy) e o histórico completo de multas, com status de pagamento
  • Não-breaking. É campo novo e anulável; diagnosis, normalized, driver e aiAnalysis não mudaram em nada. Nenhuma integração existente precisa de ajuste. details é null enquanto a consulta não respondeu.

  • Duas ausências que existiam e deixaram de existir: os exames em diagnosis.medicalExams.exams[] traziam 6 dos 11 campos (sem categoria permitida, cidade e UF), e isEAR só existia dentro de diagnosis.points — que é ausente quando não há consulta estadual. Os dois agora vêm em details, independentemente da consulta estadual.

  • Histórico de multas completo. diagnosis.fines continua sendo a janela móvel de 12 meses da Lei 14.071 (é o que decide a pontuação). details.stateLookup.fines traz o histórico inteiro, com o status de pagamento de cada autuação — são coisas diferentes e as duas continuam disponíveis.

  • Dados pessoais passam a ser expostos, incluindo filiação e documento de identidade. A base é a mesma que já sustenta o relatório: o titular consultado é o objeto do relatório que você contratou, e são os mesmos valores impressos na CNH. A garantia anti-leak não muda — payload bruto do provedor, custo, identificador de requisição, URLs internas e contadores de pipeline seguem fora, e agora também rawStatus e a origem interna da pontuação de cada multa.

  • Ver Shape do Payload e Montar a UI.

2026-07-31 — Bloqueio declarado pelo estado e entrega parcial mais fiel

  • Novo campo opcional diagnosis.blocks.stateDeclaredBlock ({ isBlocked, statusLabel, detailedBlocks }) — a resposta direta da consulta estadual sobre a situação da CNH, independente da lista de bloqueios detalhados. Quando o estado acusa bloqueio sem detalhar nenhum, o diagnosis.status agora reflete isso. Ver Shape do Payload.
  • Entrega parcial: um relatório cuja consulta estadual não chegou a rodar deixa de ser reportado como DELIVERED. Passa a PROCESSING, e o evento report.partially_delivered é enviado a quem o tiver assinado. Antes, esses relatórios chegavam sem dados estaduais (bloqueios, pontos e multas) sem nenhuma indicação disso.
  • Exames clínicos vindos da consulta estadual: quando a consulta estadual reporta aptidão física e mental ou avaliação psicológica, esses registros passam a alimentar diagnosis.medicalExams — eles trazem resultado e validade, que a base federal deixa em branco na maior parte dos casos. Consequência: um exame vencido pode ser sinalizado em relatórios que antes não o mostravam. As provas de habilitação (direção, teórico) não entram na seção clínica.
  • Pontuação pela tabela RENAINF: pontos e gravidade de cada multa passam a vir do código da infração, com as strings do provedor como recuo. Multas cujo infrator é o proprietário do veículo (CTB 257 §8º) continuam listadas, mas deixam de somar pontos na CNH do condutor.
  • Validade da CNH passa a vir da consulta estadual sempre que ela responder. O órgão que emitiu a habilitação é quem a renova; a base federal é réplica e foi medida divergindo — em 363 pares de respostas, 99 traziam datas diferentes. diagnosis.validity (incluindo validityStatus e os alertas cnh_expired / cnh_expiring_soon) passa a ser calculado sobre essa data, e relatórios já entregues podem mudar de validityStatus ao serem consultados novamente. Sem consulta estadual, a data federal permanece.
  • Datas-sentinela deixam de ser gravadas como fato. As duas consultas dizem "não sei" com 31/12/1969 — o epoch Unix no fuso de Brasília —, e por ser uma data válida ela era gravada no condutor: uma CNH aparecia vencida desde 1969. Os campos expiryDate, issueDate, firstLicenseDate e birthDate passam a vir vazios nesses casos, em vez de 1969, e a validade volta a ser decidida pela camada que respondeu de verdade. Uma data vazia é ausência de informação, não um vencimento.
  • Não-breaking: nenhum campo foi removido ou renomeado. O campo novo é opcional; as demais mudanças tornam visíveis condições que já existiam — se a sua integração trata PROCESSING como "aguardar", nada muda além do volume.

2026-07-29 — Novo valor NO_LICENSE em diagnosis.status

  • diagnosis.status (e aiAnalysis.status) passam a poder retornar "NO_LICENSE": o CPF consultado nunca teve uma CNH emitida.
  • Não-breaking: valor adicional num campo já existente. O relatório é DELIVERED normalmente (a resposta "sem CNH" é o resultado da consulta, não uma falha), hasReportData é false, e aiAnalysis vem sempre preenchido com um texto fixo em pt-BR. Ver Shape do Payload para o detalhe completo do payload.

2026-07-23 — Análise por IA automática no fluxo B2B

  • A partir de agora, o campo aiAnalysis é gerado automaticamente para relatórios criados via API/MCP — não é mais necessário falar com o suporte para habilitá-la.
  • Não-breaking: o campo já era opcional e anulável. Continua null durante PROCESSING e pode vir null num relatório entregue caso a geração falhe (é best-effort e nunca bloqueia a entrega).
  • Garantia de ordem: quando presente, a análise é gravada antes de o status virar DELIVERED e antes de o webhook report.delivered ser enfileirado. Ou seja, um relatório entregue com análise a traz de forma idêntica no GET /api/v1/reports/{reportId} e no corpo do webhook.

2026-07-18 — Campo aiAnalysis (análise por IA)

  • Novo campo opcional e anulável aiAnalysis no payload do relatório ({ markdown, status, generatedAt }) — uma análise em linguagem natural gerada por IA a partir do diagnóstico. Aparece em GET /api/v1/reports/{reportId}, no corpo do webhook report.delivered e nas ferramentas MCP get_report / wait_for_report / find_report_by_external_ref (a mesma serialização em todos).
  • Não-breaking: campo adicional opcional — integrações existentes não precisam de nenhuma mudança. É null enquanto o relatório está em PROCESSING. (Na estreia, a geração ficava desativada por padrão no fluxo B2B; desde 2026-07-23 ela é automática — ver a entrada acima.)

2026-07 — Servidor MCP

  • Novo endpoint POST /api/mcp — um servidor Model Context Protocol (JSON-RPC 2.0, stateless) que expõe a API a agentes de IA/chatbots com a mesma API key cnh_live_.
  • 10 ferramentas: request_report, get_report, wait_for_report, find_report_by_external_ref, ask_doutor_multas, get_credit_balance, get_usage, list_reports, lookup_violation_code, calculate_points.
  • Adição não-breaking: os endpoints REST existentes não são afetados; o MCP reutiliza a mesma autenticação, cobrança e rate limits.

2026-06-16 — Novo evento report.partially_delivered

  • Novo evento de webhook report.partially_delivered: enviado quando o relatório é entregue, mas dados complementares da consulta estadual ficam pendentes. Se a busca for recuperada depois, um report.delivered é enviado em seguida.
  • Adição não-breaking: o evento é opt-in — selecione-o em Configurações → API para começar a recebê-lo. Webhooks existentes não são afetados.

v1.0 — Lançamento

Endpoints disponíveis:

  • POST /api/v1/reports — criar relatório de CNH (retorna 202 Accepted)
  • GET /api/v1/reports/{reportId} — consultar relatório

Funcionalidades:

  • Autenticação Bearer com formato cnh_live_<prefix>_<secret>
  • Status público do relatório: PROCESSING, DELIVERED, ERROR
  • Webhook HMAC-SHA256 com formato t=<unix>,v1=<hex> (Stripe-style), assinatura recomputada por tentativa
  • Eventos: report.delivered, report.failed
  • Idempotência via externalRef (janela 1h) e proteção de race (janela 60s)
  • Billing pós-pago com débito na criação e estorno automático em falha do pipeline
  • Rate limiting por minuto, por dia e concorrência
  • Log de entregas com retry manual em Configurações → API
  • Garantia anti-leak: payloads brutos do provider nunca são expostos