Schedule a demo with an expertSchedule demo
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 document
2
Validation OCR
3
Liveness detection
4
Facial Comparison

1. 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