API — Biometría
Los endpoints de biometría ejecutan prueba de vida, comparación facial, estimación de edad y búsqueda facial. Requieren API key (X-API-Key: zkyc_...) y usan multipart/form-data.
Prefijo: /v1/biometrics.
Endpoints
Sección titulada «Endpoints»| Método | Ruta | Auth | Descripción |
|---|---|---|---|
| POST | /v1/biometrics/liveness | API key | Prueba de vida (anti-spoofing) |
| POST | /v1/biometrics/face-match | API key | Comparación facial 1:1 |
| POST | /v1/biometrics/age | API key | Estimación de edad |
| POST | /v1/biometrics/face-search | API key | Búsqueda facial 1:N |
| POST | /v1/biometrics/face-search/index | API key | Indexar un rostro |
Los endpoints que aceptan sessionId consolidan su resultado en la sesión y recalculan la decisión global.
Liveness
Sección titulada «Liveness»POST /v1/biometrics/livenessX-API-Key: zkyc_...Content-Type: multipart/form-data| Campo | Tipo | Requerido |
|---|---|---|
image | archivo | Sí |
sessionId | texto | No |
{ "ok": true, "result": { "status": "live", "score": 0.94, "method": "minifasnet" } }status | Significado |
|---|---|
live | Rostro real (persona presente) |
spoof | Suplantación detectada |
uncertain | No concluyente (p. ej. fallback heurístico sin modelo ONNX) |
curl -X POST https://kyc.zentto.net/v1/biometrics/liveness \ -H "X-API-Key: $KYC_API_KEY" \ -F "image=@./selfie.jpg" \ -F "sessionId=9b1c0e2a-..."const res = await kyc.biometrics.liveness({ sessionId: "9b1c0e2a-...", image: { data: selfieBuf, filename: "selfie.jpg", contentType: "image/jpeg" },});Face-match (1:1)
Sección titulada «Face-match (1:1)»Compara la selfie del usuario contra una imagen de referencia (típicamente el rostro del documento).
POST /v1/biometrics/face-matchX-API-Key: zkyc_...Content-Type: multipart/form-data| Campo | Tipo | Requerido |
|---|---|---|
user_image | archivo | Sí |
ref_image | archivo | Sí |
sessionId | texto | No |
{ "ok": true, "result": { "status": "match", "score": 0.88 } }status | Significado |
|---|---|
match | Misma persona |
no_match | Personas distintas |
uncertain | No concluyente |
curl -X POST https://kyc.zentto.net/v1/biometrics/face-match \ -H "X-API-Key: $KYC_API_KEY" \ -F "user_image=@./selfie.jpg" \ -F "ref_image=@./front.jpg" \ -F "sessionId=9b1c0e2a-..."const res = await kyc.biometrics.faceMatch({ sessionId: "9b1c0e2a-...", userImage: { data: selfieBuf, filename: "selfie.jpg" }, refImage: { data: frontBuf, filename: "front.jpg" },});Si falta user_image o ref_image, la API responde 400 missing_file.
Estimación de edad
Sección titulada «Estimación de edad»POST /v1/biometrics/ageX-API-Key: zkyc_...Content-Type: multipart/form-data| Campo | Tipo | Requerido |
|---|---|---|
image | archivo | Sí |
sessionId | texto | No |
{ "ok": true, "result": { "estimated_age": 27, "confidence": 0.82 } }const res = await kyc.biometrics.ageEstimation({ sessionId: "9b1c0e2a-...", image: { data: selfieBuf, filename: "selfie.jpg" },});Face-search (1:N)
Sección titulada «Face-search (1:N)»Busca el rostro contra el índice global para detectar duplicados o fraude (la misma persona registrándose varias veces).
POST /v1/biometrics/face-searchX-API-Key: zkyc_...Content-Type: multipart/form-data| Campo | Tipo | Requerido |
|---|---|---|
image | archivo | Sí |
{ "ok": true, "result": { "total_matches": 2, "matches": [ { "session_id": "9b1c0e2a-...", "score": 0.91 }, { "session_id": "c3d4e5f6-...", "score": 0.77 } ] }}const res = await kyc.biometrics.faceSearch({ image: { data: selfieBuf, filename: "selfie.jpg" },});Indexar un rostro
Sección titulada «Indexar un rostro»Inserta el embedding del rostro en el índice 1:N, asociado a una sesión, para que futuras búsquedas lo encuentren.
POST /v1/biometrics/face-search/indexX-API-Key: zkyc_...Content-Type: multipart/form-data| Campo | Tipo | Requerido |
|---|---|---|
image | archivo | Sí |
sessionId | texto | Sí |
{ "ok": true, "result": { "indexed": true } }const res = await kyc.biometrics.faceIndex({ sessionId: "9b1c0e2a-...", image: { data: selfieBuf, filename: "selfie.jpg" },});Si falta sessionId, la API responde 400 missing_field.
Flujo del usuario
Sección titulada «Flujo del usuario»Vista no técnica: el usuario toma un selfie y el sistema verifica que es una persona real y coincide con el documento.
Editable en draw.io: descarga el SVG → en draw.io: File → Import from → Device → selecciona el SVG. Cada nodo queda editable.
Flujo técnico
Sección titulada «Flujo técnico»Vista técnica: liveness/face-match/age → FastAPI :5200 (InsightFace + MiniFASNet) → score → actualiza sesión | face-search → Qdrant.
| Componente | Tipo | Ubicación |
|---|---|---|
POST /v1/biometrics/liveness | Route Express (multipart) | src/biometrics/routes.ts |
POST /v1/biometrics/face-match | Route Express (multipart) | src/biometrics/routes.ts |
POST /v1/biometrics/age | Route Express (multipart) | src/biometrics/routes.ts |
POST /v1/biometrics/face-search | Route Express (multipart) | src/biometrics/routes.ts |
POST /v1/biometrics/face-search/index | Route Express (multipart) | src/biometrics/routes.ts |
POST /v1/liveness | Inferencia ML (FastAPI :5200) | inference/app/routers/liveness.py |
POST /v1/face-match | Inferencia ML (FastAPI :5200) | inference/app/routers/face.py |
POST /v1/age | Inferencia ML (FastAPI :5200) | inference/app/routers/face.py |
inference/app/models/liveness.py | MiniFASNet ONNX (anti-spoofing) | inference/app/models/liveness.py |
inference/app/models/face.py | InsightFace/ArcFace (face-match, edad) | inference/app/models/face.py |
inference/app/models/search.py | Embeddings + Qdrant (face-search 1:N) | inference/app/models/search.py |
face_embeddings | Colección Qdrant (embeddings 512-dim) | Qdrant vector DB |
session_results | Tabla resultados biometría | src/db/migrations/ |
sessions.orchestrator.ts | Recalcula decisión global post-inferencia | src/sessions/sessions.orchestrator.ts |
Editable en draw.io: descarga el SVG → File → Import from → Device.