@moveris/shared
v4.2.1
Published
Core business logic for Moveris Live SDK
Readme
@moveris/shared
Core business logic for Moveris Live SDK. This package provides the HTTP client, types, utilities, and frame management for liveness detection.
Installation
pnpm add @moveris/shared
# or
npm install @moveris/shared
# or
yarn add @moveris/sharedQuick Start
import { LivenessClient, generateSessionId } from '@moveris/shared';
// Create the client
const client = new LivenessClient({
apiKey: 'mv_your_api_key',
baseUrl: 'https://api.moveris.com', // optional, uses default
});
// Perform a fast liveness check
const result = await client.fastCheck(frames, {
sessionId: 'my-session-id', // optional — auto-generated if omitted
model: 'mixed-30-v3_1_1', // optional — defaults to DEFAULT_MODEL
source: 'live',
});
console.log(result.verdict); // 'live', 'fake', or 'inconclusive'
console.log(result.confidence); // 0-1
console.log(result.score); // 0-100API Reference
LivenessClient
The main client for interacting with the Moveris Liveness API.
Constructor
const client = new LivenessClient(config: LivenessClientConfig);| Option | Type | Default | Description |
| ----------------------------- | ----------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------ |
| apiKey | string | required | Your Moveris API key |
| baseUrl | string | 'https://api.moveris.com' | API base URL |
| modelVersion | ModelVersion | - | Model version alias for X-Model-Version header (e.g. 'latest') |
| timeout | number | 30000 | Request timeout in milliseconds |
| enableRetry | boolean | true | Enable automatic retry with exponential backoff |
| customFetch | typeof fetch | fetch | Custom fetch implementation (for React Native) |
| deviceIntelligenceOverrides | DeviceIntelligenceOverrides | - | Static overrides merged into every device_intelligence payload (e.g. { vpn_detected: true }) |
| consumer | ConsumerContext | - | Consumer app context — traces which app and environment submitted each transaction |
| trackClientTime | boolean | false | When true, fires a fire-and-forget PATCH after each verdict to record end-to-end client duration |
Methods
fastCheck(frames, options)
Perform fast liveness check with server-side face detection. Send 30, 60 or 120 frames to match the selected model.
const result = await client.fastCheck(frames, {
sessionId: 'optional-uuid',
model: 'mixed-30-v3_1_1',
source: 'live', // 'live' | 'media'
});Parameters:
| Option | Type | Default | Description |
| -------------- | ---------------- | ------------------------------------- | ----------------------------------------------------------------- |
| sessionId | string | auto-generated | Unique session identifier (UUID) |
| model | FastCheckModel | 'mixed-30-v3_1_1' (DEFAULT_MODEL) | Model to use (see Models section) |
| modelVersion | ModelVersion | - | Version alias sent via X-Model-Version header (e.g. 'latest') |
| frameCount | number | - | Frame count for alias-based model resolution |
| source | FrameSource | 'live' | Frame source: 'live' (camera) or 'media' (recorded video) |
Returns: Promise<LivenessResult>
fastCheckCrops(crops, options)
Perform fast liveness check with pre-cropped 224x224 face images. Use this when you handle face detection client-side. Expects CropData[] (no timestamps — only index and pixels).
import type { CropData } from '@moveris/shared';
const crops: CropData[] = capturedFrames.map((f) => ({
index: f.index,
pixels: f.pixels, // base64 224x224 PNG
}));
const result = await client.fastCheckCrops(crops, {
model: 'mixed-30-v3_1_1',
source: 'live',
bgSegmentation: true, // optional — sent as bg_segmentation in payload
});streamFrame(frame, options)
Send a single frame to the streaming endpoint. Returns the API response which may include a partial or final result. Used internally by useLiveness for sequential streaming.
const response = await client.streamFrame(frame, {
sessionId: 'uuid',
model: 'mixed-30-v3_1_1',
source: 'live',
frameIndex: 0,
totalFrames: 30,
});verify(frames, options)
Verify liveness using spatial feature-based detection. Requires minimum 50 frames.
const result = await client.verify(frames, {
fps: 30,
frameWidth: 640,
frameHeight: 480,
});hybridCheck(frames, options)
Perform hybrid liveness check combining CNN with physiological features. Requires minimum 50 frames.
const result = await client.hybridCheck(frames, { fps: 30 });hybrid50(frames, options)
50-frame hybrid model with 93.8% balanced accuracy.
const result = await client.hybrid50(frames, { fps: 30 });hybrid150(frames, options)
150-frame hybrid model with 96.2% balanced accuracy. Best for high-security scenarios.
const result = await client.hybrid150(frames, { fps: 30 });postClientTime(sessionId, clientTime)
Fire-and-forget PATCH to record end-to-end client duration for a completed verification session. No-op when trackClientTime is false. Swallows all errors silently.
// Called automatically by useLiveness / CognitoCheckWidget after the verdict.
// Approach B consumers can call it manually after onComplete resolves:
const elapsed = Math.round(performance.now() - startTime);
void client.postClientTime(result.sessionId, elapsed);updateDeviceIntelligenceOverrides(overrides)
Merge additional fields into the cached device intelligence payload. Call this after construction to inject data only available at runtime (e.g. camera specs, VPN detection results). Merges shallowly — later calls are merged on top of earlier ones.
client.updateDeviceIntelligenceOverrides({
camera: { device_name: 'FaceTime HD Camera', resolution: '1280x720' },
});
client.updateDeviceIntelligenceOverrides({ vpn_detected: true });health()
Check API health status.
const health = await client.health();
console.log(health.status); // 'ok'
console.log(health.models_loaded); // ['mixed-30-v3_1_1', 'mixed-60-v3_1_1', 'mixed-120-v3_1_1']getJobResult(jobId)
Get job status for async processing.
const job = await client.getJobResult('job-uuid');
console.log(job.status); // 'queued' | 'processing' | 'complete' | 'failed'waitForJobResult(jobId, timeout)
Long-poll for job completion (max 120 seconds).
const job = await client.waitForJobResult('job-uuid', 30);Types
FastCheckModel
Model selection for liveness detection speed/accuracy trade-off.
type FastCheckModel =
| 'mixed-30-v3_1_1'
| 'mixed-60-v3_1_1'
| 'mixed-120-v3_1_1'
| 'clock-1s-v3_2'
| 'clock-1s-v4_2'
| 'clock-1s-ensemble-v3_2-v4_2'
| (string & object);Mixed V3.1.1 models (aliases: 'latest', 'v3_1_1'):
| Model | Frames | Description |
| -------------------- | ------ | ------------------------------------- |
| 'mixed-30-v3_1_1' | 30 | Balanced speed and accuracy (default) |
| 'mixed-60-v3_1_1' | 60 | Higher accuracy |
| 'mixed-120-v3_1_1' | 120 | Highest accuracy, longest capture |
Clock-time models score the first 1 s of capture instead of a frame count. They only run on fast-check with full frames; client.fastCheck() drops frames whose timestamp does not advance, rebases timestamps to the first frame, refuses a capture shorter than 1 s with insufficient_duration, and sends real capture timestamps without frame_count or the X-Model-Version header:
| Model | Capture | Description |
| ------------------------------- | ------- | -------------------------------------------------------- |
| 'clock-1s-v3_2' | 1 s | Clock V3.2, at most 120 frames inside the window |
| 'clock-1s-ensemble-v3_2-v4_2' | 1 s | Ensemble of Clock V3.2 and V4.2 |
| 'clock-1s-v4_2' | 1 s | In MODEL_CONFIGS but not listed by getActiveModels() |
clock-1s-v4_2 is not offered on its own because it accepts too many fakes below about 25 fps. Every other client method (fastCheckCrops, streamFrame, fastCheckStream, fastCheckStreamSequential, liveCheck and the v2 methods) throws model_endpoint_not_supported before sending anything. When the model cannot score a capture, the result has verdict 'inconclusive' and the reason in unscorableReason.
When model is omitted, every LivenessClient method uses DEFAULT_MODEL ('mixed-30-v3_1_1'). For the live model registry served by the API, call client.getModels() (GET /api/v1/models).
ConsumerContext
Consumer app context attached to every verification request for transaction tracing.
interface ConsumerContext {
url: string; // The consumer app's URL
env: 'development' | 'staging' | 'production'; // Consumer app environment
}Pass it at client construction — it is included in every subsequent request automatically:
const client = new LivenessClient({
apiKey: 'mv_your_api_key',
consumer: { url: 'https://app.example.com', env: 'production' },
trackClientTime: true, // optional — fires postClientTime after each verdict
});FrameSource
Source of the captured frames.
type FrameSource = 'media' | 'live';| Value | Description |
| --------- | ----------------------- |
| 'live' | Real-time camera feed |
| 'media' | Pre-recorded video file |
Verdict
Liveness determination result.
type Verdict = 'live' | 'fake' | 'inconclusive';| Value | Description |
| ---------------- | -------------------------------------------------------------------------------- |
| 'live' | Real person detected |
| 'fake' | Spoofing attempt detected (photo, video, mask, etc.) |
| 'inconclusive' | API processed successfully but could not determine a verdict — retry recommended |
LivenessState
State machine for liveness detection flow.
type LivenessState =
| 'idle' // Ready to start
| 'capturing' // Capturing frames from camera
| 'uploading' // Uploading frames to API
| 'processing' // API is processing frames
| 'complete' // Verification complete
| 'error'; // Error occurredLivenessResult
Result returned from liveness verification.
interface LivenessResult {
verdict: Verdict; // 'live', 'fake', or 'inconclusive'
confidence: number; // Confidence score (0-1)
score: number; // Liveness score (0-100)
sessionId: string; // Session identifier
processingMs: number; // Server processing time
framesProcessed: number; // Number of frames analyzed
deprecation?: DeprecationInfo; // Present when X-Moveris-Model-Resolved header is returned
clientTime?: number; // End-to-end client duration in ms (start() to verdict received)
}CapturedFrame
Individual frame captured from camera.
interface CapturedFrame {
index: number; // Frame sequence number (0-based)
timestampMs: number; // Timestamp from capture start
pixels: string; // Base64-encoded image data
}CropData
Pre-cropped face image for the fast-check-crops endpoint. Omits timestampMs since timing is not needed for cropped submissions.
interface CropData {
index: number; // Frame sequence number (0-based)
pixels: string; // Base64-encoded 224x224 PNG image
}ModelVersion
Version alias sent via the X-Model-Version request header for server-side model resolution.
type ModelVersion = 'latest' | 'v3_1_1' | (string & object);| Value | Resolves to |
| ---------- | ----------------------------- |
| 'latest' | Mixed V3.1.1 family (current) |
| 'v3_1_1' | Mixed V3.1.1 family (pinned) |
When
X-Model-Versionis set, the API also requiresframe_countin the request body. Pass it via theframeCountoption.
DeprecationInfo
Parsed from API response headers when the server resolves a model.
interface DeprecationInfo {
deprecated: boolean; // true when model is deprecated
resolvedModel: string; // Concrete model id (X-Moveris-Model-Resolved)
deprecatedModel?: string; // Deprecated model id (X-Moveris-Deprecated-Model)
sunsetDate?: string; // ISO date of removal (Sunset header)
suggestedModel?: string; // Replacement model (X-Moveris-Suggested-Model)
}Model Versioning & Deprecation
The client supports model alias resolution via the X-Model-Version header and automatically parses deprecation headers from API responses.
// Use aliases for automatic server-side resolution
const client = new LivenessClient({
apiKey: 'mv_your_api_key',
modelVersion: 'latest', // sent as X-Model-Version header on every request
});
const result = await client.fastCheck(frames, {
model: 'mixed-30-v3_1_1',
frameCount: 30, // required when modelVersion is set
});
// Deprecation info is automatically parsed from response headers
if (result.deprecation?.deprecated) {
console.warn(`Model "${result.deprecation.resolvedModel}" is deprecated.`);
console.warn(`Migrate to "${result.deprecation.suggestedModel}".`);
console.warn(`Sunset date: ${result.deprecation.sunsetDate}`);
}
// Per-call override (takes precedence over client-level modelVersion)
const result2 = await client.fastCheck(frames, {
modelVersion: 'v3_1_1',
frameCount: 60,
});Valid Frame Counts
When using model version aliases, frameCount must be one of:
import { VALID_FRAME_COUNTS } from '@moveris/shared';
// [30, 60, 120]Utilities
generateSessionId()
Generate a UUID v4 for session identification.
import { generateSessionId } from '@moveris/shared';
const sessionId = generateSessionId();
// e.g., 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'Session ID injection: Every
LivenessClientmethod (fastCheck,fastCheckCrops,streamFrame,verify,hybrid50,hybrid150) accepts an optionalsessionIdin its options object. If provided, that ID is used for the request; if omitted, a new UUID is generated automatically. This is useful for debugging, testing, or correlating client requests with server logs. In@moveris/react, thesessionIdcan be injected via theuseLivenesshook config or theLivenessView/LivenessModalprops.
toFrameData(frames)
Convert CapturedFrame array to API format.
import { toFrameData } from '@moveris/shared';
const apiFrames = toFrameData(capturedFrames);toHybridFrameData(frames)
Convert frames to hybrid endpoint format.
import { toHybridFrameData } from '@moveris/shared';
const hybridFrames = toHybridFrameData(capturedFrames);Frame Buffer Management
FrameBuffer
Manages a sliding window buffer of frames with automatic cleanup.
import { FrameBuffer } from '@moveris/shared';
const buffer = new FrameBuffer({
maxSize: 10, // Maximum frames to keep
maxMemoryBytes: 4 * 1024 * 1024, // 4MB limit
});
// Add frames
buffer.add(frame);
// Get all frames
const frames = buffer.getAll();
// Acknowledge processed frames
buffer.acknowledge(5); // Remove frames up to index 5
// Clear buffer
buffer.clear();FrameQueue
FIFO queue for frame processing.
import { FrameQueue } from '@moveris/shared';
const queue = new FrameQueue({ maxSize: 50 });
queue.enqueue(frame);
const frame = queue.dequeue();
const peeked = queue.peek();Error Handling
LivenessApiError
Custom error class for API errors.
import { LivenessApiError } from '@moveris/shared';
try {
await client.fastCheck(frames);
} catch (error) {
if (error instanceof LivenessApiError) {
console.log(error.code); // 'insufficient_frames'
console.log(error.message); // 'Not enough frames provided'
console.log(error.statusCode); // 400
console.log(error.required); // 10
console.log(error.received); // 5
}
}Error Codes:
| Code | Description |
| --------------------- | -------------------------- |
| insufficient_frames | Not enough frames provided |
| invalid_model | Invalid model specified |
| invalid_key | Invalid API key |
| missing_field | Required field missing |
| timeout | Request timeout |
| network_error | Network connectivity issue |
Feedback Messages
Localized user feedback for liveness detection UI.
import { getFeedbackMessage, getStatusMessage, DEFAULT_LOCALE, ES_LOCALE } from '@moveris/shared';
// Get localized feedback
const message = getFeedbackMessage('face_not_centered', ES_LOCALE);
// "Centra tu rostro en el óvalo"
// Get status message
const status = getStatusMessage('capturing', DEFAULT_LOCALE);
// "Hold still..."Feedback Keys
| Key | English | Description |
| ------------------- | -------------------------------- | --------------------------------------------------------------------------------- |
| no_face | "No face detected" | Face detection failed |
| face_not_centered | "Center your face in the circle" | Face outside guide |
| too_close | "Move back a little" | Face too close to camera |
| too_far | "Move closer" | Face too far from camera |
| poor_lighting | "Improve lighting" | Insufficient light |
| hold_still | "Hold still" | Movement detected |
| capturing | "Capturing..." | Frame capture in progress |
| processing | "Processing..." | API verification in progress |
| success | "Verification complete" | Successful completion |
| failed | "Verification failed" | Failed verification |
| eyes_not_visible | "Eyes not clearly visible" | Eye region featureless |
| eyes_shadowed | "Eyes are in shadow…" | Eye region too dark |
| eyes_overexposed | "Eye region overexposed…" | Eye region too bright |
| glasses_glare | "Glare detected…" | Specular highlights on eyes |
| eye_quality_poor | "Eye region quality is poor" | Generic eye quality failure |
| camera_angle_low | "Raise camera to eye level" | Camera below face (Y < 0.3) |
| camera_angle_high | "Lower camera to eye level" | Camera above face (Y > 0.7) |
| camera_tilted | "Hold camera level" | Device tilt > 15° (via transformation matrix, falls back to eye-corner landmarks) |
| flat_lighting | "Find better lighting" | Face-region dynamic range < 60 L-units — soft warning, capture continues |
Oval Guide States
Visual feedback states for the face positioning guide.
import { getOvalGuideState, OVAL_GUIDE_COLORS } from '@moveris/shared';
const state = getOvalGuideState(alignmentScore);
// Returns: 'no_face' | 'poor' | 'good' | 'perfect'
const color = OVAL_GUIDE_COLORS[state];
// 'red' | 'orange' | 'yellow' | 'green'| State | Color | Alignment Score |
| --------- | ------ | ---------------- |
| no_face | Red | No face detected |
| poor | Orange | < 0.5 |
| good | Yellow | 0.5 - 0.8 |
| perfect | Green | > 0.8 |
Eye Region Quality Analysis
Pre-request eye-region quality gate. Platform-agnostic functions that analyze RGBA pixel data from eye regions to detect shadows, glare, occlusion, and poor visibility.
import {
checkEyeRegionQuality,
analyzeEyeRegionBrightness,
analyzeEyeRegionContrast,
detectSpecularHighlights,
EYE_QUALITY_THRESHOLDS,
} from '@moveris/shared';
// Combined quality check (recommended)
const quality = checkEyeRegionQuality(eyeRegionPixels);
console.log(quality.passed); // boolean
console.log(quality.brightness); // 0-255
console.log(quality.contrast); // standard deviation of luminance
console.log(quality.hasGlare); // boolean
console.log(quality.glareRatio); // 0-1
console.log(quality.message); // user-facing feedback or null
// Individual checks
const brightness = analyzeEyeRegionBrightness(pixels);
const contrast = analyzeEyeRegionContrast(pixels);
const glareRatio = detectSpecularHighlights(pixels);| Check | Threshold | Condition |
| ----------- | --------------------- | --------------------------------------- |
| Shadowed | minBrightness: 40 | Average luminance too low |
| Overexposed | maxBrightness: 230 | Average luminance too high |
| Glare | maxGlareRatio: 0.15 | >15% of pixels are specular highlights |
| Occluded | minContrast: 12 | Standard deviation of luminance too low |
Glare detection uses a relative threshold (mean brightness × 2.5) so sensitivity adapts to ambient lighting, avoiding false positives in bright environments.
Custom thresholds can be passed as a second argument to checkEyeRegionQuality().
Eye Region Landmarks
Extract bounding boxes for left and right eye regions from MediaPipe 468-point face mesh landmarks.
import { getEyeRegionBounds, EYE_LANDMARK_INDICES, validateFaceLandmarks } from '@moveris/shared';
const bounds = getEyeRegionBounds(landmarks);
if (bounds) {
console.log(bounds.leftEye); // { x, y, width, height } (normalized 0-1)
console.log(bounds.rightEye); // { x, y, width, height } (normalized 0-1)
}Frame Analysis Utilities
Utilities for assessing frame quality before submission.
import {
isFaceFullyVisible,
isFaceInOval,
calculateFaceCropRegion,
detectFaceRoll,
detectFaceRollFromMatrix,
detectCameraAngle,
} from '@moveris/shared';
// Check if face is fully visible in frame
const visibility = isFaceFullyVisible(faceBbox, frameWidth, frameHeight);
console.log(visibility.isVisible); // true/false
console.log(visibility.reason); // 'left_edge' | 'top_edge' | etc.
// Check if face is within the oval guide
const inOval = isFaceInOval(faceBbox, ovalRegion);
console.log(inOval.isInOval); // true/false
console.log(inOval.alignmentScore); // 0-1
// Calculate crop region for face — frame-relative 20% margin on each side,
// matches the API server-side crop and the model training pipeline.
const cropRegion = calculateFaceCropRegion(faceBbox, frameWidth, frameHeight);
// { x, y, width, height } — possibly non-square rectangle in source pixel
// coordinates. Callers must resize the cropped pixels to 224×224 with
// bilinear interpolation; non-square crops are squashed (intentional, matches
// cv2.resize behaviour applied server-side).
// Detect face roll (device tilt) — prefer matrix when available (more accurate under glasses/occlusion)
if (facialTransformationMatrix) {
const rollResult = detectFaceRollFromMatrix(facialTransformationMatrix); // 16-element column-major 4×4
// { roll: number (degrees), tooTilted: boolean }
} else {
const rollResult = detectFaceRoll(landmarks); // fallback: eye-corner landmarks, requires ≥364 points
// { roll: number (degrees), tooTilted: boolean }
}
// Detect camera vertical angle from forehead/nose/chin landmark ratio
const angleResult = detectCameraAngle(landmarks); // requires ≥153 landmarks
// { ratio: number, cameraAbove: boolean, cameraBelow: boolean }
// ratio ~1.0 at eye level; >1.35 = camera above user; <0.75 = camera below user
// Analyse face-region dynamic range (flat lighting detection, MOV-1280)
// Pass pre-sampled face-region pixels — use sampleFaceRegionPixels() in @moveris/react
import { analyzeDynamicRange } from '@moveris/shared';
const drResult = analyzeDynamicRange(rgbaPixels);
// { range: number (max L* − min L*), isLow: boolean (< 60) }
// Soft warning — do not use as a hard capture gateCrop Constants (aligned with the model contract)
These constants control how face crops are generated for the fast-check-crops endpoint. They match the algorithm applied server-side in m-ai-check-api and during model training:
- Detect the face. Get the bounding box in source pixel coordinates.
- Expand the bbox by
FACE_CROP_FRAME_MARGIN × frameWidth/Heighton each side. - Clamp the resulting rectangle to
[0, frameWidth] × [0, frameHeight]. The crop is not necessarily square. - Resize the cropped pixels to exactly
FACE_CROP_OUTPUT_SIZE × FACE_CROP_OUTPUT_SIZEwith bilinear interpolation. Non-square crops are squashed (matchescv2.resizeserver-side).
| Constant | Value | Description |
| -------------------------------- | ------ | --------------------------------------------------------------------- |
| FACE_CROP_FRAME_MARGIN | 0.2 | Margin around the face bbox, as a fraction of frame W/H, on each side |
| FACE_CROP_OUTPUT_SIZE | 224 | Required output size for the cropped face image (px) |
| MAX_FACE_PERCENTAGE_IN_CROP | 0.45 | UX guard — nudge users back when the face dominates the crop |
| TARGET_FACE_PERCENTAGE_IN_CROP | 0.33 | UX-only target framing; not a model contract |
Model Configuration
import {
DEFAULT_MODEL,
MODEL_CONFIGS,
VALID_FRAME_COUNTS,
isDeprecatedModel,
getActiveModels,
getMinFramesForModel,
isClockTimeModel,
getCaptureRequirement,
ClockCaptureTracker,
} from '@moveris/shared';
// Default model used when `model` is omitted
console.log(DEFAULT_MODEL); // 'mixed-30-v3_1_1'
// Access model metadata
const config = MODEL_CONFIGS['mixed-60-v3_1_1'];
console.log(config.minFrames); // 60
console.log(config.deprecated); // false
console.log(config.aliases); // ['latest', 'v3_1_1']
// Check if a model is deprecated (runtime deprecation arrives via result.deprecation)
isDeprecatedModel('mixed-30-v3_1_1'); // false
// Get only active (non-deprecated, exposed) models
const active = getActiveModels();
// ['mixed-30-v3_1_1', 'mixed-60-v3_1_1', 'mixed-120-v3_1_1', 'clock-1s-v3_2', 'clock-1s-ensemble-v3_2-v4_2']
// Unknown model ids fall back to the default frame budget
getMinFramesForModel('some-future-model'); // 30
// Clock-time models are captured by time; their frame count is only a cap
isClockTimeModel('clock-1s-v3_2'); // true
getCaptureRequirement('clock-1s-v3_2');
// { kind: 'duration', durationMs: 1000, targetMs: 1100, maxFrames: 120 }
getCaptureRequirement('clock-2s-v5'); // id outside MODEL_CONFIGS: durationMs 2000, targetMs 2200
getCaptureRequirement('mixed-30-v3_1_1'); // { kind: 'frames', frames: 30 }
getMinFramesForModel('clock-1s-v3_2'); // 2 (the API minimum; the frame cap is getCaptureRequirement().maxFrames)
// Track a clock-time capture in a custom hook
// Sized from the model: completes at its target coverage or frame cap; null for frame-count models
const tracker = ClockCaptureTracker.forModel('clock-1s-v3_2');
tracker?.add(performance.now() - captureStart); // false for a timestamp that does not advance
tracker?.coveredMs; // (last - first) + median frame gap, as the API measures it
tracker?.isComplete;
VALID_FRAME_COUNTS; // [30, 60, 120]MODEL_CONFIGS is the compile-time fallback. For the live registry, call client.getModels() (GET /api/v1/models).
Device Intelligence
The SDK automatically collects device metadata and attaches it to every API request as device_intelligence. Collection happens inside LivenessClient — no extra work required for standard consumers.
collectDeviceIntelligence(opts?)
Collect device metadata manually. Useful for Approach B consumers (custom UI + raw fetch()) who bypass LivenessClient.
import { collectDeviceIntelligence } from '@moveris/shared';
const di = await collectDeviceIntelligence({
platformVersion: '[email protected]', // optional — defaults to SHARED_SDK_PLATFORM
overrides: { vpn_detected: true }, // optional — merged on top of collected data
});
if (di) {
console.log(di.ip); // '1.2.3.4'
console.log(di.country); // 'US'
console.log(di.isp); // 'Comcast Cable'
console.log(di.platform_version); // '[email protected]'
}
// Returns null if ipinfo.io is unreachable — handle gracefullyIf you import from
@moveris/react, use that package'scollectDeviceIntelligenceinstead — it pre-bakesREACT_SDK_PLATFORMas the defaultplatformVersionso the full[email protected][email protected]string is used automatically.
SHARED_SDK_PLATFORM
Pre-formatted platform string for the platform_version field. Reads the version from package.json at build time — never goes stale.
import { SHARED_SDK_PLATFORM } from '@moveris/shared';
console.log(SHARED_SDK_PLATFORM); // '[email protected]'Device Intelligence Types
import type {
DeviceIntelligence,
DeviceIntelligenceOverrides,
DeviceIntelligenceGeo,
DeviceIntelligenceCamera,
} from '@moveris/shared';interface DeviceIntelligence {
ip: string;
country: string; // ISO 3166-1 alpha-2, e.g. 'US'
region: string; // e.g. 'California'
city: string; // e.g. 'San Francisco'
isp?: string; // Internet service provider
user_agent?: string; // navigator.userAgent
platform_version?: string; // SDK version string
vpn_detected?: boolean;
camera?: DeviceIntelligenceCamera;
}
interface DeviceIntelligenceCamera {
device_name?: string; // e.g. 'FaceTime HD Camera'
resolution?: string; // e.g. '1280x720'
}
interface DeviceIntelligenceOverrides {
vpn_detected?: boolean;
camera?: DeviceIntelligenceCamera;
[key: string]: unknown;
}CSP Note
If your app enforces a Content Security Policy, add https://ipinfo.io to connect-src:
connect-src 'self' https://api.moveris.com https://ipinfo.io;Without this, the ipinfo.io fetch will be blocked silently and device_intelligence will be omitted from all requests.
Configuration Constants
import { DEFAULT_ENDPOINT, AUTH_CONFIG, RETRY_CONFIG, FRAME_CONFIG } from '@moveris/shared';
console.log(DEFAULT_ENDPOINT); // 'https://api.moveris.com'
console.log(AUTH_CONFIG.apiKeyHeader); // 'X-API-Key'
console.log(RETRY_CONFIG.maxAttempts); // 3
console.log(FRAME_CONFIG.maxBufferSize); // 10TypeScript Support
This package is written in TypeScript and provides full type definitions.
import type {
LivenessResult,
LivenessState,
LivenessConfig,
FastCheckModel,
ModelVersion,
DeprecationInfo,
ModelConfig,
FrameSource,
Verdict,
CapturedFrame,
CropData,
ConsumerContext,
LivenessClientConfig,
DetectionResult,
DetectionSummary,
FaceBoundingBox,
HeadPose,
FaceLandmarkPoint,
LandmarkValidationResult,
EyeRegionQuality,
EyeQualityThresholds,
EyeRegionBounds,
EyeRegionsBounds,
// Device Intelligence Types
DeviceIntelligence,
DeviceIntelligenceOverrides,
DeviceIntelligenceGeo,
DeviceIntelligenceCamera,
} from '@moveris/shared';License
MIT