其他
在 Sumsub 沙箱中创建或更新申请人等级(Applica…
当需要为 Sumsub 申请人流程创建或更新一个 level(身份/自拍/居住证明/问卷/支付方式/邮箱或电话验证/KYB/电子签名等步骤)时,用紧凑 spec 生成 ApplicantLevel 载荷并 POST/PATCH 到 Sumsub API。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
在 Sumsub 沙箱中创建或更新申请人特定
解决思路
- 先调用 sumsub-check-permissions 技能获取租户权限,解析 JSON 结果并保存 allowedChecks 映射(其键即已启用的权限),后续所有功能门控检查都基于它;这一步必须在其他操作之前完成。
- 在构建任何内容前,先列出已有的 levels、POA presets 和 questionnaires(GET 各自的列表端点)。若已存在匹配实体,提议复用其 id 并跳过 POST——服务器对每次 POST 都会创建重复项,且不做名称去重。
- 把用户请求翻译成紧凑 spec:name、applicantType,以及按 WebSDK 流程顺序排列的 doc-set 步骤列表。
- 校验:确认每个 type 都是真实的 IdDocSetType;每个 QUESTIONNAIRE 步骤都有指向已存在问卷的 questionnaireDefId;每个 PROOF_OF_RESIDENCE* 步骤都有 poaPresetId / poaStepSettingsId(缺失会被服务器拒绝,需先运行 sumsub-create-poa-preset);每个 COMPANY 步骤至少有一个具名子步骤;用第 0 步的 allowedChecks 键拒绝任何未授权的功能。
- 生成载荷:把 spec 通过 stdin 传给 ${CLAUDE_SKILL_DIR}/scripts/build_level.py,从 stdout 得到完整载荷。
- 在首次 POST 前,把解析后的载荷展示给用户并请求明确确认。
- 创建或更新:新建 level 用 ${CLAUDE_SKILL_DIR}/scripts/post_level.sh 发 POST(返回响应体 + HTTP 状态);更新已有 level 先用 ${CLAUDE_SKILL_DIR}/scripts/get_level.sh GET 当前状态让用户看到差异,再用 ${CLAUDE_SKILL_DIR}/scripts/patch_level.sh 发 PATCH。传给 build_level.py 的 spec 必须包含 id: <level-id>,builder 会保留它,PATCH 拒绝无 id 的请求体。PATCH 会把 requiredIdDocs.docSets 作为单个数组整体替换——发送的每个 docSet 必须携带完整目标状态,省略的子字段会被清空;从 GET 响应中复制要保留的值到 PATCH spec。
- 用 ${CLAUDE_SKILL_DIR}/scripts/get_level.sh GET 回读该 level 并与发送内容比对——若干字段落地结果与发送值不同(见 references/level-schema.md 的 gotchas),把任何差异报告给用户。
- 构建仪表盘链接:从响应体读取 id、applicantType、clientId,格式化为 https://cockpit.sumsub.com/checkus/sdkIntegrations/levels/<applicantType>Level/<id>?clientId=<clientId>&sbx=true。applicantType 为 individual 时该段为 individualLevel(已确认),为 company 时为 companyLevel(按类比推测,若 404 需标注)。sbx=true 指向 Sandbox 工作区。
- 报告:先给出人类可读名称,包括 name、applicantType、按顺序创建的 docSets 列表;以可点击 markdown 链接给出仪表盘链接;最后一行单独写 Level ID (for SDK access tokens / future PATCH): <id>。4xx 错误原样呈现——通常指向缺失的 questionnaireDefId 或未知枚举值。
- 租户权限门控:启用某功能前,确认所需 BackgroundCheckTarget 存在于第 0 步 allowedChecks 映射的键中。DocSet 类型到所需权限(任一即可)的映射:QUESTIONNAIRE/QUESTIONNAIRE2-4 → QUESTIONNAIRE;COMPANY/COMPANY_DATA → COMPANY | KYB_FULL | KYB_AUTO_AML_AND_REGISTRY | KYB_AUTO_AML_ONLY;SOLANA_ATTESTATION/LINEA_ATTESTATION → PAYMENT_METHOD_CRYPTO;PAYMENT_METHODS → PAYMENT_SOURCE | PAYMENT_METHOD | PAYMENT_METHOD_CRYPTO | KYT_UNHOSTED_WALLET_VERIFICATION;INVESTABILITY → PROOF_OF_FUNDS;PROOF_OF_RESIDENCE/PROOF_OF_RESIDENCE2 → POA | ADVANCED_POA_TYPE_DETECTION;E_KYC → E_KYC_TARGET;E_SIGN → E_SIGN_TARGET;TR_RECIPIENT_INFORMATION → TRAVEL_RULE;DEVICE_CHECK → DEVICE_INTELLIGENCE。不在表中的类型(IDENTITY、SELFIE、APPLICANT_DATA、EMAIL_VERIFICATION、PHONE_VERIFICATION 等)所有租户可用,无需权限。
- AML/监控名单筛查由 WATCHLISTS 控制(非 docSet,是 level 级行为):allowedChecks 含 WATCHLISTS 时,AML/PEP/制裁筛查默认对所有 level 开启(可用 disableWatchlists: true 按 level 关闭);不含 WATCHLISTS 时租户级关闭,任何 level 设置都无法开启——把需要 AML 筛查的策略视为被缺失权限阻塞(联系 CSM)。
- 若请求的功能需要租户没有的权限——立即停止,不要构建或 POST 该 level。告知用户缺失哪个权限、该功能在其账户不可用、需联系 CSM 或 Sumsub 支持启用;仅在用户确认权限已添加或明确决定放弃该功能后才继续。
- POA 例外:即使租户 API 实际接受 PROOF_OF_RESIDENCE level,allowedChecks 中也常缺失 POA / ADVANCED_POA_TYPE_DETECTION 键(该权限似乎是基线或被其他键覆盖,文档映射在某些租户上已过时)。仅当 POA 缺失时,带一行警告继续,不要暂停等待明确确认——若租户确实无权限,Sumsub 自身会拒绝写入,该 4xx 比预先中止更有信息量;若返回权限相关错误则原样呈现。
- 安全:创建 level 是对共享工作区的写入。始终在首次 POST 前向用户展示解析后的载荷;每次成功 POST 后 GET 回读实体并与发送内容比对(静默覆盖很常见);若某依赖(PoA preset、问卷)在本次会话中已成功创建,不要重复 POST,复用返回的 id;除非用户明确指名,不要删除或修改非本次会话创建的 level。
- 面向用户的消息中一律用名称而非 id(适用于关于该 level 的每一条消息,不只是最终报告):POST 前摘要中列出将附加的问卷和 POA preset 时,用其 name / title 指代(如 “POA preset «POA — 60 days»”、“questionnaire «Applicant basics»”),不要把原始 id 粘进正文;最终报告仍以单独一行 Level ID (for SDK access tokens / future PATCH): <id> 结尾——这是唯一适合放原始 id 的地方,因为用户需要复制它用于下一次 API 调用;诊断消息中当 4xx 响应引用依赖 id(如 POA preset 未找到)时,先翻译成名称再展示。若尚不知实体名称(如用户只给了外部 id),先 GET 该实体并给出其名称,不要退回用 id。
- 紧凑 spec 格式:接受 stdin 上的 JSON 或 YAML,builder 为每种 doc-set 类型填入合理默认值。示例:{"name":"Basic KYC","applicantType":"individual","type":"standalone","docSets":[{"type":"IDENTITY","docTypes":["PASSPORT","ID_CARD","DRIVERS"]},{"type":"SELFIE","videoRequired":"passiveLiveness"},{"type":"PROOF_OF_RESIDENCE","docTypes":["UTILITY_BILL"]},{"type":"QUESTIONNAIRE","questionnaireDefId":"source-of-funds"}]}。新建 level 必须是 WebSDK 2.0(websdkNext: true):创建时 builder 自动设置,无需在 spec 中写,但绝不要设 websdkNext: false(会发布已弃用的 WebSDK 1.0 level);更新已有 level(spec 含 id)时 builder 不动 websdkNext,你也不应动——升级在线 level 的 SDK 是重客户端迁移,不要作为无关 PATCH 的副作用翻转它,仅在用户明确要求时更改。
- 支持的 docSets[].type 值:APPLICANT_DATA、EMAIL_VERIFICATION、PHONE_VERIFICATION、IDENTITY、IDENTITY2/3/4、SELFIE、SELFIE2、PROOF_OF_RESIDENCE、PROOF_OF_RESIDENCE2、PROOF_OF_PAYMENT、PAYMENT_METHODS、INVESTABILITY、COMPANY、COMPANY_DATA、COMPANY_DOCUMENTS、COMPANY_BENEFICIARIES、ACCREDITED_INVESTOR、E_SIGN、QUESTIONNAIRE/2/3/4、E_KYC、OTHER_DOCS、TR_RECIPIENT_INFORMATION、DEVICE_CHECK。
- 按类型的紧凑快捷方式:IDENTITY* 用 docTypes、videoRequired(disabled/docapture)、captureMode 与 uploaderMode(仅 docapture 时发送)、nfcVerificationSettings: {mode},builder 展开为 docSet 上的扁平字段,只写你设置的内容;SELFIE* 用 videoRequired(默认 passiveLiveness,完整集合:disabled/enabled/photoRequired/passiveLiveness/staticLiveness)、docTypes(默认 ["SELFIE"])、selfieProcessingSettings: {skipLivenessCheck, skipFaceMatchCheck}(用于支付方式验证),展开为带 videoRequired [+ selfieProcessingSettings] 的裸 docSet;PROOF_OF_RESIDENCE* 用 docTypes(默认 ["UTILITY_BILL"])、poaPresetId(或 poaStepSettingsId)按 id 附加 POA preset,展开为 docSet + poaStepSettingsId;QUESTIONNAIRE* 用 questionnaireDefId(或 questionnaireId 别名,必填),展开为裸 docSet;APPLICANT_DATA 用 fields——字符串数组或 {name, required, prefill, immutableIfPresent},展开为带默认值的 fields[];PAYMENT_METHODS 用 typeSettings(对象,键:bankCard、bankAccount、cryptoWallet、eWallet)、skipOwnershipCheck(bool,默认 false)、skipRiskScoreCheck(bool)、walletScreeningProvider(枚举),展开为校验过的 paymentSourceSettings 结构,并始终发出 types: ["PAYMENT_SOURCE"];EMAIL_VERIFICATION / PHONE_VERIFICATION 展开为裸 docSet;COMPANY 用 steps——{name, minDocsCnt?, idDocTypes?, idDocSubTypes?, fields?, applicantLevelName?} 数组,展开为完整 KYB 步骤结构;E_SIGN 用 esignSettings(透传)。docSet 中的未知键会原样传给 API,便于在需要冷门字段时留逃生口。
- IDENTITY 步骤的仪表盘 ↔ API 映射(仪表盘侧边栏原文标签对应 API 值):Capture method 的 File upload → videoRequired: disabled;Live capture → videoRequired: docapture;Capture mode 的 Both manual and auto capture work at the same time → captureMode: manualAndAuto;Only manual capture is active → captureMode: manualOnly;Seamless live capture → captureMode: seamless;Fallback to file upload 的 Always available → uploaderMode: always;Available only if camera capture failed → uploaderMode: fallback;Not available → uploaderMode: never;NFC verification 的 Disabled → nfcVerificationSettings.mode: disabled;Optional → optional;Required → required。captureMode/uploaderMode 仅在 videoRequired: docapture 时有意义,否则会被静默丢弃(与仪表盘行为一致——切到 File upload 时删除这两个键)。NFC 的 required 会自动拒绝 Web SDK 申请人,仅 Mobile SDK 可用。
- builder 发出的默认值(仪表盘渲染所必需——控件绑定到实际存储值而非隐式 UI 默认,空字段渲染为 “Select” 占位而非默认选项):videoRequired: docapture 时 → captureMode: manualAndAuto、uploaderMode: always(除非调用方覆盖);IDENTITY 总是得到 nfcVerificationSettings: {mode: disabled}(除非调用方覆盖)。这些与仪表盘 Vue watcher 在用户首次切换 Live capture 时写入的值一致。PATCH 注意事项:requiredIdDocs.docSets 整体替换(仅顶层 Level 字段合并)——PATCH spec 中每个 docSet 必须携带完整目标状态,省略的子字段会被清空;先 GET 该 level 并复制要保留的值。
- AML/监控名单筛查有两个独立门控:1) 租户门控——WATCHLISTS 权限(第 0 步检查)。allowedChecks 含 WATCHLISTS 时 AML 筛查可用且默认对所有 level 开启;不含时整个租户关闭,任何 level 设置都无法开启——作为缺失权限呈现(联系 CSM),不要假装 level 能启用它。2) level 门控——disableWatchlists(仅在租户有 WATCHLISTS 时相关)。要为本 level 关闭筛查,发送顶层 "disableWatchlists": true;省略(或 false)则保持开启。watchListCheckSettings.amlCaseType 仅选择提供商(受租户门控),useCustomWatchListCheckSettings 仅选择自定义还是继承配置——两者都不是开关。因此对需要 AML 筛查的策略:在第 0 步确认 WATCHLISTS,然后 level 不加任何东西并 GET 回读确认 disableWatchlists 不为 true;对必须跳过 AML 的 level,设 disableWatchlists: true。
- 支付方式验证(PAYMENT_METHODS docSet,用于验证银行卡、银行账户、加密钱包或电子钱包)有两种模式:作为普通验证 level 中的步骤——在 IDENTITY / SELFIE / PROOF_OF_RESIDENCE 等旁边加一个 PAYMENT_METHODS docSet,level 保持常规 standalone 且无 actionType,适用于支付验证是更广泛入驻流程一部分时;作为 actions 类型 level 上的 action——设 type: "actions" 和 actionType: "paymentMethod",适用于独立、可重复运行的支付方式检查,与入驻解耦。根据用户表述选择模式:“给我的 KYC level 加卡/钱包验证” → 步骤模式;“创建独立的支付方式检查/action” → action 模式。
- 步骤模式示例:{"name":"KYC + payment method","type":"standalone","applicantType":"individual","docSets":[{"type":"IDENTITY"},{"type":"SELFIE","videoRequired":"passiveLiveness"},{"type":"PAYMENT_METHODS","typeSettings":{"bankCard":{"allowed":true,"countImages":"one"}}}]}。Action 模式示例:{"name":"Payment method check","type":"actions","actionType":"paymentMethod","websdkNext":true,"docSets":[{"type":"PAYMENT_METHODS","skipOwnershipCheck":false,"typeSettings":{"bankCard":{"allowed":true,"countImages":"one"}}}]}。
- builder 强制的约束:actionType: "paymentMethod" 要求 type: "actions",且 level 必须包含 PAYMENT_METHODS docSet;不能同时设 skipOwnershipCheck: true 和 skipRiskScoreCheck: true;walletScreeningProvider 放在 paymentSourceSettings 层级,不在 typeSettings.cryptoWallet 内(builder 会正确放置);bankAccount 需要具体验证方式——与 bankCard 不同,仅 allowed: true 启用银行账户会被 API 拒绝,必须至少启用 allowBankStatementUpload(对账单上传,无需额外权限)或 allowExternalSourcesCheck(外部数据源检查,使用 E_KYC 权限)之一;询问用户想要哪种方式,不要提供裸的“标准”银行账户选项。
- PAYMENT_METHODS docSet(两种模式):builder 始终发出 types: ["PAYMENT_SOURCE"] 加上由紧凑 spec 组装的 paymentSourceSettings 对象——API 拒绝没有 paymentSourceSettings 的 docSet,所以 builder 绝不省略它。typeSettings 按支付来源类型:bankCard 有 allowed(bool)、countImages("one"/"two"/"some")、extractIban(bool)、extractNationalBankAccountNumbers(bool)、requireBankAccountNumber(bool);bankAccount 有 allowed(bool)、allowBankStatementUpload(bool)、allowExternalSourcesCheck(bool,需 E_KYC_TARGET 权限),当 allowed 时这两个方法至少一个为 true;cryptoWallet 有 allowed(bool)、satoshiTestAllowed(bool)、unhostedWalletFormType("DEFAULT"/"SIMPLE"/"KAZ"/"TUR"/"SGP"/"POL"/"ITA");eWallet 有 allowed(bool)。walletScreeningProvider 枚举:crystal / merkle / trmLabs / chainalysis / elliptic / cyvers。权限:需要 PAYMENT_SOURCE 或 KYT_UNHOSTED_WALLET_VERIFICATION。仪表盘链接:两种模式都在 sdkIntegrations/levels/individualLevel/<id> 下——标准 standalone level URL 模式。
- 与其他 Sumsub-* 技能串联:本技能的 QUESTIONNAIRE 和 PROOF_OF_RESIDENCE doc-set 接受兄弟技能产出的 id,因此从零构建的三步流程很自然:先运行 sumsub-create-questionnaire,把返回的 id 作为 QUESTIONNAIRE doc-set 上的 docSets[].questionnaireDefId;先运行 sumsub-create-poa-preset,把返回的 id 作为 PROOF_OF_RESIDENCE doc-set 上的 docSets[].poaPresetId。
- 成功输出:先给人类可读信息——name、applicantType、按顺序的 docSets[].idDocSetType 列表;仪表盘链接 https://cockpit.sumsub.com/checkus/sdkIntegrations/levels/<applicantType>Level/<id>?clientId=<clientId>&sbx=true,渲染为可点击 markdown 链接(applicantType 为 individual 时该段为 individualLevel(已确认),为 company 时为 companyLevel(推测模式);三个字段 id、applicantType、clientId 均来自 POST 响应体,sbx=true 指向 Sandbox 工作区);最后单独一行 Level ID (for SDK access tokens / future PATCH): <id>。失败时:HTTP 状态 + Sumsub 错误信封中的 description/errorName。
适用边界 · 注意事项
- 不要用于其他 Sumsub 实体(问卷、工作流、申请人)——那些用对应技能。
- 不要用于紧凑 spec 未覆盖的一次性子集调整——那些用 sumsub-api-generic。
- 不要接受或使用生产 App Token——创建 level 是工作区可见的写入,仅用沙箱 token(以 sbx: 开头);若用户提供生产 token,拒绝并让其生成沙箱 token 对。