curl -X POST 'https://verification.didit.me/v3/workflows/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"workflow_label": "Standard KYC",
"features": [
{"feature": "OCR"},
{"feature": "LIVENESS", "config": {"face_liveness_method": "passive"}},
{"feature": "FACE_MATCH", "config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}}
]
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.workflows.create(
workflow_label="Standard KYC",
features=[{
"feature": "OCR",
"config": {"duplicated_user_action": "review"},
}, {
"feature": "LIVENESS",
"config": {"face_liveness_method": "passive"},
}, {
"feature": "FACE_MATCH",
"config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60},
}],
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.workflows.create({
workflow_label: "Standard KYC",
features: [{
feature: "OCR",
config: { duplicated_user_action: "review" },
}, {
feature: "LIVENESS",
config: { face_liveness_method: "passive" },
}, {
feature: "FACE_MATCH",
config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
}],
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Workflows.Create(ctx, didit.SimpleWorkflowRequest{
WorkflowLabel: didit.String("Standard KYC"),
Features: []didit.SimpleWorkflowFeature{{
Feature: "OCR",
Config: didit.Some(map[string]any{"duplicated_user_action": "review"}),
}, {
Feature: "LIVENESS",
Config: didit.Some(map[string]any{"face_liveness_method": "passive"}),
}, {
Feature: "FACE_MATCH",
Config: didit.Some(map[string]any{"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}),
}},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.util.List;
import java.util.Map;
import me.didit.Didit;
import me.didit.types.SimpleWorkflowFeature;
import me.didit.types.SimpleWorkflowRequest;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.workflows().create(SimpleWorkflowRequest.builder()
.workflowLabel("Standard KYC")
.features(List.of(
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("OCR"))
.config(Map.of("duplicated_user_action", "review"))
.build(),
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("LIVENESS"))
.config(Map.of("face_liveness_method", "passive"))
.build(),
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("FACE_MATCH"))
.config(Map.of("face_match_score_decline_threshold", 40, "face_match_score_review_threshold", 60))
.build()))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->workflows->create([
'workflow_label' => 'Standard KYC',
'features' => [[
'feature' => 'OCR',
'config' => ['duplicated_user_action' => 'review'],
], [
'feature' => 'LIVENESS',
'config' => ['face_liveness_method' => 'passive'],
], [
'feature' => 'FACE_MATCH',
'config' => ['face_match_score_decline_threshold' => 40, 'face_match_score_review_threshold' => 60],
]],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.workflows.create(
workflow_label: "Standard KYC",
features: [{
feature: "OCR",
config: { duplicated_user_action: "review" },
}, {
feature: "LIVENESS",
config: { face_liveness_method: "passive" },
}, {
feature: "FACE_MATCH",
config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
}]
)
puts result.to_husing System;
using System.Collections.Generic;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Workflows.CreateAsync(new SimpleWorkflowRequest
{
WorkflowLabel = "Standard KYC",
Features = new SimpleWorkflowFeature[]
{
new()
{
Feature = "OCR",
Config = new Dictionary<string, object> { ["duplicated_user_action"] = "review" },
},
new()
{
Feature = "LIVENESS",
Config = new Dictionary<string, object> { ["face_liveness_method"] = "passive" },
},
new()
{
Feature = "FACE_MATCH",
Config = new Dictionary<string, object> { ["face_match_score_decline_threshold"] = 40, ["face_match_score_review_threshold"] = 60 },
},
},
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"uuid": "c3d4e5f6-7890-12cd-ef34-333333333333",
"workflow_id": "c3d4e5f6-7890-12cd-ef34-333333333333",
"workflow_label": "Standard KYC",
"workflow_type": null,
"is_default": false,
"is_archived": false,
"total_price": 0.15,
"min_price": 0.15,
"max_price": 0.15,
"features": "OCR + LIVENESS + FACE_MATCH",
"is_simple_workflow": false,
"is_editable": false,
"workflow_url": "https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ",
"workflow_graph": {
"start_node": "ocr",
"nodes": "... one node per feature plus a final status node ..."
},
"max_retry_attempts": 3,
"retry_window_days": 7,
"session_expiration_time": 604800,
"version": 1,
"status": "published",
"published_at": "2026-05-17T10:30:00Z",
"created_at": "2026-05-17T10:30:00Z",
"updated_at": "2026-05-17T10:30:00Z"
}创建工作流 API:搭建 KYC 流程
通过 API 以编程方式创建 Didit 验证工作流,按顺序编排 KYC、KYB、AML 与生物识别功能,一个流程即可完成。按次计费,无需签订合同。
curl -X POST 'https://verification.didit.me/v3/workflows/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"workflow_label": "Standard KYC",
"features": [
{"feature": "OCR"},
{"feature": "LIVENESS", "config": {"face_liveness_method": "passive"}},
{"feature": "FACE_MATCH", "config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}}
]
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.workflows.create(
workflow_label="Standard KYC",
features=[{
"feature": "OCR",
"config": {"duplicated_user_action": "review"},
}, {
"feature": "LIVENESS",
"config": {"face_liveness_method": "passive"},
}, {
"feature": "FACE_MATCH",
"config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60},
}],
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.workflows.create({
workflow_label: "Standard KYC",
features: [{
feature: "OCR",
config: { duplicated_user_action: "review" },
}, {
feature: "LIVENESS",
config: { face_liveness_method: "passive" },
}, {
feature: "FACE_MATCH",
config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
}],
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Workflows.Create(ctx, didit.SimpleWorkflowRequest{
WorkflowLabel: didit.String("Standard KYC"),
Features: []didit.SimpleWorkflowFeature{{
Feature: "OCR",
Config: didit.Some(map[string]any{"duplicated_user_action": "review"}),
}, {
Feature: "LIVENESS",
Config: didit.Some(map[string]any{"face_liveness_method": "passive"}),
}, {
Feature: "FACE_MATCH",
Config: didit.Some(map[string]any{"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}),
}},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.util.List;
import java.util.Map;
import me.didit.Didit;
import me.didit.types.SimpleWorkflowFeature;
import me.didit.types.SimpleWorkflowRequest;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.workflows().create(SimpleWorkflowRequest.builder()
.workflowLabel("Standard KYC")
.features(List.of(
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("OCR"))
.config(Map.of("duplicated_user_action", "review"))
.build(),
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("LIVENESS"))
.config(Map.of("face_liveness_method", "passive"))
.build(),
SimpleWorkflowFeature.builder()
.feature(SimpleWorkflowFeature.Feature.of("FACE_MATCH"))
.config(Map.of("face_match_score_decline_threshold", 40, "face_match_score_review_threshold", 60))
.build()))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->workflows->create([
'workflow_label' => 'Standard KYC',
'features' => [[
'feature' => 'OCR',
'config' => ['duplicated_user_action' => 'review'],
], [
'feature' => 'LIVENESS',
'config' => ['face_liveness_method' => 'passive'],
], [
'feature' => 'FACE_MATCH',
'config' => ['face_match_score_decline_threshold' => 40, 'face_match_score_review_threshold' => 60],
]],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.workflows.create(
workflow_label: "Standard KYC",
features: [{
feature: "OCR",
config: { duplicated_user_action: "review" },
}, {
feature: "LIVENESS",
config: { face_liveness_method: "passive" },
}, {
feature: "FACE_MATCH",
config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
}]
)
puts result.to_husing System;
using System.Collections.Generic;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Workflows.CreateAsync(new SimpleWorkflowRequest
{
WorkflowLabel = "Standard KYC",
Features = new SimpleWorkflowFeature[]
{
new()
{
Feature = "OCR",
Config = new Dictionary<string, object> { ["duplicated_user_action"] = "review" },
},
new()
{
Feature = "LIVENESS",
Config = new Dictionary<string, object> { ["face_liveness_method"] = "passive" },
},
new()
{
Feature = "FACE_MATCH",
Config = new Dictionary<string, object> { ["face_match_score_decline_threshold"] = 40, ["face_match_score_review_threshold"] = 60 },
},
},
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"uuid": "c3d4e5f6-7890-12cd-ef34-333333333333",
"workflow_id": "c3d4e5f6-7890-12cd-ef34-333333333333",
"workflow_label": "Standard KYC",
"workflow_type": null,
"is_default": false,
"is_archived": false,
"total_price": 0.15,
"min_price": 0.15,
"max_price": 0.15,
"features": "OCR + LIVENESS + FACE_MATCH",
"is_simple_workflow": false,
"is_editable": false,
"workflow_url": "https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ",
"workflow_graph": {
"start_node": "ocr",
"nodes": "... one node per feature plus a final status node ..."
},
"max_retry_attempts": 3,
"retry_window_days": 7,
"session_expiration_time": 604800,
"version": 1,
"status": "published",
"published_at": "2026-05-17T10:30:00Z",
"created_at": "2026-05-17T10:30:00Z",
"updated_at": "2026-05-17T10:30:00Z"
}features 数组。Didit 会将其转换为基于节点的工作流,并自动添加最终状态步骤。
此 POST /v3/workflows/ endpoint 仅创建简单的线性工作流,即扁平的 features 列表。如需分支、条件路由、Document AI 以及拒绝/决策节点,请使用下文介绍的节点图格式(它为控制台中的可视化构建器和 Didit MCP 工作流工具提供支持)。
请参阅工作流功能配置,了解每个功能接受的所有 config 字段。
基础 KYC 工作流
{
"workflow_label": "Standard KYC",
"features": [
{
"feature": "OCR",
"config": {
"documents_allowed": {
"USA": {
"DL": {
"enabled": 1,
"sides": 2
}
}
},
"duplicated_user_action": "REVIEW"
}
},
{
"feature": "LIVENESS",
"config": {
"face_liveness_method": "PASSIVE"
}
},
{
"feature": "FACE_MATCH"
}
]
}
问卷工作流
首先使用POST /v3/questionnaires/ 创建问卷。然后将返回的 questionnaire_id 用作 questionnaire_uuid。
{
"workflow_label": "Source of Funds",
"features": [
{
"feature": "QUESTIONNAIRE",
"config": {
"questionnaire_uuid": "11111111-2222-3333-4444-555555555555",
"review_questionnaire_manually": true
}
}
]
}
OCR 放在 FACE_MATCH、NFC、DATABASE_VALIDATION 或依赖证件数据的用户 AML 检查之前。
功能配置参考
每个features[] 项的结构如下:
{
"feature": "OCR",
"label": "ID Verification",
"config": {}
}
config 对象取决于功能。请在工作流功能配置中查看完整字段列表、默认值、必填设置、取值范围和示例。
对于 OCR,省略 config.documents_allowed、发送 null 或发送 {},即可允许所有受支持的证件类型。仅当您希望将工作流限制为特定国家/地区或证件类型时,才需要包含此字段。对于 QUESTIONNAIRE,config.questionnaire_uuid 为必填项。
可选工作流设置
创建工作流时,您可以包含以下任一顶级字段来控制会话行为。这些字段均为可选,省略时将使用工作流默认值。| 字段 | 类型 | 说明 |
|---|---|---|
is_white_label_enabled | 布尔值 | 为使用此工作流创建的会话启用白标定制。默认值为 false。 |
is_desktop_allowed | 布尔值 | 允许验证流程在桌面浏览器中运行。默认值为 false。 |
max_retry_attempts | 整数(0–10) | 验证被拒绝后允许的最大重试次数。0 表示用户首次被拒绝后即被阻止。默认值为 3。 |
retry_window_days | 整数(1–365)或 null | 用于统计重试次数的滚动时间窗口(以天为单位)(null 表示按所有时间统计重试次数(即全时段限制))。默认值为 7。 |
session_expiration_time | 整数(3600–2419200) | 未完成的会话过期前的秒数。最短 1 小时,最长 4 周。默认值为 604800(7 天)。 |
response_attributes | 对象或 null | 限制每个功能返回的字段,例如 {"id_verifications": ["full_name", "document_number"], "face_matches": null}。null 或缺少该键时返回所有字段。空列表仅返回 status、warnings、node_id、is_nfc_skipped 和 skip_reason,其他字段均返回 null。 |
{
"workflow_label": "Standard KYC",
"is_white_label_enabled": true,
"is_desktop_allowed": true,
"max_retry_attempts": 5,
"retry_window_days": 14,
"session_expiration_time": 86400,
"features": [
{ "feature": "OCR" },
{ "feature": "LIVENESS" },
{ "feature": "FACE_MATCH" }
]
}
分支与基于节点的工作流(图)
线性的features 列表无法表达“如果 X 则 Y”。如需条件分支(根据状态拒绝、基于提取字段进行路由、基于比对结果控制某一步骤)、Document AI 请求步骤或终止性的 拒绝/决策节点,Didit 工作流使用节点图,即控制台可视化构建器生成的结构。
图的结构
图的形式为{ start_node, nodes }。nodes 是从节点 ID 到节点的映射。边是隐式的:每个节点通过 next(无条件)和/或 branches[].goto(有条件)指向后续节点。
| 节点类型 | 结构 |
|---|---|
feature | { "node_type": "feature", "feature": "OCR", "config": {…}, "next": "<id>", "branches": [] } |
branch | { "node_type": "branch", "branches": [ { "id", "logic": "and"|"or", "rules": [<rule>], "goto": "<id>" } ], "next": "<fallback id>" } |
status(终止) | { "node_type": "status", "session_status": "Approved"|"Declined"|"In Review"|"Determine" } |
action | { "node_type": "action", "actions": [ { "type": "add_tag"|"add_review"|… } ], "next": "<id>" } |
webhook | { "node_type": "webhook", "config": { "url", "method" }, "branches": [], "next": "<id>" } |
start_node 必须是功能节点。分支按顺序求值,先匹配者胜出;空的 rules: [] 是兜底的“否则”分支(需放在最后)。session_status: "Determine" 会根据各功能的综合结果自动做出决定。
rules: [] 的分支,用于承接与所有条件都不匹配的会话。不要依赖分支节点的 next 作为兜底:单独的 next 在构建器中会呈现为含糊不清的边,而且很容易被遗漏。如果您发送的分支节点带有 next 却没有否则分支,API 会为您将该 next 转换为明确的否则分支(例如 { …, "branches": [<conditions>], "next": "liveness" } 会被保存为 branches: [<conditions>, { "id": "else", "rules": [], "goto": "liveness" }])。请自行构建否则分支以保持明确;该转换只是安全兜底,不能替代显式定义。分支规则与运算符
每条规则使用运算符将字段与值进行比较:{ "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 }
- 字段可以是某个功能的结果(
kyc.status、aml.status、document_ai.status、face_match.score等),也可以是提取的值(kyc.full_name、kyc.age、kyc.nationality、kyc.extra_fields.profession、Document AI 字段document_ai.<key>、expected_details.*)。分支只能读取在所有通往该分支的路径上都已运行过的 feature 的字段。 - 运算符:
equals、not_equals、greater_than(_or_equals)、less_than(_or_equals)、contains、not_contains、in、not_in、is_empty、is_not_empty、regex,以及fuzzy_match(仅适用于字符串字段;需要一个 0–100 的score,即相似度阈值,例如职业与 “Software Engineer” 的相似度 ≥ 80%)。
根据 NFC 未执行的原因进行分支
在NFC 节点之后,可以使用 nfc.skip_reason 配合 equals 或 in,根据芯片读取未发生的原因进行路由。该字段有以下七个稳定值:
USER_SKIPPEDDOCUMENT_WITHOUT_CHIPCHIP_CERTIFICATE_UNAVAILABLEDEVICE_WITHOUT_NFCINTEGRATION_WITHOUT_NFC_ACCESSMRZ_KEY_UNAVAILABLEJPKI_UNAVAILABLE
nfc.skip_reason 为 null,因此针对跳过原因的 equals 或 in 规则都不会匹配。
该字段也适用于因门控而跳过的 NFC 节点。这包括不存在 NFC 报告或警告的情况,以及 allow_nfc_skip 为 false 但证件或设备门控阻止 NFC 运行的情况。因此,您无需依赖 nfc_verifications[] 条目即可对这些会话进行路由。
has_device_nfc_support: false 可能存在。因此,较早的 Web 会话可能会解析为 DEVICE_WITHOUT_NFC,而不是 INTEGRATION_WITHOUT_NFC_ACCESS。equals,对门控或能力跳过使用 in,并为成功的 NFC 设置明确的否则分支。分支位于 NFC 节点之后,且每个目标都指向一个终止节点:
{
"start_node": "ocr",
"nodes": {
"ocr": {
"node_type": "feature",
"feature": "OCR",
"next": "nfc"
},
"nfc": {
"node_type": "feature",
"feature": "NFC",
"config": { "allow_nfc_skip": false },
"next": "route_nfc"
},
"route_nfc": {
"node_type": "branch",
"branches": [
{
"id": "user_skipped",
"logic": "and",
"rules": [
{ "field": "nfc.skip_reason", "operator": "equals", "value": "USER_SKIPPED" }
],
"goto": "manual_review"
},
{
"id": "nfc_unavailable",
"logic": "and",
"rules": [
{
"field": "nfc.skip_reason",
"operator": "in",
"value": [
"DOCUMENT_WITHOUT_CHIP",
"CHIP_CERTIFICATE_UNAVAILABLE",
"DEVICE_WITHOUT_NFC",
"INTEGRATION_WITHOUT_NFC_ACCESS",
"MRZ_KEY_UNAVAILABLE",
"JPKI_UNAVAILABLE"
]
}
],
"goto": "manual_review"
},
{
"id": "else",
"logic": "and",
"rules": [],
"goto": "approved"
}
]
},
"manual_review": { "node_type": "status", "session_status": "In Review" },
"approved": { "node_type": "status", "session_status": "Approved" }
}
}
in。对于成功的 NFC(nfc.skip_reason: null)以及您的图尚未识别的任何未来值,请保留明确的否则分支。
Document AI(例如资金证明)
DOCUMENT_AI 功能节点会请求一份或多份证件,并提取具名字段:
{
"node_type": "feature",
"feature": "DOCUMENT_AI",
"label": "Proof of Funds",
"config": {
"document_ai_documents": [
{
"document_key": "proof_of_funds",
"title": "Proof of Funds",
"description": "Upload a recent bank statement.",
"fields": [
{
"key": "account_holder",
"name": "Account holder",
"instruction": "The full name of the account holder as printed on the statement.",
"type": "text",
"required": true,
"is_full_name": true
},
{
"key": "closing_balance",
"name": "Closing balance",
"instruction": "The closing balance amount.",
"type": "number",
"required": true
}
]
}
],
"document_ai_missing_required_fields_action": "REVIEW",
"document_ai_name_mismatch_action": "REVIEW"
},
"next": "final_decision"
}
document_ai.<key>,该步骤的结果为 document_ai.status。
在保存证件持有人姓名的字段上设置 "is_full_name": true。提取的值会与已验证身份的姓名进行比较(即 OCR/身份证件验证中的姓名,或会话的预期信息);当结果低于 document_ai_name_match_score_threshold 时,由 document_ai_name_mismatch_action 决定后续处理。该标志为可选项,一份证件可以不标记任何字段,但每份证件最多只能有一个字段设置该标志。Document AI 的全部配置字段请参阅 工作流功能配置 → Document AI。
endpoint 与草稿生命周期
图的编辑按应用进行,使用控制台/管理 endpoint(用户 Bearer 令牌 +read:workflows/write:workflows)。图只能在草稿版本上编辑,因此生命周期为创建草稿 → 设置图 → 发布:
| 方法 | 路径 | 用途 |
|---|---|---|
GET | …/verification-settings/{settings_uuid}/workflow-graph/ | 当前图 + status/is_editable |
POST | …/application/{app}/workflow-graph/validate/ (body { "graph": … }) | 试运行校验 |
GET | …/application/{app}/workflow-graph/field-definitions/ | 可用于分支的字段及有效运算符 |
POST | …/verification-settings/{settings_uuid}/create-draft/ | 基于已发布版本创建可编辑草稿 |
PUT | …/verification-settings/{settings_uuid}/workflow-graph/ (body { "graph": … }) | 保存工作流图 |
PATCH | …/verification-settings/{settings_uuid}/ (body { "status": "published" }) | 发布草稿 |
校验警告
校验端点返回{ "is_valid": true, "graph": …, "warnings": [] }。警告仅作提示:该图是有效的,可以保存,也可以在存在警告的情况下发布。每条警告都带有对应的分支节点和相关分支臂,便于构建者精确定位到相关条件:
{
"type": "branch_outcome_contradiction",
"node_id": "after_ocr",
"branch_id": "b1",
"severity": "warning",
"message": "Branch 1 only matches sessions where ID Verification Status is Approved or In Review, but every path from it ends in the session outcome \"Declined\". Check the condition or the outcome.",
"params": { "branchNumber": 1, "field": "ID Verification Status", "allowed": "Approved or In Review", "outcome": "Declined" }
}
type | 含义 |
|---|---|
branch_outcome_contradiction | 该分支臂的条件与从该分支臂出发的每条路径最终到达的结果相互矛盾,例如将 kyc.status NOT EQUALS Declined 路由到 Declined 结果。该结果可能位于路径中更靠后的若干步骤之后。 |
branch_condition_unreachable | 该分支永远无法匹配,因为到达此分支的每条路径都已将该状态字段限制为条件所排除的值。 |
else_branch_unreachable | else 分支永远不会执行,因为其他分支已覆盖该字段在此处可能取到的全部值。 |
400,同时返回 errors 以及尽力生成的 warnings 列表。只有 errors 会阻止保存。
示例:将软件工程师导向资金证明
身份证件验证完成后:如果结果为已拒绝,则拒绝该会话;如果持有人的职业 ≈ “Software Engineer”(相似度 ≥80%),则请求一份资金证明文件;然后做出最终决策。
{
"start_node": "ocr_1",
"nodes": {
"ocr_1": { "node_type": "feature", "feature": "OCR", "label": "ID Verification", "next": "after_ocr" },
"after_ocr": {
"node_type": "branch",
"label": "Route after ID verification",
"branches": [
{ "id": "declined", "logic": "or", "rules": [ { "field": "kyc.status", "operator": "equals", "value": "Declined" } ], "goto": "decline" },
{ "id": "software_engineer", "logic": "and", "rules": [ { "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 } ], "goto": "proof_of_funds" },
{ "id": "else", "logic": "and", "rules": [], "goto": "final_decision" }
]
},
"proof_of_funds": {
"node_type": "feature",
"feature": "DOCUMENT_AI",
"label": "Proof of Funds",
"config": {
"document_ai_documents": [
{ "document_key": "proof_of_funds", "title": "Proof of Funds", "description": "Upload a recent bank statement.",
"fields": [
{ "key": "account_holder", "name": "Account holder", "type": "text", "required": true },
{ "key": "closing_balance", "name": "Closing balance", "type": "number", "required": true }
] }
],
"document_ai_missing_required_fields_action": "REVIEW"
},
"next": "final_decision"
},
"final_decision": { "node_type": "status", "session_status": "Determine", "label": "Final decision" },
"decline": { "node_type": "status", "session_status": "Declined", "label": "Declined" }
}
}
session_status: "Determine",它会根据会话中实际运行过的功能自动决策(通过 / 拒绝 / 审核中),因此资金证明路径和 else 路径都能正确得出结果,无需任何分支读取 document_ai.status(在跳过 Document AI 的路径上,该字段并不存在)。请注意:分支只能读取在到达该分支的每一条路径上都已运行的功能所对应的字段。
请以 { "graph": <above> } 的形式发送到 …/workflow-graph/ 端点(请先调用校验端点)。要从零开始创建新工作流,请使用 MCP 的 didit_workflow_set_graph;要修改现有工作流,请使用 didit_workflow_edit_graph(少量操作,保留现有节点和允许列表)。参阅 Didit MCP。授权
您的应用 API 密钥,可在 Business Console 的“开发者 -> API 密钥”中获取。主密钥拥有完整访问权限。指定名称的密钥可设置作用域:针对每个资源设为无权限、只读或读写,限定于特定工作流或已通过的会话、限定于 IP 地址列表,以及设置到期日期。401 表示密钥错误、已撤销或已过期;403 表示未发送密钥、密钥无权访问该资源或操作,或请求来自其 IP 列表之外的地址;在会话路由上返回 404 表示该会话不在该密钥允许的工作流或状态范围内。没有媒体访问权限的密钥获取到的图片、视频和 PDF URL 将为 null;没有会话写入权限的密钥获取到的会话链接和 token 将为 null。请参阅 https://docs.didit.me/console/api-keys。
请求体
创建简单的线性工作流。API 会在内部将此功能列表转换为基于节点的工作流图谱。严格的字段白名单:此处未列出的任何键(包括 workflow_type)都会被拒绝并返回 400。
按执行顺序排列的验证功能。API 会自动将每个项链接到下一个项,并自动添加一个最终的状态节点。
1Show child attributes
Show child attributes
工作流的显示名称(最多 50 个字符)。省略时默认为 "ID Verification",请始终设置该值以使工作流保持可区分。
50"Standard KYC"
将此工作流设为新会话的默认工作流。
省略此字段可创建已发布且可用于会话的工作流。仅当您希望保存而不发布时才使用 draft。
draft, published 为使用此工作流创建的会话启用白标定制。
允许验证流程在桌面端浏览器上运行。
验证被拒绝后允许的最大重试次数。0 表示在首次拒绝后即阻止用户。
0 <= x <= 10用于计算重试次数的滑动时间窗口,以天为单位。null 执行历史累计限制。
1 <= x <= 365每次会话允许的最大活体检测提交次数。默认值为 3:一次初始尝试加上两次重试。除非功能级配置覆盖,否则该值也会复制到生成的活体检测和年龄估算工作流节点中。
1 <= x <= 3每次会话允许的最大人脸比对提交次数。默认值为 3:一次初始尝试加上两次重试。除非功能级配置覆盖,否则该值也会复制到生成的人脸比对工作流节点中。
1 <= x <= 3未完成会话过期前的秒数。最短 1 小时(3600),最长 4 周(2419200)。
3600 <= x <= 2419200响应
工作流已创建。响应体为完整的工作流配置,与 GET /v3/workflows/{settings_uuid}/ 返回的原生 VerificationSettings 序列化内容相同(约 190 个字段:每个功能开关、阈值、操作以及生成的 workflow_graph),而非精简的列表项。请存储 uuid(同时也暴露为 workflow_id),以便在后续创建会话调用时引用它。注意:features 是以 " + " 连接的字符串,价格为 JSON 数字。
以下字段均为类型化字段;该对象还包含工作流配置的每个其他列(功能开关、阈值、警告操作、允许的证件、回调)作为附加属性。不适用于该 workflow_type 的字段为 null。
每个版本对应的标识符(settings_uuid)。工作流的每个版本都有其独有的标识符。
工作流所有版本共享的稳定标识符。使用它创建的会话将采用最新发布的版本。
工作流的显示名称。
工作流模板。对于未使用模板创建的图谱工作流为 null。
kyc, kyb, adaptive_age_verification, biometric_authentication, address_verification, questionnaire_verification, email_verification, phone_verification, null 这是否为该应用的默认工作流。
已归档的工作流无法启动新会话。
工作流内的版本号。
草稿版本可编辑;已发布版本不可更改,并用于新会话。
draft, published 此版本发布的时间。草稿为 null。
当工作流没有自定义 workflow_graph 时为 true。
仅对 draft 版本为 true。
启动此工作流会话的公开 URL。
工作流的图谱结构(节点和边)。简单工作流为 null。
用 " + " 连接的已启用功能,例如 "OCR + LIVENESS"。
每次会话的价格;对于图谱工作流,为最昂贵路径的价格。
工作流中最便宜路径的价格。
工作流中最昂贵路径的价格。
特定于组织的附加项价格,未设置时为 null。
验证被拒绝后允许的重试次数。
允许重试的天数。
未完成会话过期前的秒数。
附加到工作流的问卷,或为 null。
针对 KYB 关键人员的按角色验证设置。
当前哪些电子签名等级可以签署。在设置 signature_level 之前请检查此项。在 kyc、kyb 和图谱工作流中返回。
Show child attributes
Show child attributes
简易 KYC 或 KYB 工作流是否运行银行验证。它在联系方式步骤(以及位置验证)之后、数据库核验、AML 及 IP 分析之前运行。银行验证目前尚未正式公开发布:在未启用的情况下开启将被拒绝。
简易 KYC 工作流是否运行位置验证。它在联系方式步骤之后、银行验证和数据库核验之前运行。企业(KYB)工作流无法运行该验证。
简易工作流的位置验证设置,具有与 GEOLOCATION 节点相同的键(例如 is_geolocation_geofencing_enabled 和 geolocation_max_attempts)。未知的键将被丢弃。每个键都是可选的,缺少的键保持其默认值,因此 {} 表示全部使用默认值。
{}