Saltar al contenido principal

POST /v1/verify/init

Inicia una nueva verificacion. Devuelve un verificationId mas tres URLs presigned (doc-front, doc-back, selfie) que el browser usa para subir imagenes directamente a Cloudflare R2.

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

Por que pre-crear el verification ID

Llamar a /init primero (en lugar de subir y submitear) hace dos cosas:

  1. Permite al cliente armar sus propios logs antes que algo llegue al backend
  2. Hace la llamada posterior a /submit idempotente — el mismo verificationId en el submit body siempre significa la misma fila en DB

Autenticacion

Bearer token. Publishable (qv_pub_*) o secret (qv_sec_*).

Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9

Request body

Todos los campos son opcionales, pero country y documentType mejoran significativamente el accuracy del OCR.

CampoTipoRequeridoDescripcion
userRefstringNoTu propio identificador de usuario — eco en eventos de webhook. Max 128 chars
countrystringNoCodigo ISO 3166-1 alpha-2 (ej. PY, BR, MX). Sugiere tipo de documento
documentTypestringNoUno de: dni, passport, drivers_license, national_id, other
submittedFullNamestringNoNombre completo como lo tipeo el usuario — para fuzzy matching. Max 255 chars

Request de ejemplo

curl

curl -X POST https://api.xxuxe.online/v1/verify/init \
-H "Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9" \
-H "Content-Type: application/json" \
-d '{
"userRef": "customer-12345",
"country": "PY",
"documentType": "dni",
"submittedFullName": "Juan Carlos Perez"
}'

JavaScript / Node.js

const response = await fetch('https://api.xxuxe.online/v1/verify/init', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.VERIDIA_PUBLISHABLE_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
userRef: 'customer-12345',
country: 'PY',
documentType: 'dni',
submittedFullName: 'Juan Carlos Perez',
}),
});

const data = await response.json();
console.log(data.verificationId);

Python

import os
import requests

response = requests.post(
"https://api.xxuxe.online/v1/verify/init",
headers={
"Authorization": f"Bearer {os.environ['VERIDIA_PUBLISHABLE_KEY']}",
"Content-Type": "application/json",
},
json={
"userRef": "customer-12345",
"country": "PY",
"documentType": "dni",
"submittedFullName": "Juan Carlos Perez",
},
)
response.raise_for_status()
data = response.json()
print(data["verificationId"])

PHP

<?php
$payload = json_encode([
"userRef" => "customer-12345",
"country" => "PY",
"documentType" => "dni",
"submittedFullName" => "Juan Carlos Perez",
]);

$ch = curl_init("https://api.xxuxe.online/v1/verify/init");
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 $data['verificationId'];

Respuesta

200 OK

{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"uploads": {
"docFront": {
"url": "https://...r2.cloudflarestorage.com/veridia-uploads/verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/doc-front.jpg?...",
"key": "verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/doc-front.jpg",
"method": "PUT",
"headers": {
"Content-Type": "image/jpeg"
}
},
"docBack": {
"url": "https://...r2.cloudflarestorage.com/veridia-uploads/verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/doc-back.jpg?...",
"key": "verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/doc-back.jpg",
"method": "PUT",
"headers": {
"Content-Type": "image/jpeg"
}
},
"selfie": {
"url": "https://...r2.cloudflarestorage.com/veridia-uploads/verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/selfie.jpg?...",
"key": "verif/tn_xyz/vf_AG07CDWRRFQV4T05ZXG2/selfie.jpg",
"method": "PUT",
"headers": {
"Content-Type": "image/jpeg"
}
}
},
"expiresAt": 1714604000
}

Campos de respuesta

CampoTipoDescripcion
verificationIdstringID unico de esta verificacion — pasarlo a /submit y /verify/:id
uploads.docFrontobjectURL presigned para el frente del documento (siempre devuelta)
uploads.docBackobjectURL presigned para el reverso (usar solo si se necesita)
uploads.selfieobjectURL presigned para la selfie (siempre devuelta)
uploads.*.urlstringLa URL presigned completa — hace PUT con bytes raw aqui
uploads.*.keystringHandle opaco — pasarlo de vuelta en /submit
uploads.*.methodstringSiempre "PUT"
uploads.*.headersobjectHeaders que tu PUT debe incluir (tipicamente solo Content-Type)
expiresAtnumberUnix timestamp cuando las URLs presigned dejan de funcionar (15 minutos despues del init)

Subiendo las imagenes

Despues de /init, tu cliente sube cada imagen directamente a R2 usando la URL presigned. No proxies a traves de tu backend — estarias pagando doble bandwidth y agregando latencia innecesaria.

async function uploadImage(presigned, blob) {
const response = await fetch(presigned.url, {
method: presigned.method, // "PUT"
headers: presigned.headers,
body: blob,
});
if (!response.ok) {
throw new Error(`Upload fallo: ${response.status}`);
}
}

await uploadImage(initResponse.uploads.docFront, docFrontBlob);
await uploadImage(initResponse.uploads.selfie, selfieBlob);
// docBack solo si tu tipo de documento lo requiere

El widget hace todo esto por vos. Solo necesitas este codigo si estas construyendo un flow custom mobile o server-side.

Errores

HTTPError codeCuando
400invalid_bodyEl body fallo validacion Zod (mira detail.fieldErrors)
401unauthorizedHeader Authorization faltante
401invalid_keyKey revocada, expirada, o nunca existio
403origin_not_allowedPublishable key llamada desde dominio no-whitelisted
403insufficient_creditsEl tenant tiene 0 creditos
429rate_limitedHit el rate limit de /init (60 req/min por defecto)
500internalAlgo se rompio de nuestro lado — incluye el requestId al reportar

Catalogo completo: Errores.

Notas

  • Formato de verificationId: prefijo vf_ + 16-24 chars alfanumericos (regex: ^vf_[A-Za-z0-9]{16,24}$)
  • Las URLs presigned son validas por 15 minutos — suficiente para que un usuario complete el flow del widget
  • Layout de storage: verif/<tenantId>/<verificationId>/<role>.jpg (asi el cleanup de retencion es directo)
  • El submittedFullName no se guarda en el path R2 — es PII y vive solo en el record de intent (y tu DB)

Que sigue

Despues de /init, tu cliente sube las imagenes, despues llama:

POST /v1/verify/submit →