@kreltix/liveness-sdk
v0.2.1
Published
Kreltix Liveness Detection SDK - Camera-based liveness verification for web and React Native
Maintainers
Readme
@kreltix/liveness-sdk
Liveness detection SDK for web and React Native applications. Verifies that a real person is present by guiding them through randomized face challenges (head turns, blinks, smiles) with an interactive camera overlay.
Installation
npm install @kreltix/liveness-sdkQuick Start
import { KreltixSDK } from "@kreltix/liveness-sdk";
const sdk = KreltixSDK.initialize("pk_live_your_public_key");
const result = await sdk.startLivenessCheck();
if (result.isLive) {
console.log("Verified!", result.confidence);
} else {
console.log("Failed:", result.recommendation);
}That's it. The SDK handles the entire flow: opens the camera, shows animated guides for each challenge, records video, submits to the server, and returns the result.
Getting Your Keys
- Log in to the Kreltix Dashboard
- Go to API Keys > Generate New Key
- Select SDK Key Pair
- Copy both keys:
pk_live_...(public key) — safe to use in frontend/mobile appssk_live_...(secret key) — server-side only, never expose to clients
How It Works
Your App SDK Kreltix API
──────── ─── ───────────
startLivenessCheck()
→ Create session ← Server picks challenges
(e.g. turn_right → turn_left)
→ Open camera
→ Show animated guide Step 1: "Turn right"
→ Record 3-7s video
Step 2: "Turn left"
→ Show animated guide
→ Record 3-7s video
→ Upload & verify ← Server analyzes frames:
Show processing animation challenge detection,
anti-spoof checks,
3D depth estimation
→ Show result animation ← { isLive, confidence }
← result returnedAPI Reference
KreltixSDK.initialize(publicKey, options?)
Creates a new SDK instance.
const sdk = KreltixSDK.initialize("pk_live_...", {
baseUrl: "https://api.kreltix.com", // default
timeout: 10000, // ms, default 10s
maxRetries: 2, // default 2
});| Parameter | Type | Description |
|---|---|---|
| publicKey | string | Your pk_live_ public key (required) |
| options.baseUrl | string | API base URL. Override for self-hosted deployments |
| options.timeout | number | Request timeout in ms (default: 10000) |
| options.maxRetries | number | Retry count for failed requests (default: 2) |
sdk.startLivenessCheck(options?)
Runs the full liveness verification flow. Returns a promise that resolves with the result.
const result = await sdk.startLivenessCheck({
// Mount inside a specific element (default: fullscreen overlay)
container: document.getElementById("my-container"),
// Show built-in UI overlay (default: true)
showOverlay: true,
// Attach metadata to the session (passed through to webhooks)
metadata: { userId: "user_123", flow: "onboarding" },
// Callbacks
onSessionCreated: (session) => {
console.log("Challenges:", session.challenges);
},
onChallengeStarted: (challenge) => {
console.log("Now doing:", challenge);
},
onProgress: (event) => {
console.log(event.stage, event.message);
},
onError: (error) => {
console.error("SDK error:", error.message);
},
});Options
| Option | Type | Default | Description |
|---|---|---|---|
| container | HTMLElement | document.body | Element to mount the camera UI into |
| showOverlay | boolean | true | Show the built-in challenge overlay UI |
| metadata | object | — | Custom metadata attached to the session |
| cameraAdapter | CameraAdapter | — | Custom camera adapter (for React Native) |
| onSessionCreated | function | — | Called when the server creates the session |
| onChallengeStarted | function | — | Called when each challenge step begins |
| onProgress | function | — | Called with progress updates throughout the flow |
| onError | function | — | Called on any error |
Result: LivenessResult
{
sessionId: string; // Unique session identifier
isLive: boolean; // true if all checks passed
confidence: number; // 0-100 confidence score
allChallengesPassed: boolean;
challengeResults: [ // Per-step results
{ challenge: "turn_right", completed: true, indicators: {...} },
{ challenge: "turn_left", completed: true, indicators: {...} },
];
recommendation: string; // Human-readable result message
error: string | null; // Error message if something failed
indicators: object | null; // Detailed analysis indicators
}Progress Stages
The onProgress callback receives events with these stages:
| Stage | When |
|---|---|
| session_created | Server session created, challenges assigned |
| camera_ready | Camera stream active |
| recording | Recording a challenge step |
| uploading | Submitting videos to server |
| complete | Result received |
Examples
Basic — Fullscreen Overlay
import { KreltixSDK } from "@kreltix/liveness-sdk";
const sdk = KreltixSDK.initialize("pk_live_...");
document.getElementById("verify-btn").addEventListener("click", async () => {
try {
const result = await sdk.startLivenessCheck();
if (result.isLive) {
showSuccess(`Verified with ${result.confidence}% confidence`);
} else {
showError(result.recommendation);
}
} catch (err) {
if (err.code === "CAMERA_ERROR") {
showError("Please allow camera access to continue.");
} else {
showError("Verification failed. Please try again.");
}
}
});Embedded in a Container
<div id="liveness-container" style="width: 400px; height: 600px;"></div>const result = await sdk.startLivenessCheck({
container: document.getElementById("liveness-container"),
});With Metadata for Webhooks
const result = await sdk.startLivenessCheck({
metadata: {
userId: "user_abc123",
flow: "kyc_onboarding",
tier: "premium",
},
});
// The metadata is included in webhook payloads sent to your serverFull Progress Tracking
const result = await sdk.startLivenessCheck({
onSessionCreated: (session) => {
console.log("Session:", session.session_id);
console.log("Challenges:", session.challenges.join(" → "));
// e.g. ["turn_right", "turn_left"]
},
onChallengeStarted: (challenge) => {
analytics.track("liveness_challenge_started", { challenge });
},
onProgress: ({ stage, message }) => {
updateStatusBar(stage, message);
},
onError: (error) => {
errorReporting.capture(error);
},
});React Component
import { useState } from "react";
import { KreltixSDK, LivenessResult, KreltixError } from "@kreltix/liveness-sdk";
const sdk = KreltixSDK.initialize("pk_live_...");
function LivenessButton() {
const [status, setStatus] = useState<"idle" | "running" | "passed" | "failed">("idle");
const handleVerify = async () => {
setStatus("running");
try {
const result = await sdk.startLivenessCheck({
metadata: { component: "onboarding" },
});
setStatus(result.isLive ? "passed" : "failed");
} catch (err) {
setStatus("failed");
}
};
return (
<button onClick={handleVerify} disabled={status === "running"}>
{status === "running" ? "Verifying..." : "Verify Identity"}
</button>
);
}React Native
The SDK supports React Native via a custom camera adapter. You provide the camera implementation, the SDK handles the rest.
import { KreltixSDK, CameraAdapter } from "@kreltix/liveness-sdk";
import { Camera } from "expo-camera";
class ExpoCameraAdapter implements CameraAdapter {
private camera: Camera | null = null;
private videoElement: HTMLVideoElement;
constructor() {
this.videoElement = document.createElement("video");
}
async start(facingMode: "user" | "environment") {
const { status } = await Camera.requestCameraPermissionsAsync();
if (status !== "granted") throw new Error("Camera permission denied");
// Return the camera stream
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode },
});
return stream;
}
stop() {
// Clean up camera resources
}
getVideoElement() {
return this.videoElement;
}
}
const sdk = KreltixSDK.initialize("pk_live_...");
const result = await sdk.startLivenessCheck({
cameraAdapter: new ExpoCameraAdapter(),
});Error Handling
All errors extend KreltixError with a code property and isRetryable flag.
import {
KreltixError,
KreltixCameraError,
KreltixAuthError,
KreltixInsufficientFundsError,
} from "@kreltix/liveness-sdk";
try {
const result = await sdk.startLivenessCheck();
} catch (err) {
if (err instanceof KreltixCameraError) {
// User denied camera permission or no camera available
showMessage("Camera access is required for verification.");
} else if (err instanceof KreltixAuthError) {
// Invalid or expired API key
showMessage("Configuration error. Contact support.");
} else if (err instanceof KreltixInsufficientFundsError) {
// Account balance too low
showMessage("Service temporarily unavailable.");
} else if (err instanceof KreltixError && err.isRetryable) {
// Network/server error — safe to retry
showMessage("Connection issue. Please try again.");
} else {
showMessage("Verification failed. Please try again.");
}
}Error Types
| Error | Code | Retryable | Cause |
|---|---|---|---|
| KreltixCameraError | CAMERA_ERROR | No | Camera permission denied or not available |
| KreltixAuthError | AUTH_ERROR | No | Invalid or expired public key |
| KreltixInsufficientFundsError | INSUFFICIENT_FUNDS | No | Account wallet balance too low |
| KreltixSessionExpiredError | SESSION_EXPIRED | No | Verification session timed out (5 min) |
| KreltixValidationError | VALIDATION_ERROR | No | Invalid request (e.g. session already used) |
| KreltixNetworkError | NETWORK_ERROR | Yes | Network failure or request timeout |
| KreltixServerError | SERVER_ERROR | Yes | Server-side error (5xx) |
Security Model
Public Key (pk_live_)
- Safe to embed in client-side apps (web, mobile)
- Can only create liveness sessions and submit verifications
- Cannot access account data, billing, verification history, or any other API
- Rate limited (30 requests/minute)
Secret Key (sk_live_)
- Server-side only — never expose in client code
- Full API access: read session results, verification history, account management
- Use for webhooks, backend integrations, and admin operations
How Challenges Work
- The server picks a random multi-step challenge sequence (e.g., "turn right" then "turn left")
- Challenges are unpredictable — the client cannot know them in advance
- Each session is single-use — a session token works exactly once
- Sessions expire after 5 minutes
- The server performs the actual liveness analysis (anti-spoof, depth estimation, texture analysis) — on-device detection is only for UX guidance
Browser Support
- Chrome 70+
- Firefox 65+
- Safari 14.1+
- Edge 79+
Requires getUserMedia and MediaRecorder APIs.
TypeScript
The SDK is written in TypeScript and ships with full type definitions. All types are exported:
import type {
LivenessResult,
LivenessOptions,
ProgressEvent,
ChallengeStepResult,
CameraAdapter,
} from "@kreltix/liveness-sdk";Webhooks
When a liveness check completes, Kreltix sends a webhook to your server (if configured in the dashboard) with the full result:
{
"event": "liveness_session.completed",
"session_id": "69c9ce0c...",
"status": "completed",
"challenges": ["turn_right", "turn_left"],
"is_live": true,
"confidence": 99.99,
"charge_amount": 90.0,
"metadata": { "userId": "user_123" },
"timestamp": "2026-03-30T06:12:44.000Z"
}Webhook payloads are signed with HMAC-SHA256. Verify the signature using your webhook secret from the dashboard.
Support
- Dashboard: https://kreltix.com
- API Docs: https://api.kreltix.com/docs
