Todo lo que necesita para integrar la verificación de documentos: REST + JSON, webhooks firmados, sandbox gratuito, OpenAPI y SDK.
La API de Validento verifica identidad, documentos e ingresos dentro de su producto — para dar de alta a un cliente (KYC), contratar a alguien de otro país, aceptar a un inquilino o comprador, o firmar con un socio comercial. Envíe los documentos de una persona (o un enlace para que los suba ella) y reciba una puntuación, una decisión y las desviaciones encontradas — por webhook o consultando.
| Base URL | https://api.validento.com |
|---|---|
| Formato | JSON (UTF-8) · HTTPS · REST |
| Versión | v1 · cabecera Validento-Version: 2026-10-01 |
| Entornos | vld_test_… sandbox (gratis, resultados deterministas) · vld_live_… producción |
| Datos | Análisis en la UE (Países Bajos); resultados guardados en EE. UU. (Supabase, Oregón). Los documentos no se guardan tras el análisis. |
Panel Enterprise → Desarrolladores → Claves API → “+ Clave sandbox”. Se muestra una sola vez: guárdela en su gestor de secretos.
curl https://api.validento.com/v1/me \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY"import Validento from 'https://validento.com/sdk/validento.mjs';
const vd = new Validento(process.env.VALIDENTO_KEY);
console.log(await vd.me());import os, requests
r = requests.get("https://api.validento.com/v1/me", headers={"Authorization": f"Bearer {os.environ['VALIDENTO_KEY']}"})
print(r.json())$ch = curl_init("https://api.validento.com/v1/me");
curl_setopt_array($ch, [CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("VALIDENTO_KEY")], CURLOPT_RETURNTRANSFER => true]);
echo curl_exec($ch);
curl https://api.validento.com/v1/screenings \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"applicant": { "name": "Ana García", "email": "ana@example.com" },
"purpose": "client_onboarding",
"country": "ES",
"reference": "case-1042",
"consent_confirmed": true,
"case_details": { "source_of_funds": "salary", "employer": "Acme SL" },
"documents": [
{ "type": "payslip", "url": "https://files.yourapp.com/signed/payslip-sept.pdf" },
{ "type": "bank_statement", "media_type": "application/pdf", "data": "JVBERi0xLjQK..." }
],
"metadata": { "customer_id": "c_981" }
}'import Validento from './validento.mjs';
const vd = new Validento(process.env.VALIDENTO_KEY);
const screening = await vd.screenings.create({
applicant: { name: 'Ana García', email: 'ana@example.com' },
purpose: 'client_onboarding', country: 'ES', reference: 'case-1042',
consent_confirmed: true,
case_details: { source_of_funds: 'salary', employer: 'Acme SL' },
documents: [
{ type: 'payslip', url: 'https://files.yourapp.com/signed/payslip-sept.pdf' },
{ type: 'bank_statement', media_type: 'application/pdf', data: fs.readFileSync('statement.pdf').toString('base64') },
],
metadata: { customer_id: 'c_981' },
});
console.log(screening.id, screening.status); // "processing"import os, uuid, base64, requests
H = {"Authorization": f"Bearer {os.environ['VALIDENTO_KEY']}"}
r = requests.post("https://api.validento.com/v1/screenings",
headers={**H, "Idempotency-Key": str(uuid.uuid4())},
json={
"applicant": {"name": "Ana García", "email": "ana@example.com"},
"purpose": "client_onboarding", "country": "ES", "reference": "case-1042",
"consent_confirmed": True,
"case_details": {"source_of_funds": "salary", "employer": "Acme SL"},
"documents": [
{"type": "payslip", "url": "https://files.yourapp.com/signed/payslip-sept.pdf"},
{"type": "bank_statement", "media_type": "application/pdf",
"data": base64.b64encode(open("statement.pdf", "rb").read()).decode()},
],
"metadata": {"customer_id": "c_981"},
}, timeout=60)
r.raise_for_status()
print(r.json()["id"])<?php
function validento(string $method, string $path, ?array $body = null): array {
$ch = curl_init("https://api.validento.com" . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => array_filter([
"Authorization: Bearer " . getenv("VALIDENTO_KEY"), "Content-Type: application/json",
$method === "POST" ? "Idempotency-Key: " . bin2hex(random_bytes(16)) : null,
]),
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$res = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) throw new RuntimeException($res["error"]["code"] ?? "error");
return $res;
}
$s = validento("POST", "/v1/screenings", [
"applicant" => ["name" => "Ana García"], "purpose" => "client_onboarding", "consent_confirmed" => true,
"case_details" => ["source_of_funds" => "salary", "employer" => "Acme SL"],
"documents" => [["type" => "payslip", "url" => "https://files.yourapp.com/signed/payslip-sept.pdf"]],
]);
echo $s["id"];
Registre un endpoint de webhook (panel o API) y reciba screening.completed. O consulte GET /v1/screenings/{id} hasta que status deje de ser processing (normalmente 10–60 s; sandbox: al instante).
Cuando su integración funciona en sandbox, su gestor de cuenta activa live. Cambie la clave por una vld_live_…; el código no cambia.
Pegue una clave sandbox y llame a la API directamente. La clave se queda en esta pestaña (sessionStorage) y solo viaja a api.validento.com.
Envíe la clave en cada petición como Authorization: Bearer vld_…. Solo se guarda un hash: si la pierde, cree otra. Las claves son de servidor: nunca en un navegador ni en una app móvil.
Una clave tiene acceso completo (*) o es restringida: déle a cada sistema solo lo que necesita. write incluye read. Sin el permiso: 403 missing_scope.
| Scope | Permite |
|---|---|
screenings:read | Leer comprobaciones e informes. |
screenings:write | Crear y borrar comprobaciones (+ leer). |
links:read | Leer enlaces de verificación. |
links:write | Crear y cancelar enlaces (+ leer). Ideal para el backend de su frontend. |
id_checks:read / id_checks:write | Leer / crear y cancelar verificaciones de identidad (ValiD). |
sanctions:read / sanctions:write | Leer / crear comprobaciones de sanciones y PEP. |
webhooks:read / webhooks:write | Gestionar endpoints; reenviar eventos. |
events:read | Leer eventos (30 días). |
usage:read | Uso del periodo y facturas. |
settings:read / settings:write | Ajustes: reglas, marca, formato, avisos. |
“Rotar” crea una clave nueva y deja la antigua viva durante el periodo de gracia que elija (0 h, 1 h, 24 h o 7 días). Despliegue la nueva, confirme en el registro de peticiones que la antigua ya no se usa y deje que caduque. ¿Clave expuesta? Revóquela: deja de funcionar en segundos.
Opcional, por cuenta: IPs exactas o rangos IPv4 CIDR (203.0.113.0/24). Otras IPs reciben 403 ip_not_allowed. Pídalo a su gestor de cuenta.
Las claves vld_test_ no analizan nada ni cobran, y devuelven resultados deterministas para que pueda probar cada camino de su código. Los objetos de test y live están separados por completo (también webhooks y eventos).
sandbox_outcome | o en reference / nombre | Resultado |
|---|---|---|
approve (por defecto) | — | score 86 · approve |
review | review | score 58 · review · 1 desviación media |
decline | decline | score 28 · decline · 1 desviación alta |
fail | fail | status failed · screening.failed |
Cuerpos en JSON (Content-Type: application/json). Fechas en ISO 8601 UTC; importes en céntimos (enteros). Cada respuesta lleva:
Validento-Request-Id | Inclúyalo cuando escriba a soporte. |
Validento-Version | Versión de la API que respondió. |
RateLimit-Limit / -Remaining / -Reset | Su cupo por minuto. |
Idempotent-Replayed: true | Respuesta repetida de una Idempotency-Key. |
Envíe Idempotency-Key: <uuid> en cada POST. Si la red falla y reintenta con la misma clave en 24 h, recibe la respuesta original y no se crea un duplicado. La misma clave con otro cuerpo devuelve 409 idempotency_conflict. El SDK lo hace por usted.
Las listas devuelven { object:"list", data:[…], has_more }, de más nuevo a más antiguo. Parámetros: limit (1–100), starting_after=<id> (siguiente página), ending_before=<id> (anterior), created[gte] y created[lte] (segundos unix o ISO 8601).
curl -G https://api.validento.com/v1/screenings \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-d limit=100 -d status=completed -d "created[gte]=1759276800" \
-d "metadata[customer_id]=c_981"for await (const s of vd.screenings.listAll({ status: 'completed', created: { gte: new Date('2026-10-01') }, metadata: { customer_id: 'c_981' } })) {
console.log(s.id, s.decision);
}params = {"limit": 100, "status": "completed"}
while True:
page = requests.get("https://api.validento.com/v1/screenings", headers=H, params=params).json()
for s in page["data"]:
print(s["id"], s["decision"])
if not page["has_more"]:
break
params["starting_after"] = page["data"][-1]["id"]$params = ["limit" => 100, "status" => "completed"];
do {
$page = json_decode(file_get_contents("https://api.validento.com/v1/screenings?" . http_build_query($params), false, $ctx), true);
foreach ($page["data"] as $s) echo $s["id"], " ", $s["decision"], "\n";
$params["starting_after"] = end($page["data"])["id"] ?? null;
} while ($page["has_more"]);
Guarde sus propios IDs en metadata (hasta 20 claves de ≤ 40 caracteres, valores string ≤ 500). Se devuelve tal cual, viaja en los webhooks y puede filtrar con metadata[clave]=valor.
v1 no se rompe. Añadimos campos, endpoints, eventos y valores de enum sin aviso: ignore lo que no conozca. Un cambio incompatible recibe una versión con fecha nueva, con al menos 12 meses de transición y aviso por email.
Los errores usan códigos HTTP y un cuerpo estable. Programe contra error.code (estable), no contra el mensaje.
{
"error": {
"type": "invalid_request_error",
"code": "consent_required",
"message": "\"consent_confirmed\": true is required — confirm the applicant agreed to the check.",
"param": "consent_confirmed",
"doc_url": "https://validento.com/developers#error-consent_required",
"request_id": "req_Fq9MsetWX27BncBC"
}
}
Reintente con seguridad (misma Idempotency-Key) en 429, 500 y errores de red. No reintente otros 4xx sin cambiar la petición.
invalid_api_key 401 authentication_errorClave ausente, mal escrita, revocada o caducada. → Compruebe la cabecera Authorization: Bearer vld_…
missing_scope 403 permission_errorLa clave es restringida y no tiene este permiso. → Use otra clave o cree una con el permiso indicado en el mensaje.
ip_not_allowed 403 permission_errorLa IP no está en su lista de IPs permitidas. → Añada la IP de salida de su servidor en el panel.
live_not_enabled 403 permission_errorEl acceso live aún no está activado. → Siga con sandbox; su gestor activa live tras la revisión.
account_suspended 403 permission_errorCuenta suspendida. → Contacte con su gestor de cuenta.
service_not_enabled 403 permission_errorEl servicio no está en su acuerdo. → Pídalo a su gestor de cuenta.
insufficient_balance 402 billing_errorSaldo prepago insuficiente. → Recargue o active la recarga automática.
credit_limit_reached 402 billing_errorLímite de crédito alcanzado. → Pague la factura abierta o pida un límite mayor.
rate_limited 429 rate_limit_errorDemasiadas peticiones. → Espere los segundos de Retry-After y reintente.
invalid_json 400 invalid_request_errorEl cuerpo no es JSON válido. → Envíe Content-Type: application/json y un objeto JSON.
invalid_parameter 400 invalid_request_errorUn parámetro no es válido (ver param). → Corrija el campo indicado en error.param.
invalid_purpose 400 invalid_request_errorpurpose desconocido. → Use client_onboarding, employment, rental, purchase, business_partner o civil_matter.
invalid_metadata 400 invalid_request_errormetadata no cumple los límites. → Máx. 20 claves de ≤ 40 caracteres, valores string ≤ 500.
applicant_required 400 invalid_request_errorFalta applicant.name.
consent_required 422 invalid_request_errorFalta consent_confirmed: true. → Confirme que la persona aceptó la comprobación.
documents_required 400 invalid_request_errorSin documentos. → Envíe de 1 a 20 documentos.
too_many_documents 400 invalid_request_errorMás de 20 documentos. → Divida el caso o combine páginas en un PDF.
invalid_document 400 invalid_request_errorDocumento sin url ni data+media_type.
unsupported_media_type 415 invalid_request_errorFormato no admitido. → PDF, JPEG, PNG o WebP.
document_too_large 413 invalid_request_errorUn documento pasa de 10 MB. → Comprima la imagen o reduzca la resolución.
documents_too_large 413 invalid_request_errorLos documentos pasan de 20 MB en total.
document_url_unreachable 422 invalid_request_errorNo pudimos descargar una URL (15 s). → Use una URL firmada válida ≥ 10 min, o envíe base64.
invalid_settings 400 invalid_request_errorAjustes no válidos (ver mensaje).
invalid_url 400 invalid_request_errorLa URL debe empezar por https://.
too_many_endpoints 409 invalid_request_errorMáximo 10 endpoints.
link_not_pending 409 invalid_request_errorSolo se cancela un enlace pendiente.
idempotency_conflict 409 idempotency_errorIdempotency-Key reutilizada con otro cuerpo. → Genere una clave nueva por operación.
link_expired 410 invalid_request_errorEnlace caducado o ya usado. → Cree un enlace nuevo.
not_found 404 invalid_request_errorEl objeto no existe (o es de otro entorno). → Las claves test no ven objetos live y viceversa.
method_not_allowed 405 invalid_request_errorMétodo no permitido en esta ruta.
server_error 500 api_errorError nuestro. → Es seguro reintentar (con la misma Idempotency-Key).
| Peticiones por clave | 120 / min live · 60 / min sandbox · ampliable bajo petición |
| Documentos por comprobación | 1–20 · ≤ 10 MB cada uno · ≤ 20 MB en total |
| Formatos | PDF, JPEG, PNG, WebP |
| Descarga de url | 15 s por documento |
| Webhook endpoints | 10 por cuenta · respuesta en 10 s |
| Eventos consultables | 30 días |
| Conservación de comprobaciones | según su contrato (por defecto 365 días); después se borran solas |
Al pasar el límite: 429 rate_limited con Retry-After. Para cargas masivas, distribuya las peticiones o pida un límite mayor.
Una comprobación = una persona (o empresa) con 1 a 20 documentos. Es asíncrona: se crea con status: "processing" y termina en completed o failed. Solo se cobran las comprobaciones live completadas.
/v1/screeningsscreenings:writeapplicant.name stringobligatorioNombre de la persona (o empresa).
applicant.email / phone / date_of_birth / id_number / company / address / country stringMás datos ayudan al cruce de identidad. date_of_birth en YYYY-MM-DD, country ISO alpha-2.
documents[] arrayobligatorio1–20 objetos: { url } (https, lo descargamos) o { data, media_type } (base64). type opcional como pista: payslip, bank_statement, id, employment_contract, tax_return, rental_contract, purchase_agreement, registration_docs, annual_accounts…
consent_confirmed trueobligatorioUsted confirma que la persona aceptó la comprobación.
purpose enumclient_onboarding (KYC) · employment (contratación) · rental · purchase · business_partner · civil_matter. Cambia qué se valora: identidad y origen de fondos en un alta, historial laboral en una contratación, ingresos en un alquiler. Por defecto: su ajuste.
country stringPaís de los documentos (ISO alpha-2). Mejora la extracción.
case_details objectHechos que el motor compara con los documentos: empleador, dirección, alquiler, precio, origen de fondos declarado… Cada diferencia sale como desviación. Plano, ≤ 30 claves snake_case. Ej.: { "monthly_rent": 1200, "employer": "Acme SL" }
reference stringSu propio ID (≤ 120). Filtrable.
metadata objectVer metadata.
lang en | esIdioma del resumen y de las desviaciones.
sandbox_outcome enumSolo sandbox: approve, review, decline, fail.
curl https://api.validento.com/v1/screenings \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"applicant": { "name": "Ana García", "email": "ana@example.com" },
"purpose": "client_onboarding",
"country": "ES",
"reference": "case-1042",
"consent_confirmed": true,
"case_details": { "source_of_funds": "salary", "employer": "Acme SL" },
"documents": [
{ "type": "payslip", "url": "https://files.yourapp.com/signed/payslip-sept.pdf" },
{ "type": "bank_statement", "media_type": "application/pdf", "data": "JVBERi0xLjQK..." }
],
"metadata": { "customer_id": "c_981" }
}'import Validento from './validento.mjs';
const vd = new Validento(process.env.VALIDENTO_KEY);
const screening = await vd.screenings.create({
applicant: { name: 'Ana García', email: 'ana@example.com' },
purpose: 'client_onboarding', country: 'ES', reference: 'case-1042',
consent_confirmed: true,
case_details: { source_of_funds: 'salary', employer: 'Acme SL' },
documents: [
{ type: 'payslip', url: 'https://files.yourapp.com/signed/payslip-sept.pdf' },
{ type: 'bank_statement', media_type: 'application/pdf', data: fs.readFileSync('statement.pdf').toString('base64') },
],
metadata: { customer_id: 'c_981' },
});
console.log(screening.id, screening.status); // "processing"import os, uuid, base64, requests
H = {"Authorization": f"Bearer {os.environ['VALIDENTO_KEY']}"}
r = requests.post("https://api.validento.com/v1/screenings",
headers={**H, "Idempotency-Key": str(uuid.uuid4())},
json={
"applicant": {"name": "Ana García", "email": "ana@example.com"},
"purpose": "client_onboarding", "country": "ES", "reference": "case-1042",
"consent_confirmed": True,
"case_details": {"source_of_funds": "salary", "employer": "Acme SL"},
"documents": [
{"type": "payslip", "url": "https://files.yourapp.com/signed/payslip-sept.pdf"},
{"type": "bank_statement", "media_type": "application/pdf",
"data": base64.b64encode(open("statement.pdf", "rb").read()).decode()},
],
"metadata": {"customer_id": "c_981"},
}, timeout=60)
r.raise_for_status()
print(r.json()["id"])<?php
function validento(string $method, string $path, ?array $body = null): array {
$ch = curl_init("https://api.validento.com" . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => array_filter([
"Authorization: Bearer " . getenv("VALIDENTO_KEY"), "Content-Type: application/json",
$method === "POST" ? "Idempotency-Key: " . bin2hex(random_bytes(16)) : null,
]),
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$res = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) throw new RuntimeException($res["error"]["code"] ?? "error");
return $res;
}
$s = validento("POST", "/v1/screenings", [
"applicant" => ["name" => "Ana García"], "purpose" => "client_onboarding", "consent_confirmed" => true,
"case_details" => ["source_of_funds" => "salary", "employer" => "Acme SL"],
"documents" => [["type" => "payslip", "url" => "https://files.yourapp.com/signed/payslip-sept.pdf"]],
]);
echo $s["id"];
Respuesta 202 con el objeto en processing. Use siempre Idempotency-Key.
{
"id": "6c1f6a2e-4b1d-4f7e-9c55-2a8f0d7b3e11",
"object": "screening",
"environment": "live",
"status": "completed",
"channel": "api",
"reference": "case-1042",
"purpose": "client_onboarding",
"country": "ES",
"applicant": { "name": "Ana García", "email": "ana@example.com" },
"document_count": 3,
"decision": "approve",
"score": 82,
"result": {
"score": 82,
"decision": "approve",
"display_score": "82/100",
"summary": "Documents are consistent; net income matches the bank statements.",
"detected_document_type": "payslip",
"detected_country": "ES",
"documents_checked": 3,
"deviations": [
{ "field": "employer_name", "severity": "low", "note": "Abbreviated on one payslip" }
],
"fields": [ { "name": "net_monthly_income", "value": "3180.00" } ],
"income": { "monthly_amount": 3180, "currency": "EUR" },
"breakdown": { "document_integrity": 88, "financial_capacity": 79, "identity": 84 }
},
"case_details": { "employer": "Acme SL" },
"error": null,
"metadata": { "customer_id": "c_981" },
"link_id": null,
"created_at": "2026-10-04T09:12:03.120Z",
"completed_at": "2026-10-04T09:12:41.877Z"
}
score 0–100Siempre la misma escala, sus reglas deciden.
decision approve | review | declineSegún sus reglas de decisión (por defecto ≥ 70 / ≥ 45).
result.deviations[] arrayLo que no cuadra: entre documentos, con case_details o señales de manipulación. severity low/medium/high.
result.fields[] arrayDatos extraídos (empleador, neto, IBAN parcial, fechas…).
result.income objectIngreso mensual detectado y moneda.
result.breakdown objectSubpuntuaciones 0–100: integridad documental, capacidad financiera, identidad.
result.display_score stringLa puntuación en su escala (82/100, 8.2/10 o B).
error string | nullSi failed: engine_unavailable u otro motivo. No se cobra; puede repetir.
result recibe lo decide usted en result_format./v1/screeningsscreenings:readFiltros: reference, status, decision, purpose, channel (api/link/dashboard), link_id, metadata[clave], created[gte|lte] + paginación.
/v1/screenings/{id}screenings:read/v1/screenings/{id}/report?lang=enscreenings:readInforme HTML imprimible con su marca. Conviértalo en PDF con cualquier navegador o Chrome headless (Puppeteer: page.pdf()).
/v1/screenings/{id}screenings:writeBorrado definitivo (RGPD): elimina la comprobación y los datos de la persona en su enlace; el uso facturado se conserva sin datos personales. Emite screening.deleted.
¿No tiene los documentos? Cree un enlace: la persona los sube en una página con su marca (o dentro de su app) y usted recibe link.completed + screening.completed.
/v1/verification-linkslinks:writeapplicant.name stringobligatorioNombre que verá la persona.
applicant.email stringNecesario con send_email.
send_email booleanEnviamos el enlace por email con su marca (respuestas a su email de soporte).
locale es | enIdioma de la página y del email.
documents_requested string[]Lista que ve la persona (≤ 20).
expires_in_days 1–30Por defecto: su ajuste (7).
redirect_url https URLAdónde volver al terminar.
purpose / country / reference / case_details / metadata / brand_name Como en una comprobación; pasan a la comprobación resultante.
curl https://api.validento.com/v1/verification-links \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"applicant": { "name": "Ana García", "email": "ana@example.com" },
"purpose": "employment",
"reference": "case-1042",
"documents_requested": ["ID", "Last payslip", "Employment letter"],
"send_email": true,
"locale": "es"
}'const link = await vd.verificationLinks.create({
applicant: { name: 'Ana García', email: 'ana@example.com' },
purpose: 'employment', reference: 'case-1042',
documents_requested: ['ID', 'Last payslip', 'Employment letter'],
send_email: true, locale: 'es',
});
console.log(link.url, link.embed_url);link = requests.post("https://api.validento.com/v1/verification-links", headers={**H, "Idempotency-Key": str(uuid.uuid4())}, json={
"applicant": {"name": "Ana García", "email": "ana@example.com"},
"purpose": "employment", "reference": "case-1042",
"documents_requested": ["ID", "Last payslip", "Employment letter"],
"send_email": True, "locale": "es",
}).json()
print(link["url"])$link = validento("POST", "/v1/verification-links", [
"applicant" => ["name" => "Ana García", "email" => "ana@example.com"],
"purpose" => "employment", "reference" => "case-1042",
"send_email" => true, "locale" => "es",
]);
echo $link["url"];
url y embed_url llevan un token secreto y solo se devuelven al crear. Guárdelos si los necesita después.Cree el enlace en su servidor y pase embed_url al navegador. Sin claves en el frontend.
<script src="https://validento.com/embed.js"></script>
<button id="verify">Verify my documents</button>
<script>
document.getElementById('verify').onclick = () => {
Validento.open({
url: EMBED_URL_FROM_YOUR_SERVER,
onReady: () => console.log('page loaded'),
onComplete: (e) => console.log('submitted', e.screening_id), // result follows by webhook
onClose: () => console.log('closed'),
closeDelay: 2500,
});
};
// Or inline: Validento.mount('#verify-box', { url: EMBED_URL, onComplete })
</script>
/v1/verification-linkslinks:readFiltros: status (pending/completed/expired/canceled), reference, purpose + paginación.
/v1/verification-links/{id}links:read/v1/verification-links/{id}/cancellinks:writeEnvíe a una persona un enlace: en su móvil fotografía su documento y hace un breve escaneo facial en vivo. Comprobamos los dígitos de control MRZ (ICAO 9303), la caducidad, la coincidencia facial y la presencia. No guardamos imágenes; usted recibe la decisión y un informe con las cifras.
/v1/id-checksid_checks:writeapplicant.name stringobligatorioComo en el documento (mín. 3 caracteres).
applicant.email stringobligatorioPara el email con el enlace y el recibo.
purpose stringPara qué es; la persona lo ve.
reference stringSu id (CRM, expediente); vuelve en cada evento.
send_email booleanLe enviamos el enlace en su nombre. Si no, use url (solo en esta respuesta).
locale es | enIdioma del email y de la página.
expires_in_days integer1–30, por defecto 14.
metadata objectHasta 20 pares clave/valor.
curl https://api.validento.com/v1/id-checks \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-d '{"applicant":{"name":"Ana García","email":"ana@example.com"},"purpose":"New client — file 1042","reference":"crm-1042","send_email":true,"locale":"es"}'const check = await vd.idChecks.create({
applicant: { name: 'Ana García', email: 'ana@example.com' },
purpose: 'New client — file 1042', reference: 'crm-1042', send_email: true, locale: 'es',
});
console.log(check.status, check.url); // "pending", https://validento.com/id/…r = requests.post("https://api.validento.com/v1/id-checks", headers={"Authorization": f"Bearer {key}"},
json={"applicant": {"name": "Ana García", "email": "ana@example.com"}, "reference": "crm-1042", "send_email": True})
print(r.json()["status"])$body = json_encode(["applicant" => ["name" => "Ana García", "email" => "ana@example.com"], "reference" => "crm-1042", "send_email" => true]);
| status | Significado |
|---|---|
pending | Esperando a la persona. |
retry | Un intento falló (foto, caducidad, cara); puede repetir hasta 3 veces. |
in_review | Un especialista de Validento decide desde el informe — nunca un rechazo automático por una foto mala. |
passed / rejected | Decisión final (evento id_check.completed). |
canceled / expired | Cancelada, o no terminada a tiempo. |
/v1/id-checksid_checks:readFiltros: status, reference, metadata[clave] + paginación.
/v1/id-checks/{id}id_checks:read/v1/id-checks/{id}/cancelid_checks:write/v1/id-checks/{id}/simulateid_checks:writeSandbox: con una clave vld_test_…, simulate con {"outcome":"passed"} (o retry, in_review, rejected) juega el resultado sin persona y envía su webhook. Facturación: una comprobación del mes cuando la persona envía su primer intento (live).
Compruebe una persona o empresa frente a las listas internacionales de sanciones (ONU, UE, OFAC de EE. UU., Reino Unido y muchas más), personas con responsabilidad pública (PEP) y listas de vigilancia penal, con datos de OpenSanctions actualizados a diario. La respuesta llega al instante.
/v1/sanctions-screeningssanctions:writesubject.name stringobligatorioNombre completo de la persona o razón social.
subject.kind person | companyPor defecto person.
subject.birth_date stringYYYY o YYYY-MM-DD (persona). Reduce mucho las coincidencias falsas.
subject.nationality stringISO 3166-1 alfa-2 (persona).
subject.country stringISO 3166-1 alfa-2, jurisdicción (empresa).
subject.registration_number stringNúmero de registro mercantil (empresa).
reference stringSu id; vuelve en el objeto y en cada evento.
customer stringSu id de cliente (Customers).
metadata objectHasta 20 pares clave/valor.
curl https://api.validento.com/v1/sanctions-screenings \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-1042-sanctions" \
-d '{"subject":{"kind":"person","name":"Ana García López","birth_date":"1986-03-14","nationality":"ES"},"reference":"crm-1042"}'const s = await vd.sanctionsScreenings.create({
subject: { kind: 'person', name: 'Ana García López', birth_date: '1986-03-14', nationality: 'ES' },
reference: 'crm-1042',
});
if (s.status === 'clear') proceed();
else if (s.status === 'in_review') hold(); // wait for sanctions_screening.reviewedr = requests.post("https://api.validento.com/v1/sanctions-screenings", headers={"Authorization": f"Bearer {key}"},
json={"subject": {"kind": "company", "name": "Acme Trading Ltd", "country": "GB"}, "reference": "supplier-77"})
print(r.json()["status"])$body = json_encode(["subject" => ["name" => "Ana García López", "birth_date" => "1986-03-14"], "reference" => "crm-1042"]);
| status | Significado |
|---|---|
clear | Ninguna coincidencia por encima del umbral. |
in_review | Un nombre coincide con una entrada de una lista. No es una conclusión: un especialista de Validento compara fecha de nacimiento, país y otros datos, normalmente en un día laborable. Mientras tanto, no tome decisiones negativas solo por esto. |
no_match | Revisado: no es la misma persona o empresa (con nota en review.note). |
confirmed_match | Revisado: la coincidencia se confirma. Siga sus propios procedimientos (p. ej. su oficial de cumplimiento). |
failed | No se pudo consultar las listas; no se factura. Reintente (con la misma Idempotency-Key no se duplica). |
result.hits lista las entradas parecidas: caption, score (0–1), topics (p. ej. sanction, role.pep, crime), datasets, countries, birth_dates y un url público. result.sanctioned y result.pep resumen las coincidencias.
/v1/sanctions-screeningssanctions:readFiltros: status, reference, customer, metadata[clave] + paginación.
/v1/sanctions-screenings/{id}sanctions:readCómo integrarlo
clear → continúe. in_review → ponga el expediente en espera.sanctions_screening.reviewed: data.status es no_match o confirmed_match.id en su expediente KYC como evidencia (fecha, listas, decisión).Facturación: cada comprobación live es una unidad de su uso Enterprise (no se factura si failed). Sandbox: con una clave vld_test_… no se consultan listas; un nombre que contiene «match» simula una posible coincidencia (in_review), cualquier otro da clear. Permisos: sanctions:read / sanctions:write.
Le enviamos un POST firmado cuando algo cambia. Cada entorno tiene sus propios endpoints.
| Evento | Cuándo |
|---|---|
screening.created | Comprobación creada (status processing). |
screening.completed | Resultado disponible: score, decision, result. |
screening.failed | No se pudo completar (no se cobra). |
screening.deleted | Borrada (RGPD) vía API o panel. |
link.created | Enlace de verificación creado. |
link.completed | La persona envió sus documentos; data.screening_id. |
link.canceled | Enlace cancelado. |
link.expired | El enlace caducó sin usarse. |
id_check.created | Verificación de identidad creada (también desde el panel). |
id_check.completed | Decisión final: data.status passed o rejected (también tras revisión). |
id_check.retry | Un intento falló; la persona puede repetir (data.attempts_left). |
id_check.in_review | Un especialista decide desde el informe. |
id_check.canceled | Verificación cancelada. |
sanctions_screening.completed | Comprobación de sanciones hecha: data.status clear o in_review. |
sanctions_screening.reviewed | Un especialista decidió una posible coincidencia: data.status no_match o confirmed_match. |
sanctions_screening.failed | No se pudieron consultar las listas (no facturado). |
ping | Prueba desde el panel o la API. |
POST /your/webhook HTTP/1.1
Content-Type: application/json
Validento-Signature: t=1759569161,v1=5f2b0c8e…
Validento-Event: screening.completed
Validento-Event-Id: evt_8sK2pQ…
Validento-Delivery: 2b1c…
Validento-Version: 2026-10-01
{
"id": "evt_8sK2pQ…",
"object": "event",
"type": "screening.completed",
"environment": "live",
"api_version": "2026-10-01",
"created": 1759569161,
"data": { "id": "6c1f6a2e-…", "object": "screening", "status": "completed", "decision": "approve", "score": 82, "…": "…" }
}
v1 = HMAC-SHA256 en hex de "{t}.{cuerpo crudo}" con su secreto whsec_…. Use el cuerpo exacto recibido (no lo vuelva a serializar), compare en tiempo constante y rechace timestamps de más de 5 minutos.
import express from 'express';
import crypto from 'node:crypto';
const app = express();
app.post('/webhooks/validento', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('Validento-Signature') || '';
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', process.env.VALIDENTO_WHSEC)
.update(`${t}.${req.body}`).digest('hex');
const ok = v1 && v1.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
if (!ok || Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(400);
const event = JSON.parse(req.body);
res.sendStatus(200); // answer fast, work after
queue.add(event); // dedupe on event.id
});
// Or with the SDK: const event = await Validento.webhooks.verify(rawBody, header, secret);import hmac, hashlib, time, json
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/webhooks/validento")
def validento_webhook():
raw = request.get_data() # bytes, exactly as received
parts = dict(p.split("=", 1) for p in request.headers.get("Validento-Signature", "").split(","))
expected = hmac.new(WHSEC.encode(), f"{parts.get('t')}.".encode() + raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts.get("v1", "")) or abs(time.time() - int(parts.get("t", 0))) > 300:
abort(400)
event = json.loads(raw)
enqueue(event) # dedupe on event["id"]
return "", 200<?php
$raw = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_VALIDENTO_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $raw, getenv('VALIDENTO_WHSEC'));
if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int)($sig['t'] ?? 0)) > 300) {
http_response_code(400); exit;
}
$event = json_decode($raw, true);
http_response_code(200);
// process $event (dedupe on $event['id'])# Go
func verify(raw []byte, header, secret string) bool {
var t, v1 string
for _, p := range strings.Split(header, ",") {
kv := strings.SplitN(p, "=", 2)
if len(kv) == 2 && kv[0] == "t" { t = kv[1] }
if len(kv) == 2 && kv[0] == "v1" { v1 = kv[1] }
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(t + "."))
mac.Write(raw)
ts, _ := strconv.ParseInt(t, 10, 64)
return hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(v1)) && math.Abs(float64(time.Now().Unix()-ts)) <= 300
}
event.id y, si importa el estado, consulte el objeto con GET.GET /v1/events y reenvíe con POST /v1/events/{id}/resend./v1/webhook-endpointswebhooks:read/v1/webhook-endpointswebhooks:write/v1/webhook-endpoints/{id}webhooks:write/v1/webhook-endpoints/{id}webhooks:write/v1/webhook-endpoints/{id}/roll-secretwebhooks:write/v1/webhook-endpoints/{id}/testwebhooks:writecurl https://api.validento.com/v1/webhook-endpoints \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" -H "Content-Type: application/json" \
-d '{ "url": "https://api.yourapp.com/webhooks/validento", "events": ["screening.completed", "screening.failed", "link.completed"] }'
# → { "id": "…", "secret": "whsec_…" } (the secret is shown once)const ep = await vd.webhookEndpoints.create({ url: 'https://api.yourapp.com/webhooks/validento', events: ['screening.completed', 'screening.failed'] });
console.log(ep.secret); // store it now
console.log(await vd.webhookEndpoints.test(ep.id)); // { delivered: true, status: 200 }ep = requests.post("https://api.validento.com/v1/webhook-endpoints", headers=H, json={"url": "https://api.yourapp.com/webhooks/validento", "events": ["screening.completed"]}).json()
print(ep["secret"])$ep = validento("POST", "/v1/webhook-endpoints", ["url" => "https://api.yourapp.com/webhooks/validento", "events" => ["screening.completed"]]);
echo $ep["secret"];
Cada evento se guarda 30 días, tenga o no endpoints. Úselos para conciliar, recuperar caídas o construir sin webhooks.
/v1/events?type=screening.completed,link.completedevents:read/v1/events/{id}events:read/v1/events/{id}/resendwebhooks:writeresend entrega de nuevo a todos los endpoints suscritos, o a uno con { "endpoint_id": "…" } (aunque no esté suscrito).
// Catch up after downtime: everything since the last event you processed
for await (const ev of vd.events.listAll({ type: ['screening.completed', 'screening.failed'], created: { gte: lastSeenUnix } })) {
await handle(ev); // same handler as your webhook
}curl -G https://api.validento.com/v1/events -H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-d type=screening.completed -d "created[gte]=1759569000" -d limit=100
Sus reglas y su marca, por API o en el panel (Business → Ajustes). Valen para live y sandbox. Envíe solo lo que cambia; null restablece un campo.
/v1/settingssettings:read/v1/settingssettings:writedecision.approve_min / review_min 1–100Umbrales de la decisión (por defecto 70 / 45).
branding.display_name / logo_url / primary_color / support_email / privacy_url stringSu marca en la página de subida, emails e informes.
branding.hide_powered_by booleanOculta “con la tecnología de Validento”.
link_defaults.* objectpurpose, country, expiry_days, documents_requested, redirect_url, email_applicant, language.
result_format.summary / deviations / fields / income / breakdown / applicant booleanQué partes recibe por API y webhooks.
result_format.score_scale 100 | 10 | gradeFormato de display_score.
notifications.recipients / on_review / on_decline / on_failed / on_completed Emails a su equipo por comprobación live.
curl -X PATCH https://api.validento.com/v1/settings \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" -H "Content-Type: application/json" \
-d '{ "decision": { "approve_min": 75 }, "result_format": { "fields": false, "score_scale": "10" }, "branding": { "primary_color": "#6D28D9" } }'await vd.settings.update({ decision: { approve_min: 75 }, result_format: { fields: false, score_scale: '10' } });
/v1/usageusage:readPeriodo actual (del 20 al 19), comprobaciones, importe por tramos, mínimo, saldo (prepago) o límite de crédito (factura).
/v1/invoicesusage:readFacturas y extractos mensuales con enlace a la factura de Stripe y su PDF.
Para software que verifica en nombre de sus propios clientes — plataformas de KYC y cumplimiento, ATS y agencias de selección, aseguradoras con corredores, software para despachos. Registre cada cliente una vez y etiquete cada comprobación, enlace, verificación de identidad y webhook con customer. Cada cliente tiene su marca, sus webhooks, su consumo y su borrado.
/v1/customerscustomers:writeid stringobligatorioSu propio id del cliente (1–64; letras, dígitos, _ . -). Es lo que pasa después en customer.
name stringobligatorioNombre (≤ 120).
brand_name / logo_url / primary_color / support_email stringMarca de ese cliente en la página de subida, los emails y el informe. Lo que no indique hereda su marca de cuenta.
metadata objectSus propios datos (≤ 20 claves).
curl https://api.validento.com/v1/customers \
-H "Authorization: Bearer vld_test_YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-d '{"id":"acme","name":"Acme Lettings Ltd","brand_name":"Acme","primary_color":"#1D4ED8","support_email":"help@acme.example"}'await vd.request('POST', '/v1/customers', { id: 'acme', name: 'Acme Lettings Ltd', brand_name: 'Acme', primary_color: '#1D4ED8' });
// then, on every object for that customer:
const link = await vd.verificationLinks.create({
customer: 'acme', purpose: 'employment',
applicant: { name: 'Ana García', email: 'ana@example.com' }, send_email: true,
});
A partir de ahí, en cada POST /v1/screenings, /v1/verification-links, /v1/id-checks y /v1/webhook-endpoints: "customer": "acme". Un cliente desconocido devuelve unknown_customer. Los objetos devuelven customer y las listas filtran con ?customer=acme.
| Marca | La página de subida, el email con el enlace y el informe llevan la marca del cliente (brand_name, logo, color, email de soporte). Sin ella, la suya. |
|---|---|
| Webhooks | Un endpoint creado con customer recibe solo los eventos de ese cliente (hasta 200 endpoints así). Los endpoints sin cliente reciben todo; el evento lleva data.customer. |
| Consumo | GET /v1/customers/{id} devuelve usage: comprobaciones completadas este periodo y en total — para facturar a su cliente. |
| Borrado | POST /v1/customers/{id}/erase elimina todas sus comprobaciones, enlaces y verificaciones de identidad (RGPD, irreversible). DELETE elimina solo la ficha del cliente. |
/v1/customerscustomers:read/v1/customers/{id}customers:read/v1/customers/{id}customers:write/v1/customers/{id}customers:write/v1/customers/{id}/erasecustomers:writecustomers:read y customers:write. Una clave por cliente final no hace falta: etiquete con customer y filtre.Conecte Validento con más de 7.000 apps sin programar: cuando termina una comprobación, actualice su CRM, avise en Slack o guarde el resultado en una hoja de cálculo; cuando entra un candidato, envíele un enlace de verificación.
| Disparadores (al instante) | Screening Completed · Screening Failed · Verification Link Completed · Verification Link Expired · ID Check Completed · Sanctions Screening Completed · Sanctions Screening Reviewed |
|---|---|
| Acciones | Create Verification Link · Create ID Check · Screen for Sanctions & PEP |
| Búsqueda | Find Screening (por id o por su referencia) |
| Conexión | Una clave API (vld_test_… para probar gratis). |
La app está en acceso por invitación: pídanos el enlace en info@validento.com. Cada Zap activo crea un endpoint de webhook para su evento (máx. 10 por cuenta).
En Zapier elija Webhooks by Zapier → Catch Hook y copie la URL.
Panel Enterprise → Desarrolladores → Webhooks → pegue la URL y elija los eventos. Pulse “Probar” para que Zapier vea un ejemplo.
El objeto está en data: data.score, data.decision, data.reference, data.metadata.*.
Webhooks by Zapier → Custom Request: POST ${API}/v1/verification-links con la cabecera Authorization: Bearer vld_….
Webhooks by Zapier requiere un plan de pago de Zapier.
En Make (antes Integromat) no hace falta una app: use los módulos estándar.
Módulo Webhooks → Custom webhook → copie la URL → regístrela en el panel (Desarrolladores → Webhooks) → “Probar” para que Make detecte la estructura.
Módulo HTTP → Make a request: URL ${API}/v1/…, cabecera Authorization: Bearer vld_…, cuerpo JSON. Marque “Parse response”.
Ponga un filtro en type (p. ej. screening.completed) si el endpoint recibe varios eventos.
{
"applicant": { "name": "{{1.name}}", "email": "{{1.email}}" },
"purpose": "client_onboarding",
"reference": "{{1.deal_id}}",
"send_email": true,
"metadata": { "source": "make" }
}
Hoy los CRM se conectan a través de Zapier o Make (ambos tienen apps oficiales de Pipedrive, Salesforce y HubSpot). La clave es la referencia: ponga el id del trato o contacto de su CRM en reference (o en metadata) al crear el enlace o la comprobación; vuelve en cada evento.
| Pipedrive | Trato en etapa “Verificar” → Create Verification Link (reference = id del trato). Screening Completed → Pipedrive “Add note” / “Update deal” con score y decisión. |
|---|---|
| Salesforce | Nuevo Lead/Opportunity → Create Verification Link. Screening Completed → “Update record” (campos propios: Validento Score, Decision, Report link). |
| HubSpot | Igual: la propiedad del contacto o del negocio se actualiza con el resultado. |
Conectores nativos (sin Zapier/Make) en el Marketplace de Pipedrive o en Salesforce AppExchange: en la hoja de ruta.
Para equipos de RR. HH., agencias de selección y plataformas de contratación: verifique identidad, documentos e historial laboral de un candidato de otro país antes de la oferta. La referencia es el id del candidato o de la vacante en su ATS; el resultado vuelve por webhook a ese registro.
| Flujo | Candidato pasa a “Verificación” → POST /v1/verification-links con purpose: "employment", documents_requested (DNI o pasaporte, última nómina, carta de empleo, titulación) y send_email: true. El candidato sube en una página con su marca. screening.completed → nota o campo en el candidato. |
|---|---|
| Identidad | Añada POST /v1/id-checks (ValiD): el candidato verifica su documento y su cara en el móvil; sin imágenes guardadas. |
| Agencias | ¿Verifica para varios empleadores? Registre cada uno como cliente: su marca en la página del candidato, sus webhooks y su consumo. |
| Greenhouse · Lever · Workable · Teamtailor · Recruitee | Hoy a través de Zapier o Make (todos tienen app oficial): disparador “candidato cambia de fase” → Create Verification Link; Screening Completed → “Add note” o campo personalizado. |
{
"customer": "{{employer_id}}",
"purpose": "employment",
"applicant": { "name": "{{candidate.name}}", "email": "{{candidate.email}}" },
"documents_requested": ["ID or passport", "Last payslip", "Employment letter", "Diploma"],
"reference": "{{candidate.id}}",
"send_email": true,
"redirect_url": "https://careers.yourcompany.com/verified"
}
Para clientes que no pueden enviar documentos fuera de su propia infraestructura: los contenedores de análisis de Validento, ejecutados por usted. Licencia anual, mismas respuestas que la API, actualizaciones como nuevas imágenes.
| verify-service | Análisis de documentos e ingresos. Se ejecuta en su proyecto de Google Cloud y usa su Vertex AI (su región, sus condiciones de tratamiento). Validento nunca ve los documentos. |
|---|---|
| idv-service (ValiD) | Identidad: documento, cara y presencia en vivo. Totalmente offline: modelos abiertos dentro de la imagen, sin ninguna llamada saliente salvo el heartbeat opcional. 2 vCPU, 4 GB, sin GPU. |
| Licencia | Un token firmado (Ed25519) en VALIDENTO_LICENSE + la clave pública en VALIDENTO_LICENSE_PUBKEY. Se verifica offline al arrancar; sin token válido el contenedor no arranca; al caducar responde 403 license_inactive. Aviso en el log desde 30 días antes. |
| Heartbeat | Opcional, por licencia: una vez al día el contenedor envía { license_id, product, version, instance } — nunca documentos, imágenes ni resultados. Permite a Validento revocar una licencia. Sin heartbeat, ninguna llamada saliente. |
| Imágenes | europe-west4-docker.pkg.dev/validento-com/private/<service>:<tag> — registro privado; su cuenta de servicio recibe acceso de lectura. Etiquetas YYYY.MM; fije una en producción. |
docker run -d -p 8080:8080 \
-e VALIDENTO_LICENSE="vdl1.…" -e VALIDENTO_LICENSE_PUBKEY="…" \
-e GOOGLE_CLOUD_PROJECT="your-gcp-project" -e GOOGLE_CLOUD_LOCATION="europe-west4" \
-e GOOGLE_APPLICATION_CREDENTIALS=/secrets/sa.json -v /path/sa.json:/secrets/sa.json:ro \
-e VERIFY_API_KEY="a-long-random-string" \
europe-west4-docker.pkg.dev/validento-com/private/verify-service:2026.10
curl -X POST http://localhost:8080/verify -H "apikey: a-long-random-string" -H "Content-Type: application/json" \
-d '{"purpose":"client_onboarding","country":"ES","lang":"en","files":[{"media_type":"application/pdf","data":"<base64>"}]}'
docker run -d -p 8081:8080 \
-e VALIDENTO_LICENSE="vdl1.…" -e VALIDENTO_LICENSE_PUBKEY="…" \
-e IDV_API_KEY="another-long-random-string" -e IDV_SESSION_SECRET="$(openssl rand -hex 32)" \
europe-west4-docker.pkg.dev/validento-com/private/idv-service:2026.10
# POST /v1/session → challenges; POST /v1/check (document, selfie, frames) → { decision, signals } — header x-api-key
POST /verify devuelve score, summary, detected_type, detected_country, fields[], deviations[], components, cross_check e ingresos detectados — la misma información que result en la API, sin la capa de cuenta (sin webhooks, enlaces ni panel: eso lo hace su sistema). GET /health en ambos.
Para empresas que ofrecen la verificación de Validento a sus propios clientes — software para despachos y asesorías, plataformas de KYC y onboarding, sistemas de selección (ATS) y nóminas, financieras y aseguradoras, plataformas de alquiler y software inmobiliario, marketplaces B2B. Una cuenta Enterprise, una API, su marca.
| Su marca | Logo, colores y nombre en el panel (Ajustes); cada cliente puede tener la suya (brand_name, logo, color, email de soporte en su ficha de cliente). |
|---|---|
| Por cliente final | Registre cada cliente final en /v1/customers y pase customer en cada llamada: marca, webhooks, consumo y borrado por cliente. Filtre con GET /v1/screenings?customer=acme. |
| En su producto | Enlace por email, embed en su web o app, o envío directo de documentos por API. |
| Resultados | Webhooks firmados y informe con su marca para reenviar a su cliente. |
| Facturación | Una factura mensual por volumen para usted; usted factura a sus clientes con el consumo de GET /v1/customers/{id}. |
Aún no hay subcuentas con inicio de sesión propio para sus clientes finales; se separan por metadata. Hable con su gestor de cuenta para condiciones de canal.
Un solo archivo, sin dependencias, para Node 18+, Deno, Bun y entornos edge. Reintentos con backoff (respeta Retry-After), Idempotency-Key automática, paginación con for await, errores tipados y verificación de webhooks. Tipos en validento.d.ts.
⬇ validento.mjs ⬇ validento.d.ts
import Validento, { ValidentoError } from './validento.mjs'; // Deno: 'https://validento.com/sdk/validento.mjs'
const vd = new Validento(process.env.VALIDENTO_KEY!, { maxRetries: 3, timeoutMs: 60_000 });
try {
const s = await vd.screenings.create({
applicant: { name: 'Ana García' }, consent_confirmed: true, purpose: 'purchase',
documents: [{ url: signedUrl, type: 'payslip' }],
case_details: { purchase_price: 320000 }, metadata: { customer_id: 'c_981' },
});
const done = await vd.screenings.waitFor(s.id); // polling helper; prefer webhooks in production
console.log(done.decision, done.result?.deviations);
} catch (err) {
if (err instanceof ValidentoError) console.error(err.status, err.code, err.param, err.requestId);
else throw err;
}
console.log(vd.lastResponse?.rateLimit);
¿Otro lenguaje? Genere un cliente desde la especificación OpenAPI 3.1 (openapi-generator, oapi-codegen, NSwag…) o impórtela en Postman / Insomnia.
# Postman: Import → Link → https://api.validento.com/v1/openapi.json
npx @openapitools/openapi-generator-cli generate -i https://api.validento.com/v1/openapi.json -g python -o ./validento-python
npx @openapitools/openapi-generator-cli generate -i https://api.validento.com/v1/openapi.json -g go -o ./validento-go
DELETE /v1/screenings/{id}.consent_confirmed registra la base de su comprobación.2026-10-09 — Nuevo recurso Sanciones y PEP (OpenSanctions): POST /v1/sanctions-screenings, eventos sanctions_screening.*, permisos sanctions:*. Sin cambios incompatibles.
2026-10-09 — Despliegue privado: verify-service e idv-service como contenedores con licencia, ejecutados por el cliente.
2026-10-08 — purpose admite client_onboarding y employment. Nuevo recurso Customers (multi-tenant): customer en comprobaciones, enlaces, verificaciones de identidad y webhooks; marca, consumo y borrado por cliente. Sin cambios incompatibles.
Escríbanos a info@validento.com con el Validento-Request-Id y la hora aproximada. Clientes Enterprise: también su gestor de cuenta. Respondemos en horario laboral europeo, normalmente el mismo día.
¿Aún no tiene cuenta? Vea Enterprise o contacte con ventas.