Fluxo de Liveness
O fluxo de liveness (prova de vida) tem quatro passos e roda em dois lugares diferentes: parte no seu backend, parte no dispositivo do usuário. É essa divisão que explica quase tudo o que existe aqui — o token JWT, o SDK de captura e o identificador que amarra as duas chamadas à API.
As páginas de referência descrevem cada endpoint isoladamente. Esta página responde o que elas não respondem: em que ordem chamar, quem chama o quê e como os identificadores se encadeiam.
‼️ Duas armadilhas, antes de qualquer coisa
Cada uma delas já quebrou integração em produção, e as duas falham em silêncio, com 200 na resposta.
- Chame
/liveness/v2, sempre com a versão no caminho. É o envelope da v2 — com o resultado emdata.isAlive— que o passo seguinte lê. Uma URL sem versão não garante essa resposta, e o face match respondematched: falsepara sempre, sem erro nenhum, quando não encontradata.isAlive. - O
/face-match-for-livenessdevolve só{"matched": true}ou{"matched": false}. Semid, semversion, semdata, semmetadata. É a exceção ao envelope padrão da API, e um cliente que esperadatarecebeundefined.
Visão geral
| # | Quem chama | O quê | O que recebe de volta |
|---|---|---|---|
| 1 | Seu backend | POST /auth-jwt | accessToken |
| 2 | Seu backend → SDK | entrega o token ao dispositivo | — |
| 3 | SDK, no dispositivo | POST /liveness/v2 com a selfie capturada | id da sessão e data.isAlive |
| 4 | Seu backend | POST /face-match-for-liveness/{id}/v2 com o documento | matched |
Entre o passo 3 e o passo 4, o id da sessão de liveness precisa voltar do dispositivo para o seu backend — é o único fio que liga as duas chamadas.
Seu backend SDK no dispositivo API Nextcode
│ │ │
│ 1. POST /auth-jwt │ │
────────────────────────────────────────────────────▶
│ { accessToken } │
◀────────────────────────────────────────────────────
│ │ │
│ 2. entrega o token │ │
──────────────────────────▶ │
│ │ │
│ 3. captura a selfie e chama │
│ POST /liveness/v2 │
│ ──────────────────────────▶
│ │ { id, data.isAlive } │
│ ◀──────────────────────────
│ │ │
│ o id da sessão volta para o backend │
◀────────────────────────── │
│ │ │
│ 4. POST /face-match-for-liveness/{id}/v2 │
│ + a foto do documento │
────────────────────────────────────────────────────▶
│ { "matched": true } │
◀────────────────────────────────────────────────────
│ │ │A linha divisória importa
Tudo o que acontece na coluna do meio roda em código que o usuário final pode inspecionar. É por isso que a chave de API nunca desce até lá, e por isso o passo 1 existe.
Passo 1 — o backend gera o token
O SDK de captura roda dentro do seu aplicativo ou no navegador do usuário. Colocar a chave de API ali seria entregá-la a qualquer pessoa disposta a abrir o inspetor do navegador ou a descompilar o APK.
A saída é um token JWT de curta duração: o seu backend chama o /auth-jwt com a chave de API e recebe um accessToken que pode descer com segurança até o dispositivo. Ele carrega as mesmas permissões da chave que o emitiu e expira sozinho.
Referência completa dos parâmetros e das respostas: Token JWT.
Três decisões que valem para este passo:
- A chamada parte sempre do seu backend. O
/auth-jwté o único endpoint da API que não aceita o próprio token JWT: é ele que emite o token, e só a chave de API o autentica. - Gere o token imediatamente antes de entregá-lo ao SDK. O
ttldeve cobrir a janela de captura, não a sessão inteira do usuário. Um token emitido no login e usado dez minutos depois costuma chegar ao SDK já expirado. - O
ttlé uma string com unidade —"15m", não900nem"900". Essa é a causa mais comum de500neste endpoint, e ela está detalhada na página de referência.
Passo 2 — capturar com o SDK
O SDK conduz o liveness no dispositivo, autentica-se com o token no header Authorization: Bearer <accessToken> e chama o /liveness/v2 diretamente. A selfie não passa pelo seu backend.
| Plataforma | Repositório de exemplo |
|---|---|
| Android | liveness-sdk-android-sample |
| iOS | liveness-sdk-ios-sample |
| Web | liveness-sdk-web-sample |
Confirme que a versão chamada é a v2. Este é o ponto exato em que a primeira armadilha se instala: sem a versão no caminho, a resposta parece normal, e o problema só aparece no passo 4 — como um matched: false que nunca vira true.
Passo 3 — ler o resultado do liveness
data.isAlive responde a uma única pergunta: há uma pessoa viva, presente no momento da captura? Ele não identifica ninguém e não compara com documento algum — essa é a função do passo 4.
isAlive | O que aconteceu | Encaminhamento usual |
|---|---|---|
true | Pessoa viva, captura legítima. | Siga para o passo 4. |
false | A análise rodou e não aprovou. | Repetir a captura costuma resolver: luz fraca, foco ou enquadramento derrubam o resultado tanto quanto uma fraude. |
Formato completo da resposta: Liveness.
Guarde o id da resposta. Ele é o livenessRequestId do passo seguinte, e sem ele a sessão não é recuperável. Ele também vem no header Nextid-ReqId de toda resposta.
Passo 4 — comparar com o documento
Aqui a pergunta muda: já sabemos que há uma pessoa viva; falta saber se é a pessoa do documento.
O endpoint é o Face Match for Liveness, e o encadeamento acontece pela URL:
curl -i -X POST 'https://api-homolog.nxcd.app/face-match-for-liveness/13cfec7c-b238-4820-a4d1-5173e4c1418e/v2' \
--header 'Authorization: ApiKey SUA_CHAVE_AQUI' \
--form 'documento=@./documento-frente.jpg'O 13cfec7c-… no caminho é o id que veio do passo 3. Você envia apenas o documento — a selfie já está guardada na sessão de liveness, e reenviá-la seria comparar a imagem com ela mesma.
A resposta é o corpo mais curto de toda a API:
{ "matched": true }Sem envelope, sem id, sem confidence. O identificador desta requisição está no header Nextid-ReqId.
Chame este passo só quando isAlive for true
Uma sessão reprovada devolve matched: false sem executar comparação alguma — e uma resposta 200 é tarifada do mesmo jeito. Verificar o resultado do passo 3 antes economiza uma chamada e evita interpretar como "faces diferentes" o que foi apenas "não havia o que comparar".
E se a selfie não veio de uma sessão de liveness?
Então o endpoint é outro: o Face Match recebe as duas imagens na mesma requisição e devolve o envelope completo, com confidence. Use o /face-match-for-liveness quando a selfie já foi capturada pelo SDK, e o /face-match quando você tem os dois arquivos em mãos.
Erros comuns no fluxo
| Sintoma | Causa provável | O que fazer |
|---|---|---|
401 no SDK, no meio da captura | Token expirado. O ttl é curto demais, ou o token foi emitido muito antes da captura começar. | Emitir o token logo antes de entregá-lo ao SDK e dimensionar o ttl pela janela de captura. |
500 no /auth-jwt | ttl enviado como número (900) ou como string sem unidade ("900", lido como 900 milissegundos). | Enviar "15m", "1h", "30s". |
401 no /auth-jwt | Chave de API ausente, inválida, ou sem permissão de emissão de token. | Conferir a chave e o ambiente. O token herda as permissões da chave: se ela não tem o produto, ele também não terá. |
matched: false sempre, mesmo com a pessoa certa | A sessão de liveness não devolveu data.isAlive — quase sempre por chamar /liveness sem a versão no caminho. | Conferir o campo version da resposta do passo 3. Se não for v2, corrigir a URL do SDK. |
matched: false logo após um isAlive: false | Comportamento esperado: sem sessão aprovada, nada é comparado. | Não interpretar como "faces diferentes". Repetir a captura. |
matched: false com sessão aprovada e pessoa certa | livenessRequestId errado — id de outra requisição, id do Nextid-ReqId da chamada errada — ou nenhum arquivo enviado. | Um livenessRequestId inexistente não devolve 404: devolve 200 com matched: false. Confira o id e o arquivo. |
404 no /face-match-for-liveness | Versão inexistente na URL, ou a imagem da sessão não está mais disponível. | Chamar /face-match-for-liveness/{id}/v2 resolve o primeiro caso: a v2 é a única versão. |
A lista completa de códigos e o formato das respostas de erro estão em Códigos HTTP das respostas.
Os identificadores, de ponta a ponta
| Identificador | Nasce em | Vai para |
|---|---|---|
accessToken | resposta do POST /auth-jwt | header Authorization: Bearer das chamadas feitas pelo SDK |
id da sessão | resposta do POST /liveness/v2 | caminho do POST /face-match-for-liveness/{id}/v2 |
Nextid-ReqId | header de todas as respostas | seus logs — é por ele que o suporte localiza uma chamada |
Próximos passos
- Token JWT — parâmetros,
ttle erros de emissão - Liveness — resposta completa, erros e versões
- Face Match for Liveness — todos os casos que levam a
matched: false - Fluxo de OCR + Biometria — o outro lado do onboarding: classificar, extrair e cruzar os dados do documento