Pular para o conteúdo principal

POST /v1/verify/submit

Depois que o cliente enviou o documento e a selfie, chame /submit para executar o pipeline de verificação.

POST https://api.xxuxe.online/v1/verify/submit

Esta chamada:

  1. Confirma que as chaves submetidas são as que o /init emitiu para esta verificação
  2. Confirma que as imagens de fato chegaram ao armazenamento
  3. Executa OCR no documento via Workers AI
  4. Despacha o job para o backend de ML para comparação facial, prova de vida e veredicto
  5. Retorna 202 Accepted

Tudo depois do passo 5 é assíncrono.

Autenticação

Bearer token. Qualquer chave pertencente ao mesmo tenant que a usada no /init.

Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9

A checagem é sobre o tenant, não sobre a chave. Então um padrão legítimo e muitas vezes preferível funciona: inicie a verificação no navegador com a sua chave publicável e depois submeta a partir do seu próprio servidor com a sua chave secreta. Submissão entre tenants diferentes é rejeitada.

Corpo da requisição

CampoTipoObrigatórioDescrição
verificationIdstringSimDo /init. Precisa casar com ^vf_[A-Za-z0-9]{16,24}$
keys.docFrontstringSimA key de init.uploads.docFront. Máx. 512 caracteres
keys.selfiestringSimA key de init.uploads.selfie. Máx. 512 caracteres
keys.docBackstringNãoA key de init.uploads.docBack, se você a capturou
livenessScorenumberNãoScore de prova de vida calculado no cliente, 0-100. Usado como sinal suave adicional
metadataobjectNãoAceito pelo schema. Veja o aviso abaixo

keys é obrigatório, e não é decorativo

keys é o que amarra os bytes armazenados a esta verificação. Não há como submeter sem ele.

Os valores precisam corresponder exatamente ao que o /init retornou. Pegue-os da resposta do init em vez de reconstruir as strings — uma divergência é rejeitada com doc_front_key_mismatch, selfie_key_mismatch ou doc_back_key_mismatch.

metadata é aceito e depois descartado

O schema valida metadata, e a requisição retorna 202. Mas o campo não é encaminhado ao backend e não é devolvido em webhooks. Ele para na borda do Worker.

Este é o pior tipo de falha — pega os seus dados, tem sucesso, e os joga fora. Se você planejava rotear por um ID de campanha, um bucket de teste A/B ou uma tag de marca, isso não vai funcionar.

Use userRef no /init no lugar. Ele é persistido, e é o único campo devolvido nos payloads de webhook.

Exemplo de requisição

curl

curl -X POST https://api.xxuxe.online/v1/verify/submit \
-H "Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9" \
-H "Content-Type: application/json" \
-d '{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"keys": {
"docFront": "verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/doc-front.jpg",
"selfie": "verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/selfie.jpg"
},
"livenessScore": 92.5
}'

JavaScript / Node.js

const response = await fetch('https://api.xxuxe.online/v1/verify/submit', {
method: 'POST',
headers: {
'Authorization': `Bearer ${publishableKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
verificationId: init.verificationId,
keys: {
docFront: init.uploads.docFront.key,
selfie: init.uploads.selfie.key,
// docBack: init.uploads.docBack.key, // apenas se você o capturou
},
livenessScore: 92.5,
}),
});

const data = await response.json(); // 202 Accepted
console.log('Submitted. Poll:', data.statusUrl);

Python

import os
import requests

response = requests.post(
"https://api.xxuxe.online/v1/verify/submit",
headers={
"Authorization": f"Bearer {os.environ['VERIDIA_PUBLISHABLE_KEY']}",
"Content-Type": "application/json",
},
json={
"verificationId": init["verificationId"],
"keys": {
"docFront": init["uploads"]["docFront"]["key"],
"selfie": init["uploads"]["selfie"]["key"],
},
"livenessScore": 92.5,
},
)
response.raise_for_status()
print("Poll:", response.json()["statusUrl"])

Resposta

202 Accepted

{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"status": "queued",
"statusUrl": "https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2"
}
CampoTipoDescrição
verificationIdstringO mesmo ID que você submeteu
statusstringqueued, processing ou completed
statusUrlstringOnde fazer polling do veredicto
status aqui não é um veredicto

status: "completed" nesta resposta — ou em qualquer polling posterior — significa que o pipeline terminou. Não significa que a pessoa passou. O resultado vive em um campo separado, verdict, e ele não está nesta resposta de forma alguma.

Ler o veredicto exige uma chave secreta em GET /v1/verify/:id. A chave publicável que fez esta chamada não consegue obtê-lo.

Idempotência

Chamar /submit duas vezes com o mesmo verificationId é seguro.

O backend deduplica: se a verificação já está processing ou completed, a duplicata é ignorada e você recebe de volta o status atual em vez de uma segunda execução do pipeline. Uma verificação ainda em queued ou failed pode ser reenfileirada.

O resultado do OCR fica em cache por 30 minutos, então uma nova tentativa também não bate de novo no Workers AI.

Então, se o seu cliente receber um erro de rede depois de enviar /submit, tente de novo com o mesmo corpo. Você não vai criar uma verificação duplicada.

Sobre a cobrança

A autenticação rejeita requisições quando o saldo de créditos do tenant está zerado (insufficient_credits, 402), mas este endpoint atualmente não decrementa esse saldo a cada verificação. Não construa previsão de uso nem um medidor de consumo assumindo que um submit equivale a um crédito a menos no contador — reconcilie contra os seus próprios registros.

Tempos

O 202 tipicamente volta em alguns segundos; a chamada de OCR domina. O veredicto em si chega alguns segundos depois disso, de forma assíncrona.

Em vez de fazer polling por ele, use webhooks. Se você precisa mesmo fazer polling, GET /v1/verify/:id tem o padrão — e note que fazer polling de um único servidor bate no limite de taxa por IP muito antes do limite por tenant.

Erros

HTTPCódigo de errodetail.reasonQuando
400invalid_bodyO corpo falhou na validação. Veja detail.fieldErrors
400invalid_bodydoc_front_key_mismatchkeys.docFront não é o que o /init retornou
400invalid_bodyselfie_key_mismatchkeys.selfie não é o que o /init retornou
400invalid_bodydoc_back_key_mismatchkeys.docBack não é o que o /init retornou
400invalid_bodydoc_front_not_uploadedNenhum objeto naquela chave — o cliente nunca enviou
400invalid_bodyselfie_not_uploadedO mesmo, para a selfie
400invalid_bodydoc_front_disappearedO objeto existia no momento da checagem mas sumiu antes do OCR (muito raro)
401missing_api_key / invalid_api_keyVeja Autenticação
402insufficient_creditsSaldo do tenant está zerado
404verification_not_foundNunca criada via /init, expirada após 1 hora, ou pertence a outro tenant
429rate_limited30/min por tenant, ou o limite por IP
500internal_errorReporte o requestId
503backend_unavailablePipeline inacessível. Tente de novo com backoff

insufficient_credits é 402, não 403. O código de erro interno é internal_error, não internal. Catálogo completo: Erros.

Se você recebe doc_front_not_uploaded acreditando que enviou a imagem, a causa usual é um upload que falhou com missing_upload_token porque o cliente remontou os cabeçalhos em vez de encaminhar slot.headers literalmente. Veja Enviando as imagens.

Notas

  • A verificação precisa ter sido criada na última hora. A intenção expira depois disso e você recebe verification_not_found.
  • livenessScore é opcional. É um sinal suave que empurra a confiança ponderada; ele nunca decide o resultado sozinho.
  • A proveniência da captura é lida a partir de metadados carimbados pelo servidor quando os bytes chegaram, não a partir do corpo desta requisição. O /submit não pode reescrevê-la.

O que vem a seguir

GET /v1/verify/:id — obtenha o veredicto