Liveness Detection
Confirm that a live human — not a printed photo, video replay, mask, or deepfake — is in front of the camera. AfriRoute offers two modes: passive liveness (no user action, single selfie) and active liveness (the user performs a challenge such as blinking or turning their head).
🚀 Quick Start
curl -X POST https://api.afriroute.ai/api/v1/identity/liveness \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "<base64-selfie>",
"method": "passive"
}'
📡 Endpoint
Check Liveness
POST /v1/identity/liveness
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
image | string | Conditional | Base64 selfie — required for passive method |
video | string | Conditional | Base64 video — required for active method |
method | string | Yes | passive or active |
challenge | string | No | For active: blink, smile, turn_left, turn_right, nod |
challenge_response | string | No | Token issued for the active challenge being answered |
💻 Code Samples
Node.js — passive
const fs = require('fs');
const res = await fetch('https://api.afriroute.ai/api/v1/identity/liveness', {
method: 'POST',
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
image: fs.readFileSync('selfie.jpg', 'base64'),
method: 'passive'
})
});
const data = await res.json();
console.log(`liveness=${data.liveness_passed} confidence=${data.confidence}`);
Python — active challenge
import base64, requests
with open('liveness-video.mp4', 'rb') as f:
video = base64.b64encode(f.read()).decode()
res = requests.post(
'https://api.afriroute.ai/api/v1/identity/liveness',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={'video': video, 'method': 'active', 'challenge': 'blink'}
)
print(res.json()['liveness_passed'])
📊 Response
{
"liveness_passed": true,
"confidence": 96.5,
"method": "passive",
"checks": {
"real_person": true,
"screen_detection": false,
"photo_detection": false,
"mask_detection": false,
"deepfake_detection": false,
"depth_analysis": "passed"
},
"quality_score": 94.2
}
🔍 Choosing a Method
| Passive | Active | |
|---|---|---|
| User action | None | Performs a challenge |
| Input | Single selfie | Short video |
| Friction | Lowest | Slight |
| Security | High | Highest |
| Best for | Quick sign-ups, re-authentication | High-value KYC, account recovery |
💡 Best Practices
- Issue active challenges randomly so attackers cannot pre-record a response.
- Bind liveness to the same session as your face match to prevent injection of an unrelated live frame.
- Run liveness server-side on the raw capture — never trust a client-asserted "live" flag.
- Re-prompt on
quality_scorebelow 80 rather than rejecting outright. - Combine with face matching so a live face is also the correct person.
⚠️ Error Handling
| Code | HTTP | Description |
|---|---|---|
NO_FACE_DETECTED | 422 | No face present in the capture |
LIVENESS_FAILED | 422 | Presentation attack detected |
CHALLENGE_MISMATCH | 422 | Active challenge response did not match the requested action |
MEDIA_TOO_LARGE | 413 | Uploaded image or video exceeds size limit |
See the error code reference for the full list.
📚 Related Resources
Last Updated: May 2026 | Need help? [email protected]