Handling Liveness Results
<liveness-check> (and LivenessReact) dispatch standard DOM events. Face matching is a session step, not a widget event — after document capture and liveness both succeed, fetch session results from your backend.
Events
| Event | When it fires | event.detail |
|---|---|---|
yesid:success | Liveness passes and submitResults() succeeds | { response, submission } |
yesid:failure | Liveness, session, or submission fails | LivenessError |
yesid:capture | A challenge capture is produced | LivenessCapture |
yesid:session-validated | Session accepted by the API | SessionValidatedEvent |
yesid:session-invalid | Session invalid or expired | SessionInvalidEvent |
import { YESID_EVENTS } from '@yesid/liveness-sdk';
el.addEventListener(YESID_EVENTS.success, (event) => {
console.log(event.detail.response.status);
});
Success payload
type LivenessSuccessPayload = {
response: {
sessionId: string;
stepType: 'LIVENESS' | 'OCR' | 'FACE_MATCH';
status: 'PENDING' | 'RUNNING' | 'PASSED' | 'FAILED' | 'ERROR' | 'REVIEW' | 'SKIPPED';
completedAt: string;
data?: Record<string, string>;
};
submission: {
captures: string[];
challenges: ('BLINK' | 'TURN_LEFT' | 'TURN_RIGHT' | 'LOOK_UP' | 'LOOK_DOWN' | 'LOOK_CENTER')[];
};
};
submission.captures are the selfie frames collected during the flow. Prefer fetching the canonical selfie from session results if you need it later (steps[].resultData.selfieImage on the LIVENESS step).
el.addEventListener('yesid:success', (event) => {
console.log(event.detail.response.sessionId);
console.log(event.detail.response.status);
console.log(event.detail.submission.captures.length);
});
Failure payload
type LivenessError = {
reason:
| 'CAMERA_PERMISSION_DENIED'
| 'NO_FACE_DETECTED'
| 'MULTIPLE_FACES_DETECTED'
| 'CHALLENGE_TIMEOUT'
| 'INITIALIZATION_FAILED'
| 'FACE_LOST'
| 'SESSION_INVALID'
| 'SESSION_EXPIRED'
| 'SESSION_COMPLETION_FAILED';
message: string;
challenge?: 'BLINK' | 'TURN_LEFT' | 'TURN_RIGHT' | 'LOOK_UP' | 'LOOK_DOWN' | 'LOOK_CENTER';
};
Timeouts and denied camera permission are often recoverable — let the user retry without treating them as a hard failure. SESSION_INVALID / SESSION_EXPIRED should create a new session.
Face match (session results)
The liveness widget does not emit a face-match event. If the session was created with "faceMatch": { "enabled": true }, yes:id runs matching automatically after document capture and liveness both succeed.
Poll your backend (which proxies the secret-key results API) until the FACE_MATCH step appears:
async function fetchFaceMatch(sessionId) {
const response = await fetch(`/session-results/${encodeURIComponent(sessionId)}`);
const data = await response.json();
const steps = data.steps ?? data.data?.steps ?? [];
return steps.find((step) => step.type === 'FACE_MATCH') ?? null;
}
Typical step shape:
{
"type": "FACE_MATCH",
"status": "SUCCEEDED",
"score": 0.91,
"resultData": {
"matched": true,
"similarity": 91
}
}
Interpreting similarity
| Range | Recommendation |
|---|---|
>= 90 | High-confidence match |
75 – 89 | Probable match — apply additional context checks |
< 75 | No match |
resultData.similarity in session results is a percentage (0–100). Keep polling while status is not terminal (SUCCEEDED, FAILED, ERROR, REQUIRES_REVIEW, SKIPPED).
Capture event
type LivenessCapture = {
timestamp: number;
challenge: 'BLINK' | 'TURN_LEFT' | 'TURN_RIGHT' | 'LOOK_UP' | 'LOOK_DOWN' | 'LOOK_CENTER';
imageBase64: string;
imageBlob: Blob;
width: number;
height: number;
};
Use this if you need per-challenge frames. For verification, rely on yesid:success plus backend session results.
Support
The SDK is tested on current Chrome, Safari, Firefox, and Opera. Camera capture requires a secure context (https or localhost).