KYC Service API Documentation
KYC 实名认证微服务提供身份证 OCR 识别、人脸活体认证、短信验证、用户权限校验等能力。
作为独立微服务,通过 API Key 鉴权,可被 menhu 等业务系统集成调用。
http://localhost:5122所有接口均需通过
X-API-Key + X-API-Secret 请求头鉴权。
Authentication
所有 API 请求必须包含以下 HTTP 请求头:
| Header | Required | Description |
|---|---|---|
X-API-Key | Required | API Key, format: kyc_xxxx |
X-API-Secret | Required | API Secret (SHA256 hashed internally) |
Content-Type | Required | application/json |
API Key 在管理后台的「API Key」页面管理。每个 Key 拥有独立的权限范围(auth / check)和速率限制。
User Levels
KYC 系统定义了 4 个用户等级,不同等级可访问不同功能:
| Level | Description | Access | Achieved By |
|---|---|---|---|
| L1 | Default | Basic functions only | Registration |
| L2 | Verified | AI generation, Seedance | Face + ID verification |
| L3 | Advanced | Custom training | Enterprise cert (TBD) |
| L4 | Enterprise | Full access, discount | Enterprise cert (TBD) |
Capability Mapping
| Capability | Required Level |
|---|---|
seedance_video | L2 |
ai_video_generation | L2 |
ai_picture_generation | L2 |
custom_training | L3 |
custom_portrait | L4 |
Process Flow
Path A: Manual Input + Face Auth
Path B: ID Card OCR + Face Auth
Error Codes
All responses follow a unified format:
{
"success": true | false,
"data": { ... }, // success case
"error": { // error case
"code": "ERROR_CODE",
"message": "Description"
}
}
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Missing or invalid parameters |
AUTH_FAILED | 401 | Invalid API Key or Secret |
NOT_FOUND | 404 | Record not found |
ID_CARD_USED | 400 | ID card already registered |
RATE_LIMIT | 429 | Too many requests |
INTERNAL_ERROR | 500 | Internal server error |
ID Card OCR
Upload an ID card photo for OCR recognition. Returns extracted name, ID number, and other fields. Supports front and back side detection with quality assessment.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
image_base64 | String | One of two | Base64 encoded image (supports data:image/... prefix) |
image_url | String | One of two | Image URL (max 8MB) |
Response (Success)
{
"success": true,
"data": {
"name": "Zhang San",
"id_number": "110101199001011234",
"gender": "Male",
"ethnicity": "Han",
"date_of_birth": "1990.01.01",
"domicile": "Beijing ...",
"issue_authority": "", // back side only
"valid_period": "", // back side only
"has_front": true,
"has_back": false,
"quality_codes": [0],
"quality_labels": ["normal"],
"has_risk": false,
"has_validation_error": false
}
}
Quality Codes
| Code | Label | Description |
|---|---|---|
| 0 | normal | Normal quality |
| 10 | blurry | Blurry image |
| 20 | no_idcard_content | No ID card detected |
| 21 | idcard_incomplete | ID card incomplete |
| 30 | tampered_or_fake | Tampered / Fake |
| 31 | photocopy | Photocopy detected |
| 32 | screen_recapture | Screen recapture |
Identity Start
Initiate face liveness authentication. Returns an H5 URL for the user to complete face verification. The URL is valid for 60 minutes.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
real_name | String | Yes | Real name (from ID card) |
id_card_no | String | Yes | 18-digit ID card number |
redirect_url | String | Optional | Custom callback URL after face auth |
Response (Success)
{
"success": true,
"data": {
"h5_url": "https://h5-v2.kych5.com?...",
"byted_token": "2026041914...",
"config_id": "uuid-...",
"expires_in": 3600
}
}
"status": "already_verified" instead.
Identity Result
Query the result of a face authentication. Call this after the user completes face verification on the H5 page.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
byted_token | String | Yes | Token from identity/start response |
Response (Verified)
{
"success": true,
"data": {
"status": "verified",
"user_level": "L2",
"safety_identifier": "SAF_xxxx",
"device_risk_level": "low",
"face_quality_score": 99.004,
"expires_at": "2028-04-18T06:40:13.619Z"
}
}
Response (Rejected)
{
"success": true,
"data": {
"status": "rejected",
"result_code": "50001"
}
}
SMS Send
Send an SMS verification code to the specified phone number.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
phone | String | Yes | Phone number (e.g. 13800138000) |
SMS Verify
Verify an SMS code. On success, marks the user's phone as verified.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
phone | String | Yes | Phone number |
code | String | Yes | 6-digit verification code |
Check User
Check if a user has the required KYC level to access a specific capability. This is the primary integration endpoint for business systems like menhu.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
capability | String | Optional | Capability to check (see Level table) |
client_ip | String | Optional | Client IP for geo restriction check |
Response (Allowed)
{
"success": true,
"data": {
"allowed": true,
"safety_identifier": "SAF_xxxx",
"user_level": "L2",
"cost_multiplier": 1.0
}
}
Response (Denied)
{
"success": true,
"data": {
"allowed": false,
"reason": "LEVEL_REQUIRED", // or KYC_EXPIRED, IP_RESTRICTED, VIOLATION_FROZEN
"message": "Identity verification required",
"required_level": "L2",
"current_level": "L1"
}
}
Integration Example (Node.js)
// menhu backend: check before allowing AI generation async function checkKYC(userId, capability) { var resp = await fetch('http://localhost:5122/api/v1/check/user', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.KYC_API_KEY, 'X-API-Secret': process.env.KYC_API_SECRET }, body: JSON.stringify({ app_id: 'menhu', external_user_id: userId, capability: capability }) }); var data = await resp.json(); return data.data.allowed; }
Check Status
Query a user's current KYC status, level, and violation count.
Query Parameters
| Field | Type | Required | Description |
|---|---|---|---|
app_id | String | Yes | Application ID |
external_user_id | String | Yes | External user identifier |
Response
{
"success": true,
"data": {
"user_level": "L2",
"kyc_status": "verified",
"safety_identifier": "SAF_xxxx",
"phone_verified": true,
"real_name_masked": "Zhang *",
"id_card_last4": "1234",
"expires_at": "2028-04-18T06:40:13Z",
"violations_count": 0
}
}
App Config (Get)
Get your app's current callback configuration. Uses API Key authentication to identify the calling app.
Response
{
"success": true,
"data": {
"app_id": "menhu",
"app_name": "Points Aggregation Portal",
"callback_url": "http://localhost:5120/api/v1/kyc/callback",
"webhook_url": "http://localhost:5120/api/v1/kyc/webhook",
"contact_name": "admin",
"contact_email": "admin@lumi.com"
}
}
App Config (Update)
Update your app's callback URLs. Your app can self-configure these without admin access.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
callback_url | String | Optional | URL to receive kyc_verified events |
webhook_url | String | Optional | URL to receive violation_created and alert_threshold events |
contact_name | String | Optional | Contact person name |
contact_email | String | Optional | Contact email |
Integration Example
// App registers its callback URLs with KYC on startup await fetch('http://kyc-service:5122/api/v1/check/config', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-API-Key': KYC_API_KEY, 'X-API-Secret': KYC_API_SECRET }, body: JSON.stringify({ callback_url: 'https://my-app.com/api/kyc/callback', webhook_url: 'https://my-app.com/api/kyc/webhook' }) });
Webhook
KYC 服务支持通过 Webhook 主动向对接方推送事件通知。在管理后台「API Key」中配置回调地址后,相关事件发生时 KYC 会向配置的 URL 发送 POST 请求。
配置方式
在管理后台 API Key 编辑中配置两个 URL:
| Field | Description | Triggered Events |
|---|---|---|
callback_url | Callback URL | kyc_verified |
webhook_url | Webhook Notification URL | violation_created, alert_threshold |
Request Format
KYC service sends POST request to configured URL:
POST {callback_url | webhook_url}
Content-Type: application/json
{
"event": "kyc_verified",
"app_id": "menhu",
"timestamp": "2026-04-24T05:00:00.000Z",
"data": { ... }
}
Response
Return HTTP 200 with JSON body. KYC will log the response status but won't retry on failure (fire-and-forget).
{
"success": true,
"received": "kyc_verified"
}
Webhook Events
kyc_verified
Triggered when a user completes identity verification. Sent to callback_url.
{
"event": "kyc_verified",
"app_id": "menhu",
"timestamp": "2026-04-24T...",
"data": {
"external_user_id": "69db6282...",
"safety_identifier": "SAF_xxxx",
"user_level": "L2",
"expires_at": "2028-04-24T..."
}
}
violation_created
Triggered when a violation is created (manually or auto). Sent to webhook_url.
{
"event": "violation_created",
"data": {
"safety_identifier": "SAF_xxxx",
"level": "P3",
"type": "content_violation",
"action_taken": "warning",
"source": "auto_alert",
"description": "Auto-created: 3 pending alerts"
}
}
alert_threshold
Triggered when a user's content alerts reach the configured threshold. Sent to webhook_url.
{
"event": "alert_threshold",
"data": {
"safety_identifier": "SAF_xxxx",
"alert_count": 3,
"threshold": 3,
"violation_level": "P3"
}
}
Receiver Example (Node.js)
// menhu: /api/v1/kyc/callback router.post('/callback', function(req, res) { var { event, data } = req.body; if (event === 'kyc_verified') { console.log('User verified:', data.external_user_id); // Update local user record... } res.json({ success: true }); }); // menhu: /api/v1/kyc/webhook router.post('/webhook', function(req, res) { var { event, data } = req.body; if (event === 'violation_created') { console.log('Violation:', data.safety_identifier, data.level); // Freeze user, send notification... } res.json({ success: true }); });
Integration Guide
KYC 服务通过 safety_identifier 实现内容溯源。以下是三种接入场景的集成方式。
Scene A: menhu Direct (Portal Call Seedance)
menhu 自身调用 Seedance API 时,先通过 KYC 获取 safety_identifier,再传入视频生成请求。
// Step 1: Check KYC & get safety_identifier var kycResult = await kycClient.checkUser('menhu', userId, 'seedance_video'); // Step 2: Pass to Seedance API var body = { model: "doubao-seedance-2-0-260128", content: [{ type: "text", text: "..." }], safety_identifier: kycResult.safety_identifier // "SAF_xxxx" };
Scene B: Downstream Tool (via OAuth)
通过 menhu 工具市场接入的下游工具,在 OAuth 授权后调用 userinfo 即可获得 safety_identifier。
// Step 1: User OAuth → Tool gets access_token // Step 2: Get userinfo (includes safety_identifier) GET /oauth/userinfo Authorization: Bearer <access_token> // Response: { "user_id": "...", "nickname": "...", "safety_identifier": "SAF_xxxx", // from KYC "kyc_status": "verified", "user_level": "L2" } // Step 3: Pass safety_identifier to upstream AI API
Scene C: Standalone Tool (direct KYC)
Independent tool with its own KYC API Key. Directly calls KYC service.
// Tool registers with KYC admin → gets api_key + api_secret // Call check/user to verify and get safety_identifier POST /api/v1/check/user Headers: X-API-Key: <tool_api_key> X-API-Secret: <tool_api_secret> Body: { "app_id": "my_video_tool", "external_user_id": "user_12345", "capability": "ai_video_generation" } // Response: { "allowed": true, "safety_identifier": "SAF_xxxx", "user_level": "L2" }
safety_identifier Properties
| Property | Value |
|---|---|
| Format | SAF_ + 16-char hex hash |
| Length | 20 characters (within Seedance 64-char limit) |
| Uniqueness | Unique per verified user |
| Privacy | Non-reversible hash, no PII exposed |
| Validity | 2 years from verification date |
| Traceability | KYC admin can trace via /admin/trace/:safetyId |