npm package: @kreltix/liveness-sdk
Description: Kreltix Liveness Detection SDK - Camera-based liveness verification for web and React Native
Latest version: 0.1.2

# @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

```bash
npm install @kreltix/liveness-sdk
```

## Quick Start

```typescript
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

1. Log in to the [Kreltix Dashboard](https://kreltix.com)
2. Go to **API Keys** > **Generate New Key**
3. Select **SDK Key Pair**
4. Copy both keys:
   - `pk_live_...` (public key) — safe to use in frontend/mobile apps
   - `sk_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 returned
```

## API Reference

### `KreltixSDK.initialize(publicKey, options?)`

Creates a new SDK instance.

```typescript
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.

```typescript
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`

```typescript
{
  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

```typescript
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

```html
<div id="liveness-container" style="width: 400px; height: 600px;"></div>
```

```typescript
const result = await sdk.startLivenessCheck({
  container: document.getElementById("liveness-container"),
});
```

### With Metadata for Webhooks

```typescript
const result = await sdk.startLivenessCheck({
  metadata: {
    userId: "user_abc123",
    flow: "kyc_onboarding",
    tier: "premium",
  },
});

// The metadata is included in webhook payloads sent to your server
```

### Full Progress Tracking

```typescript
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

```tsx
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.

```typescript
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.

```typescript
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

1. The **server** picks a random multi-step challenge sequence (e.g., "turn right" then "turn left")
2. Challenges are **unpredictable** — the client cannot know them in advance
3. Each session is **single-use** — a session token works exactly once
4. Sessions **expire after 5 minutes**
5. 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:

```typescript
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:

```json
{
  "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](https://kreltix.com)
- API Docs: [https://api.kreltix.com/docs](https://api.kreltix.com/docs)