Errores
Todos los errores de la API Veridia siguen un shape consistente. Esta pagina es la referencia canonica para cada codigo de error que podes ver.
Shape de respuesta de error
Cada respuesta de error incluye:
{
"error": "invalid_body",
"message": "Request body failed validation",
"requestId": "9f511d92ac11236d-SJC",
"detail": {
"fieldErrors": {
"country": ["expected 2 characters"]
}
}
}
| Campo | Tipo | Descripcion |
|---|---|---|
error | string | Codigo machine-readable — switcha en este en tu codigo |
message | string | Resumen human-readable, mayormente para logs |
requestId | string | ID unico de esta request — incluilo en tickets de soporte |
detail | object | Opcional. Contexto extra (errores de campo, razon de mismatch de keys, etc.) |
El mismo requestId tambien se devuelve en el response header X-Request-Id — util cuando el parsing JSON falla.
Catalogo de errores
invalid_body — 400 Bad Request
El body de la request fallo validacion del schema Zod. Revisa detail.fieldErrors por especificos.
Causas comunes:
- Campo requerido faltante
- Tipo equivocado (ej. string donde se esperaba number)
- String muy largo (ej.
userRef > 128chars) - Valor enum invalido (ej.
documentType: "id"en lugar de"dni") verificationIdno matchea^vf_[A-Za-z0-9]{16,24}$
En /submit especificamente, detail.reason puede ser:
| Reason | Significado |
|---|---|
doc_front_key_mismatch | El keys.docFront no coincide con lo que /init devolvio |
selfie_key_mismatch | El keys.selfie no coincide con lo que /init devolvio |
doc_back_key_mismatch | El keys.docBack no coincide con lo que /init devolvio |
doc_front_not_uploaded | El objeto R2 del frente no existe — el cliente nunca subio |
selfie_not_uploaded | El objeto R2 de la selfie no existe |
doc_front_disappeared | El objeto R2 existia durante HEAD pero desaparecio antes del OCR (extremadamente raro) |
Recovery: Arregla la request y reintenta. Estos son errores del cliente.
unauthorized — 401 Unauthorized
El header Authorization falta, esta malformado, o no usa el scheme Bearer.
Recovery: Agrega un header Authorization: Bearer <key> valido.
invalid_key — 401 Unauthorized
El bearer token no corresponde a una key valida. Nunca existio, fue revocada, o expiro.
Recovery: Genera una nueva key en el dashboard. Verifica que estes usando la key correcta para el environment correcto.
origin_not_allowed — 403 Forbidden
La publishable key fue usada desde un dominio que no esta en su lista de allowed origins.
Recovery: Agrega tu dominio a la lista de allowed origins en el dashboard. Para desarrollo local, agrega http://localhost:PORT.
wrong_environment — 403 Forbidden
Una test key (qv_pub_test_* / qv_sec_test_*) fue usada con un endpoint live, o viceversa.
Recovery: Usa el environment matcheante. Test keys para testing, live keys para produccion.
insufficient_credits — 403 Forbidden
El tenant tiene 0 creditos. Nuevas verificaciones no pueden iniciar.
Recovery: Top up creditos en el dashboard, o contacta soporte si estas en un plan custom.
verification_not_found — 404 Not Found
El verificationId no existe, no pertenece a este tenant, o el intent expiro (>1 hora despues del /init).
Recovery:
- Verifica que el ID no tenga typos
- Asegurate que estes usando la misma API key (o otra del mismo tenant)
- Si paso mas de 1 hora desde
/init, reinicia el flow
not_found — 404 Not Found
La ruta no existe.
Recovery: Verifica la URL. Typos comunes: /v1/verifications en lugar de /v1/verify/:id.
rate_limited — 429 Too Many Requests
Hit el rate limit por tenant. El response header Retry-After te dice cuanto esperar (en segundos).
| Endpoint | Limite por defecto |
|---|---|
/v1/verify/init | 60 / minuto |
/v1/verify/submit | 30 / minuto |
/v1/verify/:id | 600 / minuto |
Recovery:
- Espera la duracion en
Retry-After, despues reintenta - Agrega throttling de tu lado
- Contacta soporte para subir limites por-tenant
internal — 500 Internal Server Error
Algo se rompio del lado de Veridia. Raro.
Recovery:
- Reintenta con backoff exponencial (el issue puede ser transitorio)
- Si persiste, manda un ticket de soporte con el
requestIdde la respuesta
Manejando errores bien
JavaScript / Node.js
async function callVeridia(url, options) {
const response = await fetch(url, options);
if (response.ok) {
return response.json();
}
const error = await response.json();
// Switch en el error code, no en HTTP status
switch (error.error) {
case 'invalid_body':
console.error('Validation fallo:', error.detail?.fieldErrors);
throw new ValidationError(error);
case 'rate_limited':
const retryAfter = response.headers.get('Retry-After') || 1;
await new Promise(r => setTimeout(r, retryAfter * 1000));
return callVeridia(url, options); // reintenta una vez
case 'insufficient_credits':
// Notifica ops, fallback a revision manual
await notifyCreditsExhausted();
throw new BusinessError(error);
case 'verification_not_found':
// Error logico — reinicia el flow
throw new NotFoundError(error);
case 'internal':
// Issue de Veridia — loguea y alerta
logger.error('Veridia internal error', {
requestId: error.requestId,
url,
});
throw new ExternalServiceError(error);
default:
throw new Error(`Veridia error no manejado: ${error.error}`);
}
}
Python
import time
import requests
def call_veridia(url, **kwargs):
response = requests.request(**kwargs, url=url)
if response.ok:
return response.json()
error = response.json()
if error["error"] == "invalid_body":
raise ValidationError(error)
if error["error"] == "rate_limited":
retry_after = int(response.headers.get("Retry-After", 1))
time.sleep(retry_after)
return call_veridia(url, **kwargs)
if error["error"] == "insufficient_credits":
notify_credits_exhausted()
raise BusinessError(error)
if error["error"] == "verification_not_found":
raise NotFoundError(error)
if error["error"] == "internal":
logger.error(
"Veridia internal error",
extra={"request_id": error["requestId"], "url": url},
)
raise ExternalServiceError(error)
raise Exception(f"Veridia error no manejado: {error['error']}")
Best practices
- Switch en
error.error, no en HTTP status. Los codigos HTTP pueden cambiar por razones no-breaking; el codigoerrorstring es parte de nuestro contrato publico de API - Siempre loguea el
requestId. Es el camino mas rapido a diagnostico si abris un ticket de soporte - Reintenta con backoff para
internalyrate_limited. No reintentesinvalid_body,unauthorized,verification_not_found, oinsufficient_credits - Surface errores de validation a tus developers, no a tus usuarios finales. Los usuarios finales deberian ver "Algo salio mal, por favor reintenta"
- Monitorea
insufficient_creditscomo business alert — significa que se esta perdiendo facturacion
Que sigue
- Autenticacion — keys, environments, seguridad
- Rate limits — limites y manejo de cuotas
- Referencia API — volver al overview de la API