Skip to main content

🙂 Face Active Liveness Detection

1 ICper request🔏 HMAC-Signed Verdict
v1.0 Active July 2026 POST /v3/store/ekyc/face-active-liveness/finalize

Welcome to Face Active Liveness Detection API, an AI product developed by iApp Technology Co., Ltd. Unlike passive liveness (which analyzes a single image), active liveness asks the user to perform randomized challenges — blink, turn left, turn right, smile — in front of the camera, proving that a live, cooperative person is present. The session is finalized server-side, and the API returns a cryptographically signed verdict that your backend can verify independently, so a modified client can never forge a "passed" result.

Try the SDK (Live Camera)

Run the full challenge flow right here in your browser with the free open-source iApp eKYC Web SDK — it locks onto your face, issues randomized challenges, selects the best selfie frame, and submits it to the finalize API. See more flows on the full SDK live demo page.

Loading live demo…

How It Works

The flow is designed to be driven by our free open-source eKYC SDK (Flutter and Web), which handles the camera, face tracking, and challenge logic on-device:

  1. Face lock — the SDK finds exactly one frontal face and waits for it to be stable and well-framed.
  2. Randomized challenges — it draws 3 distinct challenges at random from blink / turn left / turn right / smile and verifies each one on-device using real-time face landmarks (e.g. a blink must be a closed-then-open transition, so a printed photo of closed eyes cannot pass).
  3. Best-frame selection — across the whole session, the SDK scores every sharp, frontal, eyes-open frame and keeps the best selfie (sharpness × face size).
  4. Finalize — the SDK submits the best selfie plus the timestamped challenge log to POST /v3/store/ekyc/face-active-liveness/finalize.
  5. Server re-verification — the server validates the challenge log (allowlisted challenge types, at least 2 challenges, all passed, strictly monotonic timestamps, sane per-challenge durations, fresh session) and independently re-checks the selfie with our iBeta Level 1 certified passive-liveness engine.
  6. Signed verdict — the server returns a verdict signed with HMAC-SHA256. The verdict embeds the selfie's SHA-256 hash, binding the decision to the exact image bytes. Your backend verifies the signature with the shared secret issued by iApp and trusts only the signed verdict — never the client's own claim.

Quick Start with the SDK

The fastest way to integrate active liveness is the free, Apache-2.0 licensed iApp eKYC SDK — see the SDK Getting Started guide.

Flutter

# pubspec.yaml
dependencies:
iapp_ekyc_sdk:
git:
url: https://github.com/iapp-technology/iapp-ekyc-sdk.git
path: flutter
import 'package:iapp_ekyc_sdk/iapp_ekyc_sdk.dart';

final client = IappEkycClient(apiKey: 'YOUR_API_KEY');

// Face active liveness with signed server verdict
final liveness = await ActiveLivenessView.start(context, client: client);
if (liveness.verdict.passed) { /* proceed with onboarding */ }

Web (JavaScript)

npm install @iapp-technology/ekyc-sdk
import { IappEkyc } from '@iapp-technology/ekyc-sdk';

const ekyc = new IappEkyc({ apiKey: 'YOUR_API_KEY' });

const liveness = await ekyc.startActiveLiveness({
mount: document.getElementById('ekyc-mount'),
});

Getting Started

  1. Prerequisites

    • An API key from iApp Technology
    • The eKYC SDK (recommended) or an equivalent on-device challenge implementation
    • Selfie formats: JPEG, JPG, PNG
    • Maximum file size: 10MB
  2. Quick Start

    • Drop-in SDK camera flows for Flutter (Android/iOS) and Web
    • Randomized challenge sequence on every session
    • Server-signed verdict for tamper-proof integration
    • Backed by our iBeta Level 1 certified passive-liveness engine
  3. Key Features

    • Blink, turn-left, turn-right, and smile challenges
    • Anti-cheat: session restarts on face loss, multiple faces, or identity switch
    • Best-frame selfie selection (sharpness-scored)
    • HMAC-SHA256 signed verdict bound to the selfie's SHA-256 hash
  4. Security & Compliance

    • GDPR and PDPA compliant
    • No image data retention after processing
    • Signed verdicts verifiable offline on your backend
How to get API Key?

Please visit API Key Management page to view your existing API key or request a new one.

Example

Face Active Liveness Finalize Request:

The SDK builds this request for you. If you call the API directly, submit the best selfie frame plus the JSON challenge log recorded on-device:

curl --location 'https://api.iapp.co.th/v3/store/ekyc/face-active-liveness/finalize' \
--header 'apikey: {YOUR API KEY}' \
--form 'file=@"selfie.jpg"' \
--form 'challenges={
"session_id": "b0e7c1a2-4f5d-4e6a-9b8c-7d6e5f4a3b2c",
"sdk": { "name": "iapp-ekyc-sdk-flutter", "version": "0.1.0", "platform": "android" },
"started_at": 1767500000000,
"finished_at": 1767500008000,
"challenges": [
{ "type": "blink", "issued_at": 1767500000123, "completed_at": 1767500001873, "passed": true },
{ "type": "turn_left", "issued_at": 1767500002000, "completed_at": 1767500004100, "passed": true },
{ "type": "smile", "issued_at": 1767500004500, "completed_at": 1767500006900, "passed": true }
]
}'

Face Active Liveness Finalize Response:

{
"verdict": {
"passed": true,
"passive_liveness": { "predict": "REAL", "real_score": 0.9999, "threshold": 0.5 },
"challenge_summary": {
"total": 3,
"passed": 3,
"types": ["blink", "turn_left", "smile"],
"duration_ms": 8000,
"valid": true,
"reasons": []
},
"session_id": "b0e7c1a2-4f5d-4e6a-9b8c-7d6e5f4a3b2c",
"selfie_sha256": "ab12cd34ef56ab12cd34ef56ab12cd34ef56ab12cd34ef56ab12cd34ef56ab12",
"timestamp": "2026-07-04T09:00:00.000Z",
"nonce": "9f3a1c7e2b8d4f60"
},
"signature": "hex(HMAC-SHA256(secret, canonicalJSON(verdict)))",
"signature_alg": "HMAC-SHA256",
"process_time": 0.42
}

Verifying the Signature (Node.js):

Your backend must recompute the HMAC over the canonical JSON of verdict (all object keys sorted recursively, no insignificant whitespace, UTF-8) using the shared secret issued by iApp, and compare in constant time:

const crypto = require('crypto');
const sortKeysDeep = (v) =>
Array.isArray(v) ? v.map(sortKeysDeep)
: v && typeof v === 'object'
? Object.fromEntries(Object.keys(v).sort().map((k) => [k, sortKeysDeep(v[k])]))
: v;
const canonical = (o) => JSON.stringify(sortKeysDeep(o));
const expected = crypto.createHmac('sha256', SECRET).update(canonical(verdict)).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));

Then check verdict.passed, verdict.timestamp freshness, and — if the selfie is transported separately — that its SHA-256 equals verdict.selfie_sha256.

Features & Capabilities

Core Features

  • Randomized on-device challenge sequence (blink, turn left, turn right, smile) on every session — replay-resistant by design.
  • Server-side re-verification of the selfie with our iBeta Level 1 certified passive-liveness engine.
  • HMAC-SHA256 signed verdict that binds the decision to the exact selfie bytes via selfie_sha256.
  • Strict challenge-log validation: allowlisted types, minimum 2 challenges, strictly monotonic timestamps, per-challenge duration 300 ms–30 s, session length ≤ 120 s, freshness within 5 minutes of server time.
  • Free open-source client SDKs for Flutter (Android/iOS) and Web with a fully themable UI in English, Thai, and Chinese.

Supported Fields

  • Pass/fail liveness verdict with passive-liveness score and threshold.
  • Per-session challenge summary (types, counts, duration, validation reasons).
  • Optional base64 echo of the verified selfie (return_image=true).
  • Compatibility with JPEG, JPG, and PNG selfie images.

API Endpoints

EndpointMethodDescriptionCost
POST /v3/store/ekyc/face-active-liveness/finalizePOSTFinalizes an active liveness session — validates the challenge log, re-verifies the selfie, and returns a signed verdict1 IC per request

API Reference

Face Active Liveness Endpoints

1. Face Active Liveness Finalize

POST /v3/store/ekyc/face-active-liveness/finalize

Finalizes an active liveness session. Validates the on-device challenge log, re-verifies the best selfie frame with the passive-liveness engine, and returns an HMAC-SHA256 signed verdict.

SDK is the intended client

Calling this endpoint directly requires your own implementation of equivalent on-device challenges (randomized selection, real-time landmark verification, honest wall-clock timestamps). The eKYC SDK is the intended client and handles all of this for you.


Request & Response Format

Headers

NameTypeDescription
apikeyStringYour API Key to call this API

Request Body (multipart/form-data)

NameTypeRequiredDescription
fileFileYesBest selfie frame from the session (JPEG/PNG, magic-byte validated server-side, max 10MB)
challengesStringYesJSON string of the challenge log — session ID, SDK info, and per-challenge timestamps (see example above)
return_imageStringNoSet to "true" to receive the selfie echoed back as base64 in the selfie field (default omitted)

Parameter in Response

NameTypeDescription
verdictDictionaryThe signed liveness verdict object
verdict.passedBooleanOverall result — true only if the challenge log is valid AND the selfie passes passive liveness
verdict.passive_livenessDictionaryPassive-liveness re-check: predict (REAL/SPOOF), real_score, threshold
verdict.challenge_summaryDictionaryChallenge validation: total, passed, types, duration_ms, valid, reasons
verdict.session_idStringSession UUID echoed from the challenge log
verdict.selfie_sha256StringSHA-256 hash (64 hex chars) of the uploaded selfie — binds the signature to the image bytes
verdict.timestampStringServer time of the verdict (ISO 8601)
verdict.nonceStringRandom nonce making every verdict unique
signatureStringHex HMAC-SHA256 of the canonical JSON of verdict, keyed with your shared secret
signature_algStringAlways HMAC-SHA256
selfieDictionaryOnly when return_image=true: filename, content_type, size, image_base64
process_timeFloatServer processing time in seconds

Error Codes

CodeErrorDescription
400INVALID_CHALLENGE_LOG / INVALID_IMAGE / MISSING_FIELDMalformed challenge log, invalid image, or missing required field (with reasons array)
401Invalid API keyMissing or invalid apikey header (gateway)
402Insufficient creditTop up at Credits (gateway)
413File too largeSelfie exceeds the 10MB limit
502UPSTREAM_UNAVAILABLELiveness engine temporarily unavailable — retry later
Billing

A completed check that returns "passed": false is still an HTTP 200 and is billed 1 IC — the liveness check ran and produced a signed verdict. Error responses (400/401/402/413/502) are never billed.

Pricing

OperationProduction PathIC CostUnitOn-Premise
Face Active Liveness Finalize/v3/store/ekyc/face-active-liveness/finalize1 ICper 1 requestContact us