Saltar al contenido principal

Autenticación

Veridia usa autenticación por bearer token:

Authorization: Bearer qv_pub_FJJWXMA2RN2XPRDK6YJX4KTVD0XSQHW9

Dos endpoints son la excepción: PUT /v1/verify/upload/... y GET /v1/verify/challenge/.../next no toman clave de API en absoluto. Se autentican con el token de subida de vida corta que reparte /init. Ver Subir las imágenes.

Familias de claves

FamiliaUsar desdePuedeNo puede
PublicableNavegador, widget, app móvilPOST /v1/verify/init, POST /v1/verify/submitLeer veredictos
SecretaSolo tu servidorTodo lo que puede una clave publicable, más GET /v1/verify/:id

La clave publicable es segura para publicar en tu página. Está diseñada para eso. Puede iniciar verificaciones contra tu saldo, y puede enviarlas — pero nunca puede leer un resultado.

La clave secreta nunca es segura para publicar. Puede obtener el veredicto, los campos de identidad extraídos y los scores de cualquier verificación de tu tenant. Tratala como la contraseña de una base de datos.

Por qué existe la división

Una clave publicable es, por definición, legible por cualquiera que abra el código fuente de tu página. Si esa clave pudiera llamar a GET /v1/verify/:id, entonces todos los resultados KYC de tus clientes — veredicto, confianza, número de documento, fecha de nacimiento — estarían a un fetch de distancia para cualquiera con las herramientas de desarrollo abiertas.

Por eso el endpoint de resultados rechaza las claves publicables con un código de error dedicado, secret_key_required, en lugar de un fallo de auth genérico. El código existe específicamente para que no salgas a cazar un typo en una clave que es perfectamente válida.

Entornos

Cada tenant recibe dos juegos paralelos de claves. Prestá mucha atención a los prefijos de test — son qv_pubt_ y qv_sect_, con la t antes del guion bajo. No qv_pub_test_.

EntornoPublicableSecreta
Testqv_pubt_...qv_sect_...
Liveqv_pub_...qv_sec_...

Una clave de test corre todo el cable, nada del trabajo. Cada request hace el mismo recorrido que producción — autenticación de subida real, una fila de verificación real, un webhook real firmado con tu secreto real y reintentado con la misma escalera, legible con tu clave secreta de test — pero no corre OCR, no corre el pipeline de ML, no se gasta ningún crédito, y el veredicto lo elegís vos, determinísticamente:

userRef contieneVeredictoEvento del webhook
+rejectrejectedverification.rejected
+reviewreviewverification.review
cualquier otra cosaapprovedverification.approved
{ "userRef": "qa-user-17+reject", "documentType": "passport", "country": "PY" }

Resultados determinísticos significan que tu CI puede asertar sobre cada rama del webhook en vez de esperar que el pipeline opine igual dos veces. Las imágenes que subís igual tienen que ser JPEG reales dentro de los límites de tamaño — el camino de bytes se ejercita a propósito — pero su contenido se ignora: los campos extraídos vuelven como marcadores inconfundibles (TEST PERSONA, TEST-000000).

Tu URL de webhook recibe LOS DOS mundos

Las claves de test y de live entregan a la misma URL de webhook del tenant. Por eso cada sobre de evento lleva un campo env"test" o "live" — y tu handler tiene que ramificar sobre él. Un verification.approved sintético que activa una cuenta real es exactamente el accidente que este campo existe para prevenir.

Flujo de trabajo recomendado: desarrollá contra claves de test, asertá sobre los tres resultados en CI, y recién ahí cambiá a claves live en producción. Nunca uses una clave live en staging.

Lista blanca de dominios (claves publicables)

Una clave publicable puede llevar una lista de orígenes permitidos. Cuando esa lista no está vacía, una request de navegador cuyo Origin no figure en ella se rechaza con origin_not_allowed (403).

Las entradas se comparan contra el hostname pelado — sin esquema, sin puerto:

yourapp.com
staging.yourapp.com
localhost

http://localhost:3000 no es una entrada válida. Nunca va a coincidir con nada, porque la verificación compara contra el hostname localhost.

Los comodines funcionan: *.yourapp.com coincide con app.yourapp.com pero deliberadamente no coincide con yourapp.com en sí. Listá ambos si necesitás ambos.

Leé esto antes de apoyarte en la lista de orígenes para algo

La garantía funciona al revés de lo que espera la mayoría.

  • Toda clave se crea con la lista vacía, y una lista vacía permite todos los orígenes. La verificación se saltea por completo cuando la lista está vacía. Es opt-in, no opt-out. El panel hoy no expone un campo para completarla.
  • Solo aplica a requests de navegador. curl, un servidor, un script o cualquiera de nuestros SDKs server-side no envían header Origin, así que no hay nada que comparar y la request procede. Nunca puede restringir el uso fuera del navegador.
  • Agregar tu primera entrada da vuelta la clave de "cualquier origen" a "solo esta lista". Si agregás https://yourapp.com — con el esquema, que no va a coincidir — pasás de todo permitido a todo bloqueado en un solo paso. Agregá el hostname pelado.

Lo que esta lista hace realmente es acotar dónde puede correr tu widget. No es un control de acceso. Lo que protege a una clave publicable es que no puede leer resultados, más tus límites de tasa y tu saldo de créditos.

Tratá una clave publicable como pública, porque lo es.

Usar la clave secreta

Solo del lado del servidor. Todos los ejemplos de abajo obtienen un veredicto.

curl

curl -X GET https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2 \
-H "Authorization: Bearer qv_sec_YOUR_SECRET"

En modo test la clave empieza con qv_sect_.

JavaScript / Node.js

const response = await fetch(`https://api.xxuxe.online/v1/verify/${id}`, {
headers: {
'Authorization': `Bearer ${process.env.VERIDIA_SECRET_KEY}`,
},
});
const data = await response.json();

Python

import os, requests

response = requests.get(
f"https://api.xxuxe.online/v1/verify/{verification_id}",
headers={"Authorization": f"Bearer {os.environ['VERIDIA_SECRET_KEY']}"},
)
data = response.json()

PHP

<?php
$ch = curl_init("https://api.xxuxe.online/v1/verify/$verificationId");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $_ENV['VERIDIA_SECRET_KEY'],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

Reemplazar una clave

El panel soporta dos operaciones: crear y revocar. No hay rotación, y no hay período de gracia.

La revocación tiene efecto inmediato

Revocar una clave la borra del edge store al instante. Toda request en vuelo que la use empieza a fallar con invalid_api_key en ese mismo instante — no hay ventana de solapamiento durante la cual la clave vieja siga funcionando.

Así que el orden seguro es: creá la clave nueva primero, desplegala en todos lados, confirmá que el tráfico está fluyendo por ella, y recién entonces revocá la vieja. Hacerlo al revés es una caída de servicio.

Si sospechás de una filtración, ese es exactamente el caso en que querés el corte inmediato — revocá primero y aceptá la interrupción. Solo hacelo a sabiendas.

Buenas prácticas

  • Nunca commitees claves secretas. Variables de entorno o un gestor de secretos.
  • Claves separadas por entorno. Nunca una clave live en staging.
  • Crear-después-revocar al reemplazar una clave, por el motivo de arriba.
  • Registrá el requestId de las respuestas. Convierte un ticket de soporte vago en uno trazable.
  • Para cualquier llamada del lado del servidor, usá la clave secreta. La clave publicable es para clientes.

Errores de autenticación

HTTPCódigo de errorQué significa
401missing_api_keySin header Authorization, o no es el esquema Bearer
401invalid_api_keyLa clave no existe, fue revocada, o el prefijo/formato no parsea
401secret_key_requiredSe usó una clave publicable en GET /v1/verify/:id
402insufficient_creditsEl saldo de créditos del tenant es cero
403origin_not_allowedRequest de navegador desde un origen que no está en una lista permitida no vacía
429rate_limitedVer Límites de tasa — puede ser la capa por IP, verificada antes que tu clave
503backend_unavailablePipeline inalcanzable — transitorio, reintentá con backoff

Notá que insufficient_credits es 402, no 403. Además se dispara durante la autenticación, lo que significa que puede aparecer en /init antes de que hayas hecho cualquier otra cosa — una causa común del "el widget solo dice que algo salió mal" en una cuenta de prueba agotada.

Ver Errores para el catálogo completo.

Qué sigue