关于 didit-liveness-detection
面向在身份验证中加入被动活体检测的开发者。它通过单张人脸图像评估用户是否为真人,无需视频或交互;也可保存结果并登记人脸,以供后续重复检测。
主要功能
- 01单张图像被动活体检测
- 02可配置的分数拒绝阈值
- 03人脸阻止名单与重复筛查
- 04可选保存会话并登记人脸
- 05响应中包含警告和判定状态
适用场景
- 01在用户注册时检测自拍
- 02识别身份验证中的呈现攻击
- 03将人脸与阻止名单及既往检测比对
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ didit-protocol/ skills --skill didit-liveness-detection安装后在 Claude Code 中输入/didit-liveness-detection确认已加载。
附带文件scripts/check_liveness.py
详细介绍
Didit 被动活体检测 API
概述
通过分析单张拍摄的图像,验证用户是否真实在场。无需视频、动作或用户交互。
主要限制:
- 允许的文件扩展名:tiff、jpg、jpeg、png、webp(不支持 PDF)
- 最大文件大小:5 MB(处理前会将图像压缩至约 0.5 MB,因此更高分辨率不会提高准确率)
- 图像中须有一张清晰可见的人脸。若有多张人脸,则对最大的一张进行评分,并添加
MULTIPLE_FACES_DETECTED - 必须是原始实时拍摄的图像(不能使用屏幕截图、打印件或屏幕照片)
定价:$0.05(每次成功调用;单独调用不享受免费额度)。此为截至 2026 年 11 月 1 日的标价,届时 Didit 将调整定价:每月 500 次免费检查将变为每月 $10 免费额度。请访问 didit.me/pricing 查看当前价格。
**准确率:**活体检测准确率为 99.9%,误接受率(FAR)<0.1%。
**判定:**当任何 error 警告触发时(未检测到人脸、分数低于或等于阈值、呈现攻击、命中黑名单),status 为 Declined;否则为 Approved。此端点不会返回 In Review。只有分数阈值可配置。
**黑名单和重复筛查:**每次调用都会执行,save_api_request=false 时也不例外。系统会将人脸与应用的人脸搜索索引进行比对(包括人脸黑名单和白名单、已批准会话中的人脸、上传至用户个人资料的人脸,以及保留的生物特征模板)。重复检查时会跳过 vendor_data 相同的较早人脸,因此重新验证同一用户不会触发 DUPLICATED_FACE。若人脸在黑名单中,即使 vendor_data 相同,也仍会被检测到。若与人脸白名单匹配,则会抑制重复警告。
保存和录入(save_api_request,默认为 true):调用会保存为 API 会话(Business Console、GET /v3/session/{request_id}/decision/、status.updated webhook),并存储图像和人脸裁剪图;同时会将人脸录入人脸搜索索引,以便后续 Face Search 调用和重复检查能够匹配。设为 false 时不会存储任何内容,不会录入人脸,也无法查询 request_id。
**沙盒:**沙盒 API 密钥在请求验证后会返回固定的 Approved 负载;不会保存任何内容,也不会计费。
**活体检测方法:**此端点始终使用 PASSIVE(单帧 CNN)。托管工作流还可通过 face_liveness_method 工作流设置,使用 ACTIVE_3D(3D 动作与闪光,最高安全性)和 FLASHING(3D 闪光,高安全性)。
**API 参考:**https://docs.didit.me/standalone-apis/passive-liveness **功能指南:**https://docs.didit.me/core-technology/liveness/overview
身份验证
每个请求都需要 x-api-key 标头。请使用 Didit Business Console 中 Developers > API keys 下的应用 API 密钥,或通过编程方式创建账户和密钥(参见入门指南)。具有自定义访问权限的命名密钥需要具备 Sessions 写入权限,才能运行独立检查;仅限部分工作流或仅限已批准会话的密钥无法运行此类检查。
入门指南(还没有账户?)
如果你还没有 Didit API 密钥,只需调用 2 次 API 即可创建:
- **注册:**使用
{"email": "[email protected]", "password": "MyStr0ng!Pass"}调用POST https://apx.didit.me/auth/v2/programmatic/register/ - 查看电子邮件,获取 6 位验证码
- **验证:**使用
{"email": "[email protected]", "code": "A3K9F2"}调用POST https://apx.didit.me/auth/v2/programmatic/verify-email/。密钥位于响应中的application.api_key。
**添加额度:**调用 GET /v3/billing/balance/ 查看余额,调用 POST /v3/billing/top-up/ 并传入 {"amount_in_dollars": 50} 获取 Stripe 结账链接。
如需全面的平台管理功能(工作流、会话、用户、账单),请参阅 didit-verification-management 技能。
端点
POST https://verification.didit.me/v3/passive-liveness/
请求头
| 请求头 | 值 | 必填 |
|---|---|---|
x-api-key | 你的 API 密钥 | 是 |
Content-Type | multipart/form-data(由你的 HTTP 客户端设置) | 是 |
请求参数(multipart/form-data)
| 参数 | 类型 | 必填 | 默认值 | 约束 | 描述 |
|---|---|---|---|---|---|
user_image | 文件 | 是 | - | tiff/jpg/jpeg/png/webp,最大 5 MB | 正面人脸图像(自拍照) |
face_liveness_score_decline_threshold | 浮点数 | 否 | 30 | 0-100 | 分数小于或等于此值时,会触发 LOW_LIVENESS_SCORE 并拒绝。提高此值可采取更严格的防伪措施 |
rotate_image | 布尔值 | 否 | false | - | 尝试旋转 90 度,并使用人脸检测效果最佳的方向。这会增加延迟 |
save_api_request | 布尔值 | 否 | true | - | 保存为 API 会话并录入人脸(参见概述) |
vendor_data | 字符串 | 否 | - | - | 你的用户标识符。该值会被存储并回传,也用于在人脸重复检查时跳过同一用户之前的人脸 |
metadata | JSON 对象 | 否 | - | 在 multipart 中以 JSON 编码字符串传递 | 与会话一同存储(保存时)并回传 |
示例
import json
import requests
with open("selfie.jpg", "rb") as f:
response = requests.post(
"https://verification.didit.me/v3/passive-liveness/",
headers={"x-api-key": "YOUR_API_KEY"},
files={"user_image": ("selfie.jpg", f, "image/jpeg")},
data={
"face_liveness_score_decline_threshold": "50",
"vendor_data": "user-123",
"metadata": json.dumps({"flow": "withdrawal"}),
},
timeout=60,
)
response.raise_for_status()
liveness = response.json()["liveness"]
print(liveness["status"], liveness["score"], [w["risk"] for w in liveness["warnings"]])
const formData = new FormData();
formData.append("user_image", selfieFile);
formData.append("face_liveness_score_decline_threshold", "50");
formData.append("vendor_data", "user-123");
formData.append("metadata", JSON.stringify({ flow: "withdrawal" }));
const response = await fetch("https://verification.didit.me/v3/passive-liveness/", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY" },
body: formData,
});
const { liveness } = await response.json();
响应(200 OK)
{
"request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"liveness": {
"status": "Approved",
"method": "PASSIVE",
"score": 95.0,
"user_image": {
"entities": [
{"bbox": [661, 728, 1688, 2188], "confidence": 0.732973, "age": 26.91, "gender": "male"}
],
"best_angle": 0
},
"warnings": [],
"face_quality": 84.21,
"face_luminance": 50.33
},
"vendor_data": "user-123",
"metadata": {"flow": "withdrawal"},
"created_at": "2026-06-12T01:04:42.763237+00:00"
}
即使人脸为伪造或在阻止名单中,仍会返回 HTTP 200:务必检查 liveness.status 和 liveness.warnings。
状态值与处理方式
| 状态 | 含义 | 操作 |
|---|---|---|
"Approved" | 未触发任何 error 警告 | 继续处理。仍需检查 warnings 中是否有 DUPLICATED_FACE / POSSIBLE_DUPLICATED_FACE / MULTIPLE_FACES_DETECTED |
"Declined" | 至少触发了一条 error 警告 | 查看 warnings[].risk:采集问题应重新拍摄;攻击和阻止名单命中则应拦截或审核 |
错误响应
| 代码 | 含义 | 操作 |
|---|---|---|
400 | 请求无效 | 查看响应正文中的字段错误 |
400 | {"error": "Invalid user image format."}:文件扩展名虽允许,但无法解码文件 | 将图像重新编码为 JPEG 或 PNG,然后重试 |
401 | API 密钥已过期 | 轮换密钥,或在 Developers > API keys 中创建新密钥 |
403 | API 密钥缺失、错误或已撤销;受限密钥无权访问此端点;请求来自密钥 IP 允许列表之外;或额度不足 | 查看 detail 或 error 中的消息:修正 x-api-key 请求头或密钥权限,或充值 |
429 | 速率限制:每个密钥每分钟允许 300 次 POST/PATCH/DELETE 请求和 600 次 GET 请求 | 等待 Retry-After 指定的秒数,然后重试 |
响应字段参考
| 字段 | 类型 | 描述 |
|---|---|---|
request_id | uuid | 当 save_api_request=true 时为会话 ID(与 GET /v3/session/{request_id}/decision/ 配合使用);否则为一次性关联 ID |
liveness.status | 字符串 | "Approved" 或 "Declined" |
liveness.method | 字符串 | 始终为 "PASSIVE" |
liveness.score | 浮点数或 null | 0-100(保留两位小数),分数越高,越可能是真人。当模型未返回置信度时为 null;与阈值比较时,null 分数按 0 计算 |
liveness.user_image.entities[] | 数组 | 每张检测到的人脸对应一条记录:bbox [x_min, y_min, x_max, y_max]、confidence(0-1)、age、gender。年龄和性别仅供参考,不影响判定 |
liveness.user_image.best_angle | 整数或 null | 检测效果最佳时的旋转角度(度);仅当 rotate_image=true 且旋转进行了校正时才为非零值 |
liveness.warnings[] | 数组 | {risk, feature, additional_data, log_type, short_description, long_description}。feature 始终为 LIVENESS |
liveness.face_quality | 浮点数或 null | 人脸的采集质量,范围为 0-100。无可靠值时为 null |
liveness.face_luminance | 浮点数或 null | 人脸亮度,范围为 0-100。不可用时为 null |
vendor_data | 字符串或 null | 回传你发送的内容 |
metadata | 对象或 null | 回传你发送的内容 |
created_at | 字符串 | 响应时间,采用 ISO 8601 UTC 格式 |
警告标签
| 标签 | 严重性 | 是否拒绝 | 触发条件 |
|---|---|---|---|
NO_FACE_DETECTED | error | 是 | 活体检测模型未检测到人脸 |
LOW_LIVENESS_SCORE | error | 是 | score 小于或等于 face_liveness_score_decline_threshold。不会与 NO_FACE_DETECTED 同时触发 |
LIVENESS_FACE_ATTACK | error | 是 | 检测到呈现攻击(打印照片、屏幕回放、面具、深度伪造) |
FACE_IN_BLOCKLIST | error | 是 | 与人脸屏蔽名单中的某张人脸高度匹配。additional_data 包含 blocklisted_session_id、blocklisted_session_number、api_service |
POSSIBLE_FACE_IN_BLOCKLIST | error | 是 | 与屏蔽名单中的某张人脸较弱匹配。此端点会拒绝该请求(工作流会将其送审) |
MULTIPLE_FACES_DETECTED | warning | 否 | 检测到多张人脸;系统对最大的人脸进行评分 |
DUPLICATED_FACE | information | 否 | 与另一已通过审核的会话中的人脸(或导入的人脸、保留的模板)高度匹配。additional_data 包含 duplicated_session_id、duplicated_session_number、api_service |
POSSIBLE_DUPLICATED_FACE | information | 否 | 同类人脸较弱匹配 |
- 此端点不会触发
LOW_FACE_QUALITY、LOW_FACE_LUMINANCE和HIGH_FACE_LUMINANCE(这些是工作流警告)。如需判断,请自行设置face_quality/face_luminance规则。 - 请根据
status和warnings[].risk做出判断。以上严重性是此端点的规则;将每条警告中回显的log_type视为信息即可。
常见工作流
基本人脸活体检测
1. Capture the user's selfie
2. POST /v3/passive-liveness/ with user_image and vendor_data=<your user id>
3. "Approved" -> the user is live, proceed
"Declined" -> check warnings[].risk:
NO_FACE_DETECTED / LOW_LIVENESS_SCORE -> ask for a retake: one well-lit, centered face
LIVENESS_FACE_ATTACK -> suspected spoof, do not approve
FACE_IN_BLOCKLIST / POSSIBLE_FACE_IN_BLOCKLIST -> block or send to manual review
4. "Approved" with DUPLICATED_FACE -> this face already passed under another vendor_data:
look up additional_data.duplicated_session_id before creating a new account
活体检测 + 人脸匹配(组合)
1. POST /v3/passive-liveness/ -> verify the selfie is live
2. If Approved -> POST /v3/face-match/ with user_image=selfie, ref_image=ID portrait
3. Both Approved -> the live person matches the document photo
需要按相似度列出所有人脸,而不是获取屏蔽名单/重复人脸判定结果?请使用 didit-face-search 技能,并在此处保存同一张自拍照之前运行它(已保存的活体检测人脸本身也可搜索)。
实用脚本
export DIDIT_API_KEY="your_api_key"
python scripts/check_liveness.py selfie.jpg
python scripts/check_liveness.py selfie.jpg --threshold 50 --vendor-data user-123
python scripts/check_liveness.py selfie.jpg --rotate --no-save --metadata '{"flow": "withdrawal"}'