OpenAPI · REST 參考
REST 請求結構、身份驗證、工作流生命週期、錯誤處理、配置與文件上傳。
快速入門
基礎地址
| 環境 | 網關 URL |
|---|---|
| 測試環境 | https://api-gateway-platform.width.com/gateway |
| 生產環境 | https://api-gateway.width.com/gateway |
請求格式
POST {base_url}?api={apiName}&apiVersion={apiVersion}&apiKey={apiKey}&sign={sign}
Content-Type: application/json
x-domain: {domainId}
{JSON body}
| 參數 | 位置 | 必填 | 描述 |
|---|---|---|---|
api |
查詢參數 | 是 | API 名稱 |
apiVersion |
查詢參數 | 是 |
按接口而定 — workflow.apply.submit 推薦
2.0.0,其餘用 1.0.0(見
工作流生命週期 API)
|
apiKey |
查詢參數 | 是 | 您的 API key |
sign |
查詢參數 | 是 | 請求體的 HMAC-SHA256 簽名 |
x-domain |
請求頭 | 是 | 您的業務 domain ID |
發起工作流
接口:
workflow.apply.submit
POST
按 eventType + source
路由到當前已發佈(PUBLISHED)的工作流併發起執行。
apiVersion 版本
| apiVersion | 說明 |
|---|---|
1.0.0 |
按 eventType + source 自動路由
|
2.0.0 |
在 1.0.0 基礎上支持可選
workflowId,顯式指定要執行的已發佈工作流;workflowId
爲空時行爲與 1.0.0 完全一致。推薦使用。
|
請求
{
"eventType": "KYC_EVENT",
"source": "API",
"data": {
"customerId": "demo-customer-001",
"fullName": "ZHANG SAN",
"dateOfBirth": "1990-01-01",
"nationality": "CN"
},
"workflowId": ""
}
| 字段 | 類型 | 必填 | 描述 |
|---|---|---|---|
eventType |
string | 是 |
事件類型:KYC_EVENT / KYB_EVENT /
TRANSACTION_EVENT /
WEB3_TRANSACTION_EVENT /
MANUAL_CASE_EVENT,以租戶開通的事件爲準。前兩項在平臺界面上顯示爲
KYC 和
KYB,那是界面顯示名;接口裏要傳
KYC_EVENT 和 KYB_EVENT。MANUAL_CASE_EVENT
用於把主體提交做名單篩查,在平臺界面上顯示爲 人工。
|
source |
string | 是 |
來源。服務端對接固定填 API。
|
data |
object | 是 |
業務數據。customerId
必填且必須對同一客戶保持穩定(workflow.apply.query
依賴它做恢復查詢);其餘字段依工作流入口事件的字段定義。
|
workflowId |
string | 否 | 僅 2.0.0。指定已發佈工作流 ID;爲空則按事件自動路由。 |
響應
{
"apiCode": 200,
"apiMsg": "OK",
"data": {
"code": 200,
"message": null,
"executionId": "exec-7208f03b931e4897",
"status": "EXECUTING",
"requestId": "...",
"sync": false
}
}
| 字段 | 類型 | 描述 |
|---|---|---|
executionId |
string |
執行實例 ID。請務必持久化 — 後續用它輪詢
workflow.execution.status。
|
status |
string |
初始狀態(EXECUTING /
SUBMITTED)。
|
sync |
boolean | 是否同步執行;同步模式下 output 直接攜帶決策結果。 |
code |
int |
200 = 正常;403 = 額度不足(billing
blocked)。
|
submit 是唯一計費的接口。data.code 返回
403 表示該 eventType 的額度已用盡;不要盲目重試 —
請聯繫管理員充值或調整額度。
工作流 ID 位置:在 WIDTH One Platform 打開 Workflow,在工作流列表中複製工作流名稱旁顯示的 Workflow ID,並在使用 apiVersion 2.0.0 時通過 workflowId 傳入。
響應格式
成功:
{
"apiCode": 200,
"apiMsg": "OK",
"data": {}
}錯誤:
{
"apiCode": 999001,
"apiMsg": "SIGN_ERROR",
"data": null
}
4xx/5xx 表示網關未受理請求(無
data)。HTTP 200 時再看
apiCode 判斷網關結果(200 =
成功)。部分業務接口會在 data 下再包一層
{ code, data, msg } 業務信封 — 業務數據在
data.data(見註冊客戶示例)。
按順序讀取響應:先看 HTTP 狀態,再看 apiCode;若接口使用業務信封,再看 data.code 和 data.data。僅 apiCode 爲 200 不代表業務成功,仍須檢查該接口的業務結果。
錯誤碼
| apiCode | apiMsg | 描述 | 處理方式 |
|---|---|---|---|
| 200 | OK | 成功 | — |
| 999000 | GATEWAY_ERROR | 網關通用錯誤 | 聯繫技術支持 |
| 999001 | SIGN_ERROR | 簽名驗證失敗 | 參見身份驗證 |
| 999301 | API_INVALID_REQUEST | 請求格式錯誤 | 檢查請求體 |
| 999302 | API_NOT_FOUND | API 名稱未找到 |
檢查 api 參數
|
| 999303 | API_NON_AUTHORIZED | 無此 API 權限 | 檢查 apiKey 權限 |
| 999304 | RATE_LIMIT_TRY_AGAIN | 超出頻率限制 | 等待後重試 |
| 999401 | SERVICE_TIME_OUT | 後端服務超時 | 退避重試 |
| 999402 | SERVICE_INVOKE_ERROR | 後端調用失敗 | 重試;持續失敗則聯繫技術支持 |
| 999403 | SERVICE_NOT_FOUND | 後端服務不可用 | 聯繫技術支持 |
| 999404 | SERVICE_RESPONSE_FORMAT_ERROR | 後端返回格式異常 | 聯繫技術支持 |
| 999501 | REQUEST_API_EXCEED_LIMIT | API 調用配額已用盡 | 聯繫技術支持提升配額 |
身份驗證
簽名計算
sign = HmacSHA256(apiSecret, requestBody)
密鑰和請求體均使用 UTF-8 編碼。結果爲 64 位小寫十六進制字符串。 簽名必須基於 HTTP body 的原始字節計算。
代碼示例
Java:
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes("UTF-8"), "HmacSHA256"));
byte[] hash = mac.doFinal(requestBody.getBytes("UTF-8"));
String sign = HexFormat.of().formatHex(hash); // Java 17+
Python:
import hmac, hashlib
sign = hmac.new(
api_secret.encode('utf-8'),
request_body.encode('utf-8'),
hashlib.sha256
).hexdigest()
Node.js:
const crypto = require('crypto');
const sign = crypto.createHmac('sha256', apiSecret)
.update(requestBody, 'utf8').digest('hex');
冪等性
所有節點數據提交接口均基於 callbackKey
冪等。重複提交相同 callbackKey 是安全的
— 僅首次成功提交生效,因此網絡超時後可放心重試。
交互模型
-
客戶端 (App/Web): 鏈式驅動流程。
前端由每次提交響應中返回的
nextStep對象驅動,無需全局輪詢。僅對異步任務(如 OCR)輪詢專用.data接口。 -
服務端 (租戶後端): 最終狀態輪詢。
最後一個用戶輸入節點完成後,以寬鬆間隔(1–5 分鐘)輪詢
workflow.execution.status,直至達到終態。
工作流生命週期 API
工作流生命週期 API 是純服務端對接路徑:由你的後端發起一個事件、輪詢執行狀態與 Case(案件,一次審覈的記錄單元)審覈結論,並在請求丟失或被重試時查詢該客戶是否已有進行中的執行。三個接口構成一個閉環。
它與 KYC / KYB 章節記錄的節點驅動 SDK 路徑不同:那裏客戶端逐個節點提交數據,這裏由平臺從頭到尾跑完整個已發佈工作流,你只需發起並輪詢。
流程概覽
- 發起
- 輪詢狀態
- 審覈結論
workflow.apply.query 位於這個循環之前,作爲恢復路徑:當一次
submit
可能已經發生過(超時或上游重試)時先調用它,避免重複提交。
計費
| API | 用途 | 計費 |
|---|---|---|
workflow.apply.submit |
發起工作流執行 | 是 — 按 eventType 扣對應額度 |
workflow.execution.status |
按 executionId 查執行狀態與 Case 結論 | 否 |
workflow.apply.query |
查該客戶是否已有執行 | 否 |
api / apiVersion /
apiKey / sign)加 x-domain
請求頭,以及同樣的請求體 HMAC-SHA256 簽名。基礎地址(Staging
api-gateway-platform.width.com/gateway / 生產
api-gateway.width.com/gateway)與請求格式見
OpenAPI 參考 → 快速入門,完整簽名規則見
身份驗證
章節。
查詢狀態與審覈結論
接口:
workflow.execution.status
POST
只讀接口 — 不觸發執行、不計費。executionId 按租戶(及
x-domain)校驗歸屬,無法跨租戶查詢。使用
apiVersion 1.0.0。
請求
{ "executionId": "exec-7208f03b931e4897" }
| 字段 | 類型 | 必填 | 描述 |
|---|---|---|---|
executionId |
string | 是 |
workflow.apply.submit 返回的執行實例 ID。
|
響應
{
"apiCode": 200,
"apiMsg": "OK",
"data": {
"executionId": "exec-7208f03b931e4897",
"status": "COMPLETED",
"caseNo": "C001443",
"caseStatus": "REJECTED",
"riskScore": 45.0,
"resolvedAt": 1784714818000
}
}
| 字段 | 類型 | 描述 |
|---|---|---|
status |
string |
執行狀態:RUNNING / WAITING /
COMPLETED / FAILED /
取消狀態值:待確認。
|
caseNo |
string |
關聯 Case 編號(C%06d);尚未生成 Case
時爲空。
|
caseStatus |
string |
Case 處理狀態:UNASSIGNED(隊列待分配)/
UNDER_REVIEW(審覈員處理中)/
RETURNED(退回重做)/
OTHER_GROUP(本組已處理完,流程仍在其他組/節點進行)/
APPROVED / REJECTED /
CLOSED(已完成,流程結束);無 Case 時爲空。輪詢退出條件依據此字段。
|
riskScore |
number | 風險評分(0–100);無則爲空。 |
resolvedAt |
long | 結案時間(epoch 毫秒);未結案爲空。 |
currentNode 等
|
object |
SDK 流程控制用的內部字段(currentNode /
output /
pendingCallbackTask),服務端集成請忽略。
|
data 爲空字符串時,表示
executionId 不存在,或不屬於當前
apiKey 所在的租戶/domain。
狀態語義
status |
caseStatus |
含義 |
|---|---|---|
RUNNING / WAITING |
空 | 執行中(含等待信息採集),尚未生成 Case。 |
RUNNING / WAITING |
UNASSIGNED / UNDER_REVIEW /
RETURNED
|
執行停在 review 節點等待人工處理 — 繼續輪詢。 |
RUNNING / WAITING |
OTHER_GROUP |
當前 Case 所在組已處理完,流程仍在其他組/節點進行 — 繼續輪詢。 |
COMPLETED / 取消狀態值:待確認 |
APPROVED / REJECTED / CLOSED
|
已出結論,流程結束。 |
FAILED |
— |
執行失敗,請聯繫支持並提供 executionId。
|
各階段響應示例
階段 1 — 自動節點執行中,Case 尚未生成(到達決策節點前的短暫窗口):
{ "apiCode": 200, "apiMsg": "OK",
"data": { "executionId": "exec-xxxx", "status": "RUNNING" } }
無 caseStatus 字段 → 尚未生成 Case,繼續輪詢。
階段 2 — Case 已生成,等待人工處理:
{ "apiCode": 200, "apiMsg": "OK",
"data": { "executionId": "exec-xxxx", "status": "RUNNING",
"caseNo": "C001286", "caseStatus": "UNASSIGNED", "riskScore": 49.17 } }
人工處理期間
caseStatus
反映進度:UNASSIGNED(隊列待分配)→
UNDER_REVIEW(審覈員領取後);退回重做時爲
RETURNED。
階段 3 — 已出結論(人工審覈或自動決策):
{ "apiCode": 200, "apiMsg": "OK",
"data": { "executionId": "exec-xxxx", "status": "COMPLETED",
"caseNo": "C001443", "caseStatus": "REJECTED", "riskScore": 45.0,
"resolvedAt": 1784714818000 } }
caseStatus 爲
OTHER_GROUP — 請繼續輪詢至
caseStatus 變爲終態(APPROVED /
REJECTED / CLOSED)。
輪詢退出條件
每次執行到達決策節點後都會生成 Case(人工審覈 →
UNASSIGNED / UNDER_REVIEW;自動通過/拒絕/關閉
→ 直接產生終態 Case),因此:
caseStatus in {APPROVED, REJECTED, CLOSED}
OR status == FAILED
OR status == CONFIRMED_CANCELLATION_ENUMcaseStatus ∈
{UNASSIGNED, UNDER_REVIEW,
RETURNED})可放寬至 30–60 秒。
恢復查詢(防重 / 斷點恢復)
接口:
workflow.apply.query
POST
按 eventType + source + customerId
查詢該客戶是否已有執行。只讀 — 不觸發新執行、不計費。使用
apiVersion 1.0.0。
-
斷點恢復 — 調用
submit後超時或宕機、未拿到executionId:先 query,命中則直接拿回executionId繼續輪詢,避免重複提交(submit 計費)。 -
提交防重 —
上游重試導致同一客戶可能重複發起:query 命中
RUNNING/APPROVED時跳過submit。
請求
{
"eventType": "KYC_EVENT",
"source": "API",
"customerId": "demo-customer-001"
}
| 字段 | 類型 | 必填 | 描述 |
|---|---|---|---|
eventType |
string | 是 | 與 submit 時一致。 |
source |
string | 是 | 與 submit 時一致。 |
customerId |
string | 是 |
與 submit 時
data.customerId
一致(必須對同一客戶保持穩定)。
|
響應
{
"apiCode": 200,
"apiMsg": "OK",
"data": {
"code": 200,
"executionId": "exec-7208f03b931e4897",
"status": "RUNNING"
}
}
| 命中情況 | executionId |
status |
建議動作 |
|---|---|---|---|
| 客戶已審批通過 | 最近一次 COMPLETED 執行 | APPROVED |
不要重複提交。 |
| 存在進行中的執行 | 該執行 | RUNNING |
轉輪詢 workflow.execution.status。
|
| 未命中(被拒/無記錄) | 空 | — |
可正常調用 submit。
|
customerId 必須與 submit 時
data.customerId
完全一致,且對同一客戶長期穩定;否則恢復查詢無法命中。
錯誤碼
| 層級 | 碼 | 說明 | 處理建議 |
|---|---|---|---|
網關 apiCode
|
200 |
成功(業務結果看 data.code)
|
— |
網關 apiCode
|
999302 |
API 名稱錯誤 |
檢查 api / apiVersion
|
網關 apiCode
|
999305 |
apiKey 無效或已停用
|
覈對 API & Integration 頁面的 API Key |
網關 apiCode
|
999001 |
簽名錯誤 | 檢查 sign 計算(body 原文簽名、無多餘換行) |
網關 apiCode
|
999401 |
服務超時 | 稍後重試;持續出現請聯繫支持 |
data.code |
400 |
參數缺失 | 檢查請求 body |
data.code |
403 |
額度不足(僅 submit) | 聯繫管理員充值/調整額度 |
data 爲空
|
— | execution 不存在或不屬於當前租戶/domain(僅 status) |
覈對 executionId 與 apiKey /
x-domain;x-domain 爲必填
header,請始終顯式攜帶
|
代碼示例
curl(完整閉環)
API_KEY="ak_xxx"
API_SECRET="sk_xxx"
DOMAIN_ID="{domainId}"
BASE_URL="https://api-gateway-platform.width.com/gateway"
# HMAC-SHA256 sign helper / 签名助手
sign() { printf "%s" "$1" | openssl dgst -sha256 -mac HMAC -macopt key:"$API_SECRET" | sed 's/^.* //'; }
# 0. (optional) recovery query — dedup before submit / 恢复查询防重
BODY='{"eventType":"KYC_EVENT","source":"API","customerId":"demo-customer-001"}'
curl -s -X POST "${BASE_URL}?api=workflow.apply.query&apiVersion=1.0.0&apiKey=${API_KEY}&sign=$(sign "$BODY")" \
-H "Content-Type: application/json" -H "x-domain: ${DOMAIN_ID}" -d "$BODY"
# 1. submit / 发起工作流
BODY='{"eventType":"KYC_EVENT","source":"API","data":{"customerId":"demo-customer-001","fullName":"ZHANG SAN"},"workflowId":""}'
curl -s -X POST "${BASE_URL}?api=workflow.apply.submit&apiVersion=2.0.0&apiKey=${API_KEY}&sign=$(sign "$BODY")" \
-H "Content-Type: application/json" -H "x-domain: ${DOMAIN_ID}" -d "$BODY"
# -> record data.executionId / 记下 data.executionId
# 2. poll status / 轮询执行状态
BODY='{"executionId":"exec-7208f03b931e4897"}'
curl -s -X POST "${BASE_URL}?api=workflow.execution.status&apiVersion=1.0.0&apiKey=${API_KEY}&sign=$(sign "$BODY")" \
-H "Content-Type: application/json" -H "x-domain: ${DOMAIN_ID}" -d "$BODY"
Python(輪詢循環)
import hashlib, hmac, json, os, time, requests
GATEWAY = "https://api-gateway-platform.width.com/gateway"
API_KEY = os.environ["WIDTH_API_KEY"]
API_SECRET = os.environ["WIDTH_API_SECRET"]
DOMAIN_ID = os.environ["WIDTH_DOMAIN_ID"]
# Supply only the cancellation enum verified by WIDTH engineering.
# Do not guess between the conflicting spellings in the reference.
CANCELLED_STATUS = os.environ["WIDTH_CONFIRMED_CANCEL_STATUS"]
def call(api, version, payload):
body = json.dumps(payload, separators=(",", ":"))
signature = hmac.new(API_SECRET.encode(), body.encode(), hashlib.sha256).hexdigest()
response = requests.post(GATEWAY,
params={"api": api, "apiVersion": version, "apiKey": API_KEY, "sign": signature},
headers={"Content-Type": "application/json", "x-domain": DOMAIN_ID},
data=body, timeout=30)
response.raise_for_status()
result = response.json()
if result.get("apiCode") != 200:
raise RuntimeError(result)
data = result.get("data")
if isinstance(data, dict) and data.get("code", 200) != 200:
raise RuntimeError(data)
return data
identity = {"eventType": "KYC_EVENT", "source": "API", "customerId": "demo-customer-001"}
recovered = call("workflow.apply.query", "1.0.0", identity)
if recovered and recovered.get("status") == "APPROVED":
print(recovered)
else:
if recovered and recovered.get("status") == "RUNNING":
execution_id = recovered["executionId"]
else:
submitted = call("workflow.apply.submit", "2.0.0", {
"eventType": identity["eventType"], "source": identity["source"],
"data": {"customerId": identity["customerId"], "fullName": "ZHANG SAN"},
"workflowId": ""})
execution_id = submitted["executionId"]
# Persist execution_id before polling; recover after an interrupted submit.
while True:
state = call("workflow.execution.status", "1.0.0", {"executionId": execution_id})
if not isinstance(state, dict):
raise RuntimeError("Verify executionId, apiKey and x-domain; no execution returned")
if state.get("caseStatus") in ("APPROVED", "REJECTED", "CLOSED") or state.get("status") in ("FAILED", CANCELLED_STATUS):
print(state)
break
time.sleep(30 if state.get("caseStatus") in ("UNASSIGNED", "UNDER_REVIEW", "RETURNED") else 5)
常見問題
不要直接重試
submit(會重複計費)。先調
workflow.apply.query:命中 RUNNING /
APPROVED 則拿回
executionId 繼續輪詢;未命中再重新 submit。
data
爲空)?確認與提交時使用同一個
apiKey 與
x-domain——接口按租戶和 domain 校驗歸屬,跨
domain 查詢返回空 data。
不一定。停在 review 節點等待人工處理時執行狀態不會變爲終態,此時
caseStatus 爲 UNASSIGNED /
UNDER_REVIEW / RETURNED。以
caseStatus 判斷處理進度。
配置
在啓動工作流前,獲取平臺配置數據(國家列表、證件類型、表單模板等)。
接口:
prepare.avira.config
POST
{
"keys": "particular,avira_ssic,avira_ssoc,avira_countries,avira_document_master,avira_document_default_rule,required-form,avira_phone_code,avira_entity_types,ownership_structure_layer,transaction_behaviour,product_service_complexity,source_of_wealth,avira_source_of_funds"
}
配置項
| Key | 描述 | 使用方 |
|---|---|---|
particular |
個人/企業信息字段定義 | Particular 節點 (KYC & KYB) |
required-form |
帶驗證規則的表單模板 | Required Forms 節點 |
avira_countries |
國家列表 (ISO 代碼 + 名稱) | 國籍、居住國等 |
avira_document_master |
各國支持的證件類型 | OCR 及證件上傳 |
avira_document_default_rule |
默認證件要求 | 必傳文件節點 |
avira_ssic |
行業分類代碼 | 行業字段 |
avira_ssoc |
職業分類代碼 | 職業字段 |
avira_phone_code |
國際區號 | 手機驗證 |
avira_entity_types |
企業實體類型 | KYB 企業信息 |
ownership_structure_layer |
股權結構層級設置 | KYB 工作流 |
transaction_behaviour |
交易行爲類別 | KYB 表單 |
product_service_complexity |
產品/服務複雜度類別 | KYB 表單 |
source_of_wealth |
財富來源選項 | KYC/KYB 表單 |
avira_source_of_funds |
資金來源選項 | KYC/KYB 表單 |
文件上傳
需要 S3 URI 的接口(證件、照片等),需先通過此接口上傳文件。
步驟 1 — 獲取上傳令牌
接口:
storage.upload.token
POST
請求體: {}。令牌有效期 15 分鐘。
響應:
{
"apiCode": 200,
"data": {
"uploadToken": "a1b2c3d4e5f6...",
"expiresIn": 900
}
}
步驟 2 — 上傳文件
存儲服務地址:
| 環境 | URL |
|---|---|
| 測試環境 | https://api-gateway-platform.width.com/storage |
| 生產環境 | https://api-gateway.width.com/storage |
Multipart 上傳:
curl -X POST "${STORAGE_URL}/api/v1/upload/file" \
-F "uploadToken=a1b2c3d4e5f6..." \
-F "file=@passport_front.jpg"
Base64 上傳:
curl -X POST "${STORAGE_URL}/api/v1/upload" \
-H "Content-Type: application/json" \
-d '{
"uploadToken": "a1b2c3d4e5f6...",
"image": "data:image/jpeg;base64,/9j/4AAQ...",
"filename": "passport_front.jpg"
}'
響應:
{
"apiCode": 200,
"data": {
"url": "s3://workflow-kyc/tenantImages/2026-04-16/abc123.jpg",
"filename": "passport_front.jpg",
"content_type": "image/jpeg",
"size": 245760
}
}
支持格式: PNG, JPG, JPEG, GIF, WEBP, TIFF, PDF — 大小限制: 20 MB
錯誤處理與重試策略
| 場景 | 處理方式 |
|---|---|
| SIGN_ERROR / 999001 | 修復簽名,不要在未修正時重試。 |
| 999304 | 等待限流恢復後重試。 |
| HTTP 500 / 999401 | 最多重試三次,按 1 秒、2 秒、4 秒指數退避;若 submit 可能已被受理,先用 workflow.apply.query 恢復。 |
| data.code = 403 | 請管理員充值或調整事件額度,不要盲目重試。 |
| 上傳令牌 HTTP 401 | 獲取新的上傳令牌,再重試上傳一次。 |
| OTP 驗證 | 達到最大驗證次數後重新發送 OTP。 |
| 節點提交超時 | 節點數據提交可使用相同 callbackKey 重試;不要將此保證推廣至 workflow.apply.submit。 |