Autenticación y convenciones
Credenciales
FINORA emite las primeras credenciales al dar de alta tu cuenta; después las gestionas tú en /v1/credenciales. El prefijo decide el entorno sobre la misma URL:
| Prefijo | Entorno |
|---|---|
kyc_live_… | Producción |
kyc_test_… | Sandbox: gemelo de tu cuenta con datos separados. Ver Sandbox. |
Tu cuenta (el tenant) se deduce siempre de la credencial, nunca del cuerpo de la petición. De cada secreto solo se guarda su hash: se muestra una vez, al emitirlo o rotarlo.
Los ejemplos de estas guías usan dos variables:
export KYC_BASE=https://kyc.finoracore.com
export KYC_API_KEY=kyc_test_… # se muestra una sola vez al emitirla
API key
Envía la clave en la cabecera X-API-Key:
curl -s "$KYC_BASE/v1/eventos" -H "X-API-Key: $KYC_API_KEY"
OAuth 2.0 client credentials
Recomendado para sistemas centrales. Emite una credencial de tipo OAUTH_CLIENT: su id (cre_…) es el client_id y su secreto (kcs_…) es el client_secret. Envíalos por autenticación básica o en el cuerpo.
curl -s -u "cre_01J9Z3K8Q2W7XPT4M6N5R0B1CQ:kcs_…" \
-d grant_type=client_credentials \
-d scope="casos:escribir casos:leer" \
"$KYC_BASE/oauth2/token"
{
"access_token": "eyJhbGciOiJSUzI1NiJ9…",
"token_type": "Bearer",
"expires_in": 599,
"scope": "casos:escribir casos:leer"
}
curl -s "$KYC_BASE/v1/casos/$CASO" -H "Authorization: Bearer $TOKEN"
- El token es un JWT RS256 de 10 minutos. Pide uno nuevo antes de que venza.
- Sin
scope, el token lleva todos los alcances del cliente. - Los errores de este endpoint siguen RFC 6749 (
invalid_client,invalid_scope…), que es lo que esperan las bibliotecas OAuth.
Alcances
| Alcance | Permite |
|---|---|
casos:escribir | Crear casos, subir documentos y selfie, enviar y crear enlaces. |
casos:leer | Consultar casos y eventos. |
screening | Screening suelto y monitoreo. |
listas:gestionar | Listas propias y sus entradas. |
webhooks:gestionar | Destinos de webhooks, rotación de secreto, pruebas y reenvíos. |
credenciales:gestionar | Emitir, rotar y revocar credenciales. |
Una credencial no puede conceder alcances que no tiene: pedirlos responde 403 ALCANCE_INSUFICIENTE.
Emitir, rotar y revocar
# Emitir una API key solo para leer casos
curl -s -X POST "$KYC_BASE/v1/credenciales" \
-H "X-API-Key: $KYC_API_KEY" -H "Idempotency-Key: cred-lectura-1" \
-H "Content-Type: application/json" \
-d '{"tipo":"API_KEY","nombre":"Tablero de cumplimiento","alcances":["casos:leer"]}'
# Rotar sin corte: el secreto anterior sigue valiendo 24 h (graciaSegundos, máximo 7 días)
curl -s -X POST "$KYC_BASE/v1/credenciales/$CRED/rotar" \
-H "X-API-Key: $KYC_API_KEY" -H "Idempotency-Key: rotar-$CRED-1" \
-H "Content-Type: application/json" -d '{"graciaSegundos":86400}'
# Revocar de forma irreversible
curl -s -X POST "$KYC_BASE/v1/credenciales/$CRED/revocar" \
-H "X-API-Key: $KYC_API_KEY" -H "Idempotency-Key: revocar-$CRED-1" \
-H "Content-Type: application/json" -d '{"motivo":"Equipo dado de baja"}'
La emisión y la rotación responden con el secreto una sola vez. Una credencial revocada responde 401 al instante, también con un JWT que aún no vence.
Idempotencia
Toda escritura (POST) exige Idempotency-Key: de 8 a 128 caracteres de [A-Za-z0-9._:-]. Reutiliza la misma clave al reintentar una petición que no sabes si llegó.
| Situación | Respuesta |
|---|---|
| Misma clave y misma petición (método, ruta y cuerpo) en 24 h | La misma respuesta, con Idempotent-Replayed: true |
| Misma clave con otra petición | 409 IDEMPOTENCIA_CONFLICTO |
| La primera sigue en curso | 409 IDEMPOTENCIA_EN_CURSO, con Retry-After |
| Sin la cabecera | 400 IDEMPOTENCIA_REQUERIDA |
Un error 5xx libera la clave para que puedas reintentar.
Errores
Los errores siguen RFC 9457 (application/problem+json). Ramifica por codigo, que es estable; el texto de title puede cambiar.
{
"type": "urn:finora:kyc:seguridad:no-autenticado",
"title": "Credencial ausente o inválida",
"status": 401,
"codigo": "NO_AUTENTICADO"
}
| HTTP | codigo | Cuándo |
|---|---|---|
| 400 | SOLICITUD_INVALIDA, IDEMPOTENCIA_REQUERIDA | Cuerpo o parámetros mal formados; falta Idempotency-Key. |
| 401 | NO_AUTENTICADO | Falta la credencial, no es válida o fue revocada. |
| 403 | ALCANCE_INSUFICIENTE, TENANT_SUSPENDIDO | La credencial no tiene el alcance; FINORA suspendió la cuenta (la credencial sigue siendo buena y no hace falta rotarla). |
| 404 | NO_ENCONTRADO | No existe o pertenece a otra cuenta: nunca se distingue. |
| 409 | TRANSICION_INVALIDA, IDEMPOTENCIA_CONFLICTO, IDEMPOTENCIA_EN_CURSO | El estado del caso no admite la operación; conflictos de idempotencia. |
| 422 | Código del dominio, por ejemplo RIF_INVALIDO | Un dato viola una regla. errores[] trae el detalle por campo. |
| 429 | RITMO_EXCEDIDO, CUOTA_MENSUAL_EXCEDIDA | Ritmo por minuto o cuota mensual del plan. Respeta Retry-After. |
| 503 | CUSTODIA_NO_DISPONIBLE | La custodia criptográfica no está disponible: no se aceptan documentos. Reintenta más tarde. |
Límites
Cada plan fija un ritmo por minuto y cuotas mensuales de casos y de screening. Las respuestas traen X-RateLimit-Limit y X-RateLimit-Remaining; al exceder se responde 429 con Retry-After en segundos.
Versionado
info.version del contrato sigue SemVer. Un cambio que rompe a un integrador (quitar un campo, cambiar un tipo o un código de error) sube la versión mayor y la ruta pasa a /v2. Añadir campos opcionales, operaciones o valores de enumeración es un cambio menor: tu código debe ignorar lo que no conoce.