Saltar al contenido principal

Instalación

1. Obtené tus claves

Entrá a tu panel de Veridia y abrí la sección API keys. Necesitás dos claves, de dos familias distintas:

  • Publicableqv_pubt_... (test) o qv_pub_... (live). Va en la página. Este paso solo necesita esta.
  • Secretaqv_sect_... (test) o qv_sec_... (live). Se queda en tu servidor. La vas a necesitar en el paso 3 para leer el veredicto.

Las claves publicables se pueden exponer sin riesgo en el HTML del cliente. Identifican a tu tenant y pueden iniciar y enviar verificaciones, pero GET /v1/verify/{id} las rechaza, así que nunca pueden leer un veredicto.

Lista blanca de dominios

Una clave puede llevar una lista de orígenes permitidos, que se compara contra el hostname pelado (tuapp.com, se aceptan comodines como *.tuapp.com — no https://tuapp.com). El panel todavía no expone este campo, así que toda clave se crea con la lista vacía, y una lista vacía permite todos los orígenes. Acá no hay nada que configurar, tampoco para localhost. Tomalo como una forma de acotar más adelante dónde corre tu widget, no como un control de acceso.

2. Embebé el widget

Pegá dos etiquetas de script y el custom element en cualquier página:

<!-- 1. Cargá face-api.js (UMD) y despues el bundle del widget (modulo ESM).
El orden importa: el widget lee window.faceapi al arrancar. -->
<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>

<!-- 2. Insertá el widget -->
<veridia-widget
publishable-key="qv_pubt_TU_CLAVE_ACA"
user-ref="customer-12345"
country="PY"
document-type="dni"
locale="es">
</veridia-widget>

<!-- 3. Escuchá los eventos -->
<script>
document.querySelector('veridia-widget')
.addEventListener('veridia:complete', (e) => {
// e.detail es { verificationId, status } — el veredicto NO viene aca.
console.log('verification:', e.detail.verificationId);
});
</script>

Esa es la integración completa. Abrí la página en un celular o notebook con cámara, tocá "Comenzar" y ya tenés un flujo de captura biométrica funcionando.

Guardá vos la correspondencia

veridia:complete te da verificationId y nada más — no tu user-ref. GET /v1/verify/{id} tampoco lo devuelve. Solo el webhook te devuelve userRef.

Así que cuando se dispare el evento, escribí verificationId → tu id de usuario en tu propia base de datos en ese mismo momento. Si te lo salteás, un veredicto que llegue después por polling no tiene manera de decirte a qué cuenta pertenece.

Atributos de configuración

Diez atributos, todos opcionales salvo publishable-key.

AtributoObligatorioValor por defectoDescripción
publishable-keyTu clave qv_pubt_* o qv_pub_* del panel
api-baseNohttps://api.xxuxe.onlineSobrescribe el endpoint de la API. Casi nunca lo vas a necesitar
user-refNoTu propio identificador de usuario (máx. 128 caracteres). Se devuelve solo en los webhooks
countryNoCódigo de país ISO 3166-1 alfa-2 (PY, BR, MX) — mejora la precisión del OCR. Se normaliza a mayúsculas automáticamente
document-typeNoUno de: dni, passport, drivers_license, national_id, other
submitted-full-nameNoEl nombre completo del usuario, comparado de forma difusa contra el documento (máx. 255 caracteres). Produce el puntaje name_match
require-doc-backNodepende — ver abajoSi se captura el dorso del documento
localeNoidioma del navegador, después enIdioma de la interfaz: en, es o pt
accent-colorNo#0f172aColor de acento para botones y estados activos. Cualquier color CSS válido, no solo hex
active-livenessNofalsePoné "true" para habilitar el reto de prueba de vida (liveness) activa verificada en el servidor

require-doc-back vale true por defecto para todo menos pasaportes

Este es el atributo que más probablemente te sorprenda, así que leé bien la regla.

Si no seteás el atributo en absoluto, el widget calcula el valor por defecto como document-type !== 'passport'. Un pasaporte lleva dos capturas (frente + selfie); todo lo demás — incluido el caso en que no seteás ningún document-type — lleva tres (frente + dorso + selfie).

Y cuando sí lo seteás, solo dos valores literales lo desactivan: "false" y "0". Cualquier otro string, incluidos "no", "off" y el atributo vacío require-doc-back="", resuelve a true.

<!-- 3 capturas: frente, dorso, selfie -->
<veridia-widget publishable-key="..." document-type="dni"></veridia-widget>

<!-- 2 capturas: frente, selfie -->
<veridia-widget publishable-key="..." document-type="dni" require-doc-back="false"></veridia-widget>

<!-- 2 capturas: los pasaportes no tienen dorso -->
<veridia-widget publishable-key="..." document-type="passport"></veridia-widget>

Diseñá tu embudo alrededor del número real de capturas antes de armar las pantallas encima.

active-liveness viene apagado por defecto

Poner active-liveness="true" activa el reto de prueba de vida (liveness) verificado en el servidor: la API emite un plan de reto en /v1/verify/init y revela los pasos de a uno, de modo que un video pregrabado o inyectado no lo puede satisfacer.

Viene apagado por defecto, lo que significa que una integración que nunca menciona el atributo corre sin él. Si tu modelo de riesgo incluye a alguien inyectando cuadros en el navegador en vez de poner una cara real frente a la cámara, encendelo.

Tené en cuenta que agrega pasos de captura y, por lo tanto, tiempo al flujo del usuario, y que hace que la verificación emita alrededor de veinte subidas en lugar de tres — algo relevante para el límite de tasa por IP si muchos de tus usuarios comparten un mismo NAT.

Configuración por código

En vez de atributos, podés asignar un objeto de configuración:

const widget = document.querySelector('veridia-widget');
widget.config = {
publishableKey: 'qv_pubt_TU_CLAVE',
userRef: 'customer-12345',
country: 'PY',
documentType: 'dni',
requireDocBack: false,
locale: 'es',
activeLiveness: true,
};
No mezcles los dos mecanismos

Setear cualquier atributo observado reconstruye la configuración solo a partir de los atributos, descartando en silencio todo lo que le hayas asignado a .config — incluida publishableKey. Si configurás por código y después cambiás, digamos, locale en tiempo de ejecución para cambiar de idioma, el widget pierde su clave y falla con invalid_api_key. Elegí un solo mecanismo por instancia del widget.

Ejemplos por framework

React

import { useEffect, useRef } from 'react';

export function VeridiaVerification({ userRef, onComplete }) {
const widgetRef = useRef(null);

useEffect(() => {
const handler = (e) => onComplete(e.detail);
const node = widgetRef.current;
node?.addEventListener('veridia:complete', handler);
return () => node?.removeEventListener('veridia:complete', handler);
}, [onComplete]);

return (
<veridia-widget
ref={widgetRef}
publishable-key={process.env.NEXT_PUBLIC_VERIDIA_KEY}
user-ref={userRef}
locale="es"
/>
);
}

Asegurate de cargar las etiquetas de script una sola vez en tu _document.tsx o index.html:

<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>

Vue 3

<template>
<veridia-widget
ref="widget"
:publishable-key="publishableKey"
:user-ref="userRef"
locale="es"
/>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const props = defineProps(['publishableKey', 'userRef']);
const emit = defineEmits(['complete']);
const widget = ref(null);

const handler = (e) => emit('complete', e.detail);

onMounted(() => {
widget.value?.addEventListener('veridia:complete', handler);
});
onUnmounted(() => {
widget.value?.removeEventListener('veridia:complete', handler);
});
</script>

Vue trata <veridia-widget> como custom element automáticamente — no hace falta configuración extra.

JavaScript plano (sin framework)

<!DOCTYPE html>
<html>
<head>
<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>
</head>
<body>
<veridia-widget
id="kyc"
publishable-key="qv_pubt_TU_CLAVE"
user-ref="user-001"
document-type="dni"
country="PY"
locale="es">
</veridia-widget>

<script>
const widget = document.getElementById('kyc');

widget.addEventListener('veridia:complete', async (e) => {
const { verificationId } = e.detail;
// Registra verificationId -> tu id de usuario AHORA. Es el unico
// identificador que vas a recibir del polling, y no lleva user-ref.
await fetch('/api/kyc-started', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verificationId, userId: 'user-001' }),
});
});

widget.addEventListener('veridia:error', (e) => {
// e.detail es { code, message, detail? }
console.error('Veridia error:', e.detail.code, e.detail.message);
});
</script>
</body>
</html>

Verificá que funcione

Abrí la página en un navegador sobre HTTPS (o localhost — los navegadores dan acceso a la cámara en esos dos casos y en ningún otro). Deberías ver:

  1. Un botón "Comenzar" bajo el título "Verificación de identidad" (o su equivalente traducido)
  2. Después de tocar comenzar, el pedido de permiso de cámara
  3. Los pasos de captura — frente del documento, dorso salvo que lo hayas desactivado, y después la selfie
  4. Una pantalla de confirmación "Enviado ✓"

En los pasos del documento el usuario puede usar la cámara o elegir una imagen existente de su galería. En los pasos de selfie y prueba de vida la opción de galería está deliberadamente ausente: permitir un archivo guardado ahí anularía el sentido de la captura. Vale la pena saberlo antes de que te lo pregunten en una revisión de cumplimiento.

Si algo sale mal

La pantalla de error del widget siempre muestra el mismo mensaje genérico — no nombra la causa. La causa real está en el evento veridia:error, así que registralo:

widget.addEventListener('veridia:error', (e) => {
console.error('code:', e.detail.code, '| message:', e.detail.message, '| detail:', e.detail);
});

El paso siguiente lista todos los códigos que emite el widget y qué significa cada uno. Las dos causas más comunes de una falla justo al arrancar son una publishable-key mal tipeada o revocada (invalid_api_key) y un tenant de test sin créditos (insufficient_credits).

Paso siguiente

Corré una verificación real de punta a punta e inspeccioná el resultado.

Paso 2: Primera verificación →