API REST (4 endpoints públicos)
A superfície server-to-server é enxuta de propósito: com ask_ você cria a sessão (ou emite um link hospedado, se não for montar o widget) e cuida da confiabilidade do webhook. Todo o resto (flows, verificações, blocklist, destinos de webhook, billing) se gerencia pela plataforma.
POST /v1/verification-sessions
origin_tag é o rótulo da origem da jornada, e quem escolhe o valor é você. Ele responde de qual campanha, canal ou tela a pessoa veio, sem tabela paralela do seu lado: o valor volta exato no 201 e no data.origin_tag do webhook da verificação, e a sessão renovada pelo widget herda o mesmo rótulo. São de 1 a 64 caracteres entre letras, dígitos, ponto, sublinhado, dois-pontos e hífen, começando por letra ou dígito. E-mail e URL não cabem, de propósito: nunca coloque dado pessoal aqui. A caixa é preservada, e ferramentas de análise tratam Google e google como valores diferentes, então prefira minúsculas. Como o campo entra no corpo, a idempotência vale para ele: repetir o reference_id com outro origin_tag é 422 idempotency_key_reuse. Sem o campo, nenhuma resposta muda. Junto do bloco de transação ele é recusado com 422 origin_tag_not_supported, porque a ingestão não tem jornada a rotular.
transaction e transactions são a ingestão do Gate Transacional, e só existem para flow que contenha um módulo consumidor de transação. Flow sem consumidor responde 422 transaction_not_supported, e os dois campos são exclusivos entre si: mandar os dois no mesmo corpo é 422 transaction_conflict.
No singular, transaction é um evento e external_id é obrigatório: ele é a idempotência durável do evento, e é o que faz o seu retry não virar uma segunda cobrança. A resposta 201 traz a forma de ingestão, com o id ing_ e os contadores, e num flow com gate síncrono ela traz também o veredito do gate, que é o ponto do módulo: você tem um pagamento parado esperando, e a resposta vem na mesma chamada. Ela vem sempre, inclusive no seu retry do mesmo external_id, que recebe de volta o mesmo veredito da primeira vez e não é cobrado de novo. E no sandbox o gate decide também, de forma determinística, para você exercitar os três ramos antes de ir para produção. Os detalhes estão na página do Gate Transacional.
Fora do flow com gate, transactions é o lote, com a mesma condição de consumidor. Ele aceita de 1 até o teto por chamada, e acima disso responde 422 batch_too_large com o teto no corpo do erro, para você não precisar descobrir o número por tentativa. O dedupe é por external_id: reenviar o mesmo lote não duplica evento nem cobra de novo.
monitoring é { enabled: boolean }, o override por sessão do Monitoramento Contínuo. Ausente ou null, a sessão herda o monitoring_enabled do flow. false significa “esta sessão não inscreve” e é sempre aceito, mesmo com o módulo desligado, porque é um pedido que o produto honra em qualquer estado. true inscreve o titular aprovado. Chave desconhecida dentro do bloco é 400 unknown_monitoring_key, nunca ignorada: toggle de compliance aceito e ignorado faria você acreditar que monitora enquanto ninguém monitora.
O 422 monitoring_unavailable deixou de acontecer em 11 de setembro de 2026, quando a venda do módulo abriu. Ele só volta se o módulo for desligado de novo, e nesse caso vale de novo a mesma regra de sempre: enabled: false continua passando, e só true é recusado.
Quatro blocos de módulo seguem a mesma regra: obrigatórios quando o flow tem o módulo, recusados quando não tem. Nenhum deles é aceito e ignorado em silêncio, e chave desconhecida dentro do bloco é 400.
esignatureé{ document_sha256 }, o SHA-256 em hex do documento que o titular vai assinar (o documento nunca é enviado). Sem o bloco num flow com o módulo,422 esignature_document_required; num flow sem ele,422 esignature_not_supported. Ver Assinatura eletrônica.consultation_authorizationé{ text_sha256, text_version, scope }, o texto da autorização de consulta que o titular leu, identificado pelo hash. Sem o bloco,422 consultation_authorization_required; sem o módulo,422 consultation_authorization_not_supported. Ver Custódia da autorização.pix_deviceé{ fingerprint, platform, label }, a identidade do aparelho que o titular está cadastrando, enviada pelo seu servidor. O flow com o módulo exigereference_ide o bloco (422 pix_device_required); sem o módulo,422 pix_device_module_not_in_flow. Ver Cadastro de dispositivo Pix.session_monitoringé{ window_hours }, a janela em horas em que a sessão logada é observada. Só existe em flow com o módulosession_monitor; sem ele,422 session_monitoring_not_supported. Ver Monitoramento de sessão.
/v1/webhook-events
/v1/webhook-events/replay
Seu sistema ficou fora do ar? Liste com
GET /v1/webhook-events e redispare pelos verification_ids: você recebe exatamente o mesmo corpo que teria recebido, sem nenhuma mudança. Os eventos que não pertencem a uma verificação, como a revogação de um dispositivo, são resgatados pelo resource_id que veio no próprio evento.Link de verificação hospedado
Nem toda integração quer montar o widget. Quando o titular não está no seu site (cobrança por e-mail, onboarding por WhatsApp, atendimento no balcão, QR num contrato), você emite um link hospedado: a página é nossa, o fluxo é o mesmo do widget e você só entrega aurl.
Três diferenças importam antes de escolher. O link vive horas e a sessão só nasce no resgate (e aí vive os mesmos 900 segundos de sempre), então um link não vira sessão vencida na caixa de entrada de ninguém. O token volta em claro uma única vez, porque guardamos só o hash: não existe reexibição, e perder o token é emitir outro. E emitir link não cobra nada: quem cobra é a verificação que nascer do resgate, com o preço do flow que você escolheu.
POST /v1/verification-links
Sem idempotência nesta rota, de propósito. Selar a resposta a guardaria no banco, e a resposta aqui carrega o segredo do link. Repetir a chamada só cria outro link, que expira sozinho e não custa nada: o risco de guardar o segredo é maior que o de emitir um link a mais.
verification.completed aprovado, recomendamos mandar um aviso de que a verificação aconteceu para o telefone ou o endereço postal que você já validou (o e-mail validado só basta no nível mais baixo de garantia de identidade). O aviso diz o nome do seu serviço e a data da verificação, como contestar se não foi ela, com um contato, como os dados dela são protegidos e o que cabe a ela como cliente daqui em diante. Quem fez a verificação a pedido de um golpista descobre pelo aviso, e a contestação chega a você.
Confirmação fora de banda
Um pedido chegou por chamada, por vídeo ou por mensagem: liberar um pagamento, trocar o telefone de uma conta, assinar uma contratação. Quem pede diz ser o seu cliente, e o rosto e a voz na tela parecem os dele. A confirmação fora de banda tira a decisão dessa conversa: você manda um link pontual pelo canal que já tinha cadastrado para aquela pessoa, e só ela confirma, com a passkey dela ou com a reautenticação facial com prova de vida. Quando usar. No financeiro, antes de liberar um pagamento pedido fora do fluxo normal. Na central de atendimento, antes de trocar e-mail, telefone ou senha de quem ligou. Na contratação remota, antes de aceitar a assinatura de quem apareceu só na chamada. Em todos, a regra é a mesma: quem inicia é você, pelo canal que você já tinha, com um pedido que só existe naquele momento. Como mandar pelo painel. No detalhe de uma verificação da pessoa, ou na linha dela em Sessões, use Confirmar identidade agora. Escolha o flow que confirma (só aparecem os flows ativos com passkey ou com reautenticação facial), escreva o resumo do pedido, com até 140 caracteres, e crie. A tela mostra o link, o QR, o botão de copiar, o prazo de 10 minutos e o estado ao vivo: aguardando, aberto, em andamento, confirmado, não confirmado, recusado pelo titular, expirado ou revogado. Como mandar pela API. É a mesma rota do link hospedado, compurpose igual a confirmation e o bloco act, que descreve o pedido. O act.external_id é obrigatório pela API: é o id do pedido no seu sistema, e volta no webhook para você casar a resposta com o pedido.
verification.completed com data.purpose igual a confirmation e o bloco data.act com kind, digest e external_id. O digest é o mesmo act_digest da criação do link: um digest só do link ao webhook. O check_details diz qual fator confirmou. Com passkey, o bloco do módulo passkey traz user_verified e a evidência conferível da assinatura. Com o rosto, o bloco do módulo face_reauth traz a semelhança à matrícula feita no cadastro: é semelhança, e não se lê como autenticação.
act.rejected, a sessão fecha e não há cobrança. Trate como sinal para parar o pedido e falar com ela pelo canal cadastrado, e não como prova de fraude: quem recebeu o link encaminhado também consegue recusar. Se a confirmação não fechar, o verification.completed chega reprovado ou em revisão, e a revisão de uma confirmação nunca vira aprovação pelo painel: a decisão do pedido é sua, pelo seu canal. Se o prazo acabar sem resposta, nenhum webhook de verificação é enviado, e o estado aparece no painel como expirado.
Os limites. O link de confirmação vale no máximo 10 minutos, e a sessão que nasce dele vale o que resta desse prazo, sem renovação: quem perdeu o prazo pede um novo link. Existe no máximo um pedido aberto por pessoa, e o novo substitui o anterior, que é revogado. São no máximo cinco pedidos por pessoa por hora. O resumo aparece para a pessoa exatamente como você escreveu, então caractere invisível, de controle ou de direção do texto é recusado. No sandbox, a cerimônia da passkey pode ser simulada, e o bloco do módulo diz quando foi, com simulated.
O que ela não é. O link é transporte, não fator: quem o abre ainda precisa da passkey ou do rosto da pessoa. Por isso e-mail nunca é fator de autenticação aqui, e um clique num link nunca confirma nada sozinho, como a norma pública NIST SP 800-63B-4, seção 3.1.3.1, exige. E a confirmação não é detecção de deepfake na chamada: ela não olha a chamada, ela tira a decisão de dentro dela.
Spec OpenAPI e tipos
Tudo o que esta página promete existe também como OpenAPI 3.1: um documento gerado do código do backend e comparado com ele em teste a cada mudança, cobrindo as 4 rotas públicas, a rota de capacidades, o corpo assinado do webhook e o código estável de cada erro, por status. Spec escrita à mão envelhece e mente; esta não tem como. SPEC · SITE (URL estável) https://unifokal.com/docs/openapi.json SPEC · API (mesmo documento) https://api.unifokal.com/v1/openapi.json Esta página declara o spec no<head> (rel="describedby" e rel="service-desc"), então ferramentas o encontram sozinhas. Para gerar tipos TypeScript do contrato, uma linha basta:
curl · Gerar os tipos do contrato · no terminal
/v1/capabilities
A descoberta viva da API: módulos, preços em centavos, estado available/coming_soon e o grafo requires, lidos do banco (a mesma fonte da vitrine de preços, nunca uma segunda lista). A credencial é opcional: anônimo recebe o catálogo geral com o preço-base público; com a sua sk_ no Authorization, a mesma URL devolve o contrato efetivo da sua organização (preço negociado, ambiente e livemode da chave).
Coleção importável
A API inteira numa coleção pronta para o seu cliente HTTP, no formato aberto Postman Collection v2.1, que o Postman, o Insomnia e o Bruno importam. Ela é gerada da mesma especificação OpenAPI a cada publicação, então acompanha a API: traz as rotas públicas da chave secreta com o corpo mínimo e a autenticação já montados, em pastas na ordem de uso e com o link para a seção de cada uma nesta documentação. A coleção não carrega chave nem script. A autenticação usa a variávelUNIFOKAL_SECRET_KEY, que você define no ambiente do seu cliente HTTP (ou no cofre dele), nunca dentro da coleção. Nenhum código roda ao importar.
- Baixe o arquivo ou importe pelo endereço https://unifokal.com/docs/unifokal.postman_collection.json.
- Crie um ambiente com
UNIFOKAL_SECRET_KEY(a sua chave de sandbox,sk_test) eUNIFOKAL_FLOW_ID(um flow do mesmo ambiente). - Rode. O catálogo público responde sem chave nenhuma, e as outras chamadas usam a chave do ambiente.
Para agentes de IA
Integrando com um agente (Claude, Cursor, Copilot ou o seu)? Aponte-o para https://unifokal.com/llms.txt: é o briefing, no formato llmstxt.org, do que um agente não infere do spec: retry seguro e idempotência, a semântica dos códigos de erro, o webhook como fonte da verdade, o sandbox determinístico e o que nunca fazer com a chave secreta. A versão expandida, com os 10 pontos por extenso, está em https://unifokal.com/llms-full.txt. O contrato executável fica no spec OpenAPI 3.1 e a descoberta viva de módulos e preços emGET /v1/capabilities. Os três arquivos são gerados do código e comparados com ele em teste: o que o seu agente lê é o que a API faz.
Para dar ao seu assistente a documentação inteira de um produto de uma vez, use os pacotes de contexto: cada um é esta documentação em Markdown, recortada por produto, com o tamanho medido. E toda página daqui tem a própria versão em Markdown, na mesma URL com .md no fim.
O ponto de partida por máquina é o catálogo de API em /.well-known/api-catalog (RFC 9727): de lá saem o spec e esta documentação, em uma leitura. E o nosso robots.txt nomeia os principais rastreadores de resposta, um a um, para dizer com todas as letras o que já valia: esta superfície é aberta para leitura por máquina. A área autenticada e as rotas de API seguem fora, para todo mundo igual.