Pular para o conteúdo principal

POST /v1/verify/submit

Apos seu cliente ter feito upload das imagens do documento e da selfie ao R2 (usando as URLs presigned do /init), chame /submit para disparar o pipeline de verificacao.

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

Esta e a chamada que:

  1. Verifica que todas as imagens subidas chegaram realmente ao R2
  2. Roda OCR sobre o documento via Workers AI
  3. Debita um credito do balance do tenant
  4. Despacha o job ao backend ML para face match + liveness + veredicto
  5. Retorna 202 Accepted com uma status URL para fazer polling

Autenticacao

Bearer token. A mesma key usada em /init (o tenant deve coincidir).

Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9

Request body

CampoTipoObrigatorioDescricao
verificationIdstringSimO ID retornado por /init. Formato: vf_[A-Za-z0-9]{16,24}
keys.docFrontstringSimA R2 key retornada por /init para a frente do documento. Max 512 chars
keys.selfiestringSimA R2 key retornada por /init para a selfie. Max 512 chars
keys.docBackstringNaoA R2 key para o verso, se seu fluxo capturou
livenessScorenumberNaoScore de liveness opcional do MediaPipe (0-100). Usado como sinal adicional
metadataobjectNaoPares key-value opcionais para seu bookkeeping. Ecoado em webhooks

Os valores de keys.* devem coincidir exatamente com o que /init retornou. Submeter keys arbitrarias e rejeitado (doc_front_key_mismatch, selfie_key_mismatch, doc_back_key_mismatch).

Request de exemplo

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,
"metadata": {
"campaign": "spring_2026",
"platform": "web"
}
}'

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: initResponse.verificationId,
keys: {
docFront: initResponse.uploads.docFront.key,
selfie: initResponse.uploads.selfie.key,
},
livenessScore: 92.5,
}),
});

if (response.status === 202) {
const data = await response.json();
console.log('Submetido, polling em:', 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_response["verificationId"],
"keys": {
"docFront": init_response["uploads"]["docFront"]["key"],
"selfie": init_response["uploads"]["selfie"]["key"],
},
"livenessScore": 92.5,
},
)
response.raise_for_status()
data = response.json()
print("Status URL:", data["statusUrl"])

PHP

<?php
$payload = json_encode([
"verificationId" => $initResponse['verificationId'],
"keys" => [
"docFront" => $initResponse['uploads']['docFront']['key'],
"selfie" => $initResponse['uploads']['selfie']['key'],
],
"livenessScore" => 92.5,
]);

$ch = curl_init("https://api.xxuxe.online/v1/verify/submit");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $_ENV['VERIDIA_PUBLISHABLE_KEY'],
"Content-Type: application/json",
]);

$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Status URL: " . $data['statusUrl'];

Resposta

202 Accepted

{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"status": "queued",
"statusUrl": "https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2"
}

Campos de resposta

CampoTipoDescricao
verificationIdstringO mesmo ID que voce enviou — confirma que a request foi aceita
statusstringEstado atual: queued, processing, ou completed
statusUrlstringOnde fazer polling para o veredicto (tipicamente /v1/verify/:id)

A verificacao agora esta no pipeline. O veredicto tipicamente volta em 2-3 segundos. Use a statusUrl para obte-lo.

Idempotencia

Chamar /submit duas vezes com o mesmo verificationId e seguro. O backend deduplica por verificationId, entao voce obtem o mesmo resultado em ambas as vezes. O resultado do OCR e cacheado por 30 minutos — retries nao re-disparam chamadas ao Workers AI.

Isso significa: se seu cliente receber um erro de rede apos enviar /submit, tente de novo com o mesmo body. Voce nao vai dobrar o faturamento nem criar verificacoes duplicadas.

Pipeline nos bastidores

Quando /submit e chamado, isto e o que acontece (a API retorna imediatamente no passo 5 — o resto e async):

PassoLatenciaO que
1<50msValida o body da request (Zod schema)
2<100msBusca o intent do cache KV
3<200msVerifica que os objetos R2 existem via chamadas HEAD
4<2sRoda OCR via Workers AI (cacheado em retries)
5<200msDespacha ao backend ML, retorna 202
6asyncBackend roda face match + liveness + veredicto
7asyncBackend persiste no MySQL + dispara webhook

Tempo total de /submit ao veredicto: tipicamente 2-3 segundos.

Erros

HTTPError codeQuando
400invalid_bodyO body falhou validacao. Veja detail.fieldErrors
400invalid_body (com reason: "doc_front_key_mismatch")O keys.docFront nao coincide com o que /init retornou
400invalid_body (com reason: "selfie_key_mismatch")O keys.selfie nao coincide com o que /init retornou
400invalid_body (com reason: "doc_front_not_uploaded")O objeto R2 nao existe — o cliente nunca subiu
400invalid_body (com reason: "selfie_not_uploaded")Mesmo, para a selfie
404verification_not_foundO verificationId nunca foi criado via /init, ou o intent expirou (>1h)
401unauthorized / invalid_keyIssue de auth. Veja Autenticacao
403insufficient_creditsO tenant tem 0 creditos
429rate_limitedAtingiu o rate limit de /submit (30 req/min por padrao)
500internalAlgo quebrou. Inclua o requestId no seu ticket de suporte

Catalogo completo: Erros.

Notas

  • O verificationId deve ter sido criado na ultima hora. Apos isso, o intent expira e voce obtem verification_not_found
  • A mesma API key usada em /init deve ser usada aqui. Submission cross-tenant e rejeitada
  • O livenessScore e opcional mas recomendado — mesmo um score basico browser-side melhora o accuracy do veredicto
  • metadata e opaco para Veridia — apenas e ecoado em webhooks. Use para coisas como buckets de A/B test, campaign IDs, ou tags de plataforma

Proximos passos

Agora que a verificacao esta no pipeline, obtenha o veredicto:

GET /v1/verify/:id