Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Detects liveness from a single selfie image via the Didit standalone API. Use when checking if a person is physically present, detecting spoofing or presentation attacks, implementing anti-spoofing measures, or performing passive liveness verification. Returns liveness score, face quality, and luminance metrics. 99.9% accuracy.
.claude/skills/didit-protocol-didit-liveness-detection/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 76% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 124% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 134% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 82% | 0% |
Verifies that a user is physically present by analyzing a single captured image — no explicit movement or interaction required.
Key constraints:
Accuracy: 99.9% liveness detection accuracy, <0.1% false acceptance rate (FAR).
Capabilities: Liveness scoring, face quality assessment, luminance analysis, age/gender estimation, spoof detection (screen captures, printed copies, masks, deepfakes), duplicate face detection across sessions, blocklist matching.
Liveness methods: This standalone endpoint uses PASSIVE method (single-frame CNN). Workflow mode also supports ACTIVE_3D (action + flash, highest security) and FLASHING (3D flash, high security).
API Reference: https://docs.didit.me/standalone-apis/passive-liveness Feature Guide: https://docs.didit.me/core-technology/liveness/overview
All requests require x-api-key header. Get your key from Didit Business Console → API & Webhooks, or via programmatic registration (see below).
If you don't have a Didit API key, create one in 2 API calls:
POST https://apx.didit.me/auth/v2/programmatic/register/ with {"email": "you@gmail.com", "password": "MyStr0ng!Pass"}POST https://apx.didit.me/auth/v2/programmatic/verify-email/ with {"email": "you@gmail.com", "code": "A3K9F2"} → response includes api_keyTo add credits: GET /v3/billing/balance/ to check, POST /v3/billing/top-up/ with {"amount_in_dollars": 50} for a Stripe checkout link.
See the didit-verification-management skill for full platform management (workflows, sessions, users, billing).
POST https://verification.didit.me/v3/passive-liveness/| Header | Value | Required | |---|---|---| | x-api-key | Your API key | Yes | | Content-Type | multipart/form-data | Yes |
| Parameter | Type | Required | Default | Constraints | Description | |---|---|---|---|---|---| | user_image | file | Yes | — | JPEG/PNG/WebP/TIFF, max 5MB | User's face image | | face_liveness_score_decline_threshold | integer | No | — | 0-100 | Scores below this = Declined | | rotate_image | boolean | No | — | — | Try rotations to find upright face | | save_api_request | boolean | No | true | — | Save in Business Console | | vendor_data | string | No | — | — | Your identifier for session tracking |
pythonimport requests response = requests.post( "https://verification.didit.me/v3/passive-liveness/", headers={"x-api-key": "YOUR_API_KEY"}, files={"user_image": ("selfie.jpg", open("selfie.jpg", "rb"), "image/jpeg")}, data={"face_liveness_score_decline_threshold": "80"}, )
typescriptconst formData = new FormData(); formData.append("user_image", selfieFile); formData.append("face_liveness_score_decline_threshold", "80"); const response = await fetch("https://verification.didit.me/v3/passive-liveness/", { method: "POST", headers: { "x-api-key": "YOUR_API_KEY" }, body: formData, });
json{ "request_id": "a1b2c3d4-...", "liveness": { "status": "Approved", "method": "PASSIVE", "score": 95, "user_image": { "entities": [ {"age": 22.16, "bbox": [156, 234, 679, 898], "confidence": 0.717, "gender": "male"} ], "best_angle": 0 }, "warnings": [], "face_quality": 85.0, "face_luminance": 50.0 }, "created_at": "2025-05-01T13:11:07.977806Z" }
| Status | Meaning | Action | |---|---|---| | "Approved" | User is physically present | Proceed with your flow | | "Declined" | Liveness check failed | Check warnings. May be a spoof or poor image quality |
| Code | Meaning | Action | |---|---|---| | 400 | Invalid request | Check file format, size, parameters | | 401 | Invalid API key | Verify x-api-key header | | 403 | Insufficient credits | Top up at business.didit.me |
| Field | Type | Description | |---|---|---| | status | string | "Approved" or "Declined" | | method | string | Always "PASSIVE" for this endpoint | | score | integer | 0-100 liveness confidence (higher = more likely real). null if no face | | face_quality | float | 0-100 face image quality score. null if no face | | face_luminance | float | Face luminance value. null if no face | | entities[].age | float | Estimated age | | entities[].bbox | array | Face bounding box [x1, y1, x2, y2] | | entities[].confidence | float | Face detection confidence (0-1) | | entities[].gender | string | "male" or "female" | | warnings | array | {risk, log_type, short_description, long_description} |
| Tag | Description | |---|---| | NO_FACE_DETECTED | No face detected in image | | LIVENESS_FACE_ATTACK | Potential spoofing attempt (printed photo, screen, mask) | | FACE_IN_BLOCKLIST | Face matches a blocklisted entry | | POSSIBLE_FACE_IN_BLOCKLIST | Possible blocklist match detected |
| Tag | Description | Notes | |---|---|---| | LOW_LIVENESS_SCORE | Score below threshold | Configurable review + decline thresholds | | DUPLICATED_FACE | Matches another approved session | — | | POSSIBLE_DUPLICATED_FACE | May match another user | Configurable similarity threshold | | MULTIPLE_FACES_DETECTED | Multiple faces (largest used for scoring) | Passive only | | LOW_FACE_QUALITY | Image quality below threshold | Passive only | | LOW_FACE_LUMINANCE | Image too dark | Passive only | | HIGH_FACE_LUMINANCE | Image too bright/overexposed | Passive only |
1. Capture user selfie
2. POST /v3/passive-liveness/ → {"user_image": selfie}
3. If "Approved" → user is real, proceed
If "Declined" → check warnings:
- NO_FACE_DETECTED → ask user to retake with face clearly visible
- LOW_FACE_QUALITY → ask for better lighting/positioning
- LIVENESS_FACE_ATTACK → flag as potential fraud1. POST /v3/passive-liveness/ → verify user is real
2. If Approved → POST /v3/face-match/ → compare selfie to ID photo
3. Both Approved → identity verifiedbashexport DIDIT_API_KEY="your_api_key" python scripts/check_liveness.py selfie.jpg python scripts/check_liveness.py selfie.jpg --threshold 80
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 8,343 | 5,709 | -32% | 1 | 1 | 0% | 1,914 | 3,364 | +76% | 0 | 0 | — |
case-02 | fail→pass | 8,819 | 5,500 | -38% | 1 | 1 | 0% | 2,093 | 3,407 | +63% | 0 | 0 | — |
case-03 | fail→pass | 6,234 | 3,290 | -47% | 1 | 1 | 0% | 1,335 | 2,995 | +124% | 0 | 0 | — |
case-04 | fail→pass | 6,335 | 4,056 | -36% | 1 | 1 | 0% | 1,174 | 2,753 | +134% | 0 | 0 | — |
case-05 | fail→pass | 8,058 | 2,638 | -67% | 1 | 1 | 0% | 1,513 | 2,760 | +82% | 0 | 0 | — |
case-06 | fail→pass | 8,082 | 3,739 | -54% | 1 | 1 | 0% | 1,610 | 3,032 | +88% | 0 | 0 | — |
case-07 | fail→pass | 8,917 | 3,456 | -61% | 1 | 1 | 0% | 1,806 | 2,786 | +54% | 0 | 0 | — |
case-08 | fail→pass | 7,573 | 2,639 | -65% | 1 | 1 | 0% | 1,514 | 2,710 | +79% | 0 | 0 | — |
case-09 | fail→pass | 6,956 | 2,480 | -64% | 1 | 1 | 0% | 1,292 | 2,649 | +105% | 0 | 0 | — |
case-10 | fail→pass | 8,425 | 2,277 | -73% | 1 | 1 | 0% | 1,722 | 2,571 | +49% | 0 | 0 | — |
case-11 | pass→pass | 6,404 | 1,698 | -73% | 1 | 1 | 0% | 1,326 | 2,432 | +83% | 0 | 0 | — |
case-12 | fail→pass | 4,825 | 1,828 | -62% | 1 | 1 | 0% | 1,043 | 2,455 | +135% | 0 | 0 | — |
case-13 | pass→pass | 10,797 | 2,444 | -77% | 1 | 1 | 0% | 2,379 | 2,622 | +10% | 0 | 0 | — |
case-14 | fail→pass | 7,161 | 3,251 | -55% | 1 | 1 | 0% | 1,323 | 2,781 | +110% | 0 | 0 | — |
case-15 | fail→pass | 9,876 | 3,989 | -60% | 1 | 1 | 0% | 1,942 | 2,943 | +52% | 0 | 0 | — |
case-16 | pass→pass | 5,461 | 2,596 | -52% | 1 | 1 | 0% | 1,173 | 2,536 | +116% | 0 | 0 | — |
case-17 | pass→pass | 3,129 | 1,613 | -48% | 1 | 1 | 0% | 663 | 2,356 | +255% | 0 | 0 | — |
case-18 | pass→pass | 6,781 | 2,328 | -66% | 1 | 1 | 0% | 1,263 | 2,617 | +107% | 0 | 0 | — |
case-19 | fail→pass | 5,377 | 1,913 | -64% | 1 | 1 | 0% | 1,038 | 2,423 | +133% | 0 | 0 | — |
case-20 | fail→pass | 4,138 | 4,316 | +4% | 1 | 1 | 0% | 942 | 3,244 | +244% | 0 | 0 | — |
case-21 | pass→pass | 12,900 | 10,055 | -22% | 1 | 1 | 0% | 2,432 | 4,112 | +69% | 0 | 0 | — |
case-22 | fail→pass | 7,262 | 2,315 | -68% | 1 | 1 | 0% | 1,381 | 2,565 | +86% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +73 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.