Back to documentation
Guide30 min
onboarding flow KYC
It implements a complete flow of identity checks with document capture, liveness detection and facial comparison.
Overview
The onboarding flow KYC of JAAK consist of the following steps:
1
Collection of document2
Validation OCR3
Liveness detection4
Facial Comparison1. Create verification session
Configure the check flow as required:
POST / v1 / verifications
const session = await jaak.verification.create({
type: 'full_kyc',
config: {
// Tipos de documento aceptados
documentTypes: ['ine', 'ine_reverso', 'passport'],
// Habilitar prueba de vida pasiva
livenessCheck: {
enabled: true,
type: 'passive' // 'passive' o 'active'
},
// Habilitar comparación facial
faceMatch: {
enabled: true,
threshold: 0.85 // Umbral de similitud (0-1)
},
// Consultar bases oficiales
officialSources: {
ine: true, // Validar en padrón INE
renapo: true, // Validar CURP
sat: false // Validar RFC
},
// Opciones de captura
capture: {
allowUpload: false, // Solo permitir cámara
quality: 'high',
retries: 3
}
},
// Datos del usuario (opcional)
userData: {
email: 'usuario@ejemplo.com',
phone: '+525512345678'
},
// Metadatos para tu sistema
metadata: {
userId: 'usr_abc123',
applicationId: 'app_xyz789'
},
// URL de redirección al terminar
redirectUrl: 'https://tuapp.com/onboarding/completado',
// Webhook para notificaciones
webhookUrl: 'https://tuapp.com/webhooks/jaak'
});2. Redirect the user
Redirect the user to the verification URL. JAAK takes care of all the experience:
// En tu frontend
window.location.href = session.verificationUrl;
// O en React/Next.js
import { useRouter } from 'next/navigation';
const router = useRouter();
router.push(session.verificationUrl);Tip: You can also drink the flow into an iframe or use our SDK web for an integrated experience.
3. To process results
It receives the result of the monitoring through webhold:
Webhook: vertiation.complete
{
"event": "verification.completed",
"timestamp": "2025-01-09T10:30:00Z",
"data": {
"sessionId": "ses_abc123",
"status": "approved", // approved, rejected, pending_review
"document": {
"type": "ine",
"number": "XXXX1234567890",
"name": "JUAN PEREZ GARCIA",
"birthDate": "1990-05-15",
"address": "CALLE EJEMPLO 123, CDMX",
"curp": "PEGJ900515HDFRRL09",
"claveElector": "PRGRJN90051509H800",
"validUntil": "2029-12-31",
"isValid": true,
"securityFeatures": {
"hologram": true,
"microprint": true,
"uvElements": true
}
},
"biometrics": {
"livenessScore": 0.98,
"livenessResult": "live",
"faceMatchScore": 0.95,
"faceMatchResult": "match"
},
"officialSources": {
"ine": {
"status": "valid",
"matchedFields": ["name", "curp", "photo"]
},
"renapo": {
"status": "valid",
"curpValid": true
}
},
"evidence": {
"documentFront": "https://evidence.jaak.ai/doc_front_xxx.jpg",
"documentBack": "https://evidence.jaak.ai/doc_back_xxx.jpg",
"selfie": "https://evidence.jaak.ai/selfie_xxx.jpg",
"livenessVideo": "https://evidence.jaak.ai/liveness_xxx.mp4"
},
"metadata": {
"userId": "usr_abc123",
"applicationId": "app_xyz789"
}
}
}4. To manage results
app.post('/webhooks/jaak', async (req, res) => {
const { event, data } = req.body;
if (event === 'verification.completed') {
const { sessionId, status, document, biometrics } = data;
switch (status) {
case 'approved':
// Verificación exitosa
await updateUserStatus(data.metadata.userId, 'verified');
await saveVerificationData(sessionId, document);
await sendWelcomeEmail(document.name);
break;
case 'rejected':
// Verificación rechazada
await updateUserStatus(data.metadata.userId, 'rejected');
await notifyRejection(data.metadata.userId, data.rejectionReason);
break;
case 'pending_review':
// Requiere revisión manual
await createReviewTask(sessionId, data);
break;
}
}
res.status(200).send('OK');
});Best practices
- Always valid the HMAC signature of the web hold before processing
- Save the session for future consultations and audits
- Implement idempower at your endpoint from webhold
- Use Sandbox for Test before production