sumsub-create-workflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSumsub — Create Workflow
Sumsub — 创建工作流
Builds an JSON payload from a compact node/edge spec, validates it (dry-run), POSTs it as a , and then — gated by the workflow's current live exposure — publishes it via : directly when exposure is low, or only after explicit confirmation when it's high. Reports the resulting (kind) / / / . Read the Danger section before publishing anything.
ApplicantWorkflowdraftPUT /{id}/revisionStatusnamerevisionrevisionStatusid从紧凑的节点/边缘规范构建 JSON负载,对其进行验证(试运行),将其作为提交(POST请求),然后根据工作流的当前实时曝光量,通过发布:曝光量低时直接发布,曝光量高时仅在明确确认后发布。返回生成的(类型)/(修订版本)/(修订状态)/。发布任何内容前请阅读“危险”部分。
ApplicantWorkflowdraftPUT /{id}/revisionStatusnamerevisionrevisionStatusidConcepts
概念
A workflow routes applicants through levels and branches. There is exactly one workflow
per kind: (auto-runs for every verification), (auto-runs for applicant
actions), (isolated; runs only from its draft, launched manually per applicant — not
publishable). Each save creates a new revision of that singleton, never a new workflow.
A revision is a draft — editable, routes no traffic, always safe to create / update /
validate — until it is published, which makes it live for all real production traffic
and auto-archives the previously published revision. never publishes; it runs from its
draft.
defaultactionstesttest工作流将申请人引导通过不同层级和分支。每种类型(kind)对应恰好一个工作流:
- (默认):对每一次验证自动运行
default - (操作):对申请人操作自动运行
actions - (测试):独立运行;仅从草稿手动针对单个申请人启动——不可发布
test
每次保存都会创建该单一实例的新修订版本(revision),而非新工作流。修订版本最初是草稿(draft)——可编辑,不路由流量,创建/更新/验证操作始终安全——直到被发布(published),发布后它会处理所有实际生产流量,并自动归档之前已发布的修订版本。类型永远不会发布;它从草稿直接运行。
testEndpoints
端点
| Method | Path | When |
|---|---|---|
| | Create (no |
| | Dry-run validation. Returns |
| | List workflows (every revision, all statuses). Response is flat: |
| | Read one revision (the |
| | Applicants recently on this revision's levels: |
| | Fork a new draft from a published/archived revision — the way to iterate on a live workflow without touching it. |
| | Publish / archive a revision. Body |
Permission required on the App Token: . Sandbox tokens typically have it; if you hit , regenerate the token with the right scopes.
manageWorkflows403 Unauthorized (atd)| 方法 | 路径 | 适用场景 |
|---|---|---|
| | 创建(无 |
| | 试运行验证。返回 |
| | 列出所有工作流(所有修订版本、所有状态)。响应为扁平化结构: |
| | 读取单个修订版本( |
| | 最近在此修订版本层级上的申请人数量: |
| | 从已发布/已归档的修订版本分叉出新草稿——这是在不影响实时工作流的前提下迭代的方式。 |
| | 发布/归档修订版本。请求体为 |
应用令牌需具备权限:。沙箱令牌通常已包含该权限;若遇到错误,请重新生成带有正确权限范围的令牌。
manageWorkflows403 Unauthorized (atd)⚠️ Danger — publishing affects LIVE production traffic
⚠️ 危险——发布操作影响实时生产流量
Workflow revisions are shared across live and sandbox; only applicants are
isolated. Publishing changes the workflow that handles all real production
traffic — a sandbox token does not make this safe. This is the single
most important fact about this skill. (Drafts, by contrast, route no traffic —
create / update / validate them freely.)
sbx:Publishing / is gated by the workflow's current live exposure
— the recent applicant traffic on the revision that is currently (the
one a publish auto-archives), measured by the subcommand. The
threshold is 400 recent applicants (override: ):
defaultactionspublishedworkflow_api.py trafficSUMSUB_PUBLISH_TRAFFIC_THRESHOLDWorkflow | Current live exposure | Publishing |
|---|---|---|
| > 400 recent applicants, or unreadable | Fully guarded: clean validate · diff vs current published · explicit user "yes" · |
| ≤ 400, or no published revision yet | Low-exposure: publish directly — no confirmation, no latch (still report what you did) |
| — | Not publishable — |
The exposure count is level-derived — it counts applicants recently seen on the level
names the published revision contains (so revisions sharing level names report overlapping
counts), reflecting how busy this workflow's levels are now, not the new revision's blast
radius. Phrase it to the user as "the levels in this revision have N recent applicants,"
never "this workflow handled N." Rules that hold regardless of exposure:
- Publishing is the default end of a build, not a separate request — the gate above decides what it costs (low → direct; high → confirm + latch). The only thing that keeps a build at a draft is the user explicitly opting out ("just save a draft").
- Fail safe on unknown exposure. If the live revision's traffic can't be read (API error), treat it as over-threshold and run the full guarded flow.
- Never switch kinds to dodge the gate. A publish request can't be made "safer"
by retargeting to (the API rejects it anyway). Publish the requested kind.
test - is the backstop: it independently re-measures exposure and requires
workflow_api.py publishonly when exposure is over-threshold or unreadable — letting a low-exposure publish through unlatched.SUMSUB_ALLOW_WORKFLOW_PUBLISH=1
工作流修订版本在实时环境和沙箱环境之间共享;仅申请人数据是隔离的。发布操作会更改处理所有实际生产流量的工作流——沙箱令牌无法保证操作安全。这是本技能最重要的一点。(相比之下,草稿不路由流量——可自由创建/更新/验证。)
sbx:发布/类型工作流受限于工作流的当前实时曝光量——即当前处于状态的修订版本(发布操作会自动归档该版本)的近期申请人流量,由子命令统计。阈值为400个近期申请人(可通过覆盖):
defaultactionspublishedworkflow_api.py trafficSUMSUB_PUBLISH_TRAFFIC_THRESHOLD工作流 | 当前实时曝光量 | 发布规则 |
|---|---|---|
| > 400个近期申请人,或无法读取曝光量 | 完全管控:验证通过 · 与当前已发布版本对比差异 · 用户明确确认“是” · 设置 |
| ≤ 400个,或尚无已发布版本 | 低曝光:直接发布——无需确认,无需开关(仍需报告操作内容) |
| —— | 不可发布—— |
曝光量计数是基于层级的——统计最近出现在已发布修订版本包含的层级名称上的申请人数量(因此共享层级名称的修订版本会报告重叠计数),反映的是当前该工作流层级的繁忙程度,而非新修订版本的影响范围。向用户表述时应说*“此修订版本中的层级近期有N个申请人”,而非“此工作流处理了N个申请人”*。无论曝光量如何,以下规则始终适用:
- 发布是构建流程的默认终点,而非单独请求——上述管控规则决定发布的条件(低曝光→直接发布;高曝光→确认+开关)。仅当用户明确选择退出(“仅保存草稿”)时,构建流程才会停留在草稿阶段。
- 曝光量未知时安全处理。若无法读取实时修订版本的流量(API错误),则视为超过阈值,执行完整的管控流程。
- 切勿通过切换类型规避管控。无法通过将发布请求目标改为来“降低风险”(API仍会拒绝)。请发布用户请求的类型。
test - 是最后保障:它会独立重新测量曝光量,仅当曝光量超过阈值或无法读取时,才要求设置
workflow_api.py publish——低曝光发布无需开关即可通过。SUMSUB_ALLOW_WORKFLOW_PUBLISH=1
Auth — App Token + secret (sandbox only)
认证——应用令牌+密钥(仅沙箱环境)
Each request is signed with App Token + secret (HMAC-SHA256). Full signing details
and debugging: .
401sumsub-api-auth⚠️ Sandbox only. A workflow routes real applicants, so production tokens are refused — the scripts reject anything not prefixed. Mint a sandbox pair in the dashboard (switch the workspace to Sandbox → App tokens → Create; both are shown once).sbx:
| Var | Example |
|---|---|
| |
| The paired secret shown once at token creation. |
| Optional. Defaults to |
If the user has already supplied credentials in conversation, reuse them;
otherwise ask once before running. Never echo the secret back.
Not a credential: publishing a high-exposure/defaultrevision (live traffic above the threshold, or unreadable) is armed byactions, but that is not a configured env var — it is a per-command arming token you type inline on the publish call (step 8), nothing else. Never add it toSUMSUB_ALLOW_WORKFLOW_PUBLISH=1or any persistent env: doing so leaves the gate permanently open, which defeats its only purpose. It belongs on exactly one command line, at the moment you publish, and nowhere else.settings.local.json
每个请求都使用应用令牌+密钥进行签名(HMAC-SHA256)。完整签名细节及错误调试:。
401sumsub-api-auth⚠️ 仅适用于沙箱环境。工作流会路由真实申请人,因此会拒绝生产环境令牌——脚本会拒绝所有非前缀的令牌。在控制面板中生成沙箱令牌对(切换工作区至Sandbox→应用令牌→创建;两者仅显示一次)。sbx:
| 变量 | 示例 |
|---|---|
| |
| 创建令牌时显示的配对密钥,仅显示一次。 |
| 可选。默认值为 |
若用户已在对话中提供凭据,请复用;否则在运行前询问一次。切勿回显密钥。
注意:这不是凭据——发布高曝光的/default修订版本(实时流量超过阈值,或无法读取)需要设置actions,但这不是配置环境变量——它是发布命令中手动输入的单次启用令牌(步骤8),仅此而已。切勿将其添加到SUMSUB_ALLOW_WORKFLOW_PUBLISH=1或任何持久化环境中:这样会永久打开管控开关,失去其唯一作用。它仅应出现在发布时的命令行中,别无他处。settings.local.json
Principles
原则
These three govern every step below; the procedure points back here where each one bites. They share one spine — the user's intent and the workspace's real entities are the ground truth; your job is to wire them faithfully, never to invent or improve.
- Build exactly what the user specified. Their level names, node structure, branch logic, and chosen are fixed constraints — not raw material to improve. If your own reasoning concludes a choice is redundant or suboptimal — often resting on a wrong assumption about how levels or the engine behave (e.g. chained levels don't re-capture already-collected data — see references/workflow-patterns.md) — build it literally and flag the concern in your report, never silently substitute a design you prefer. Validation catches what's invalid; "suboptimal" is not "invalid," and is never license to change their design.
kind - Wire entities; don't own them. Every level, client list, and tag a node references must resolve to something real before you build on it — a name the user gave you, one you confirmed via , or one explicitly being created (by you, a companion skill, or the user). An ungrounded reference is a stop, not a blank to fill: reuse what exists, get it created, or mark it explicitly pending — never invent a plausible name to make the graph look complete (that a missing reference blocks only publish, not the draft, is no license to fabricate one). Ground proactively — read the existing levels and reuse a real one before asking or inventing.
GET /resources/applicants/-/levels - Aim at the reference, not the green checkmark. Structural/builder errors are yours to fix — iterate until clean. But a missing-entity error (,
levelNotFound, unknown client list) is cleared only by grounding the reference — never by repointing it at a convenient entity to make validation pass. And report the territory, not the map you wish were true: a draft with unresolved references is unfinished, and structure you bent to fit an existing entity is a change to the user's policy — surface both plainly, never as a clean success.tagDoesNotExist
以下三条原则指导后续每一步;流程中会在相关步骤回溯到这些原则。它们的核心是用户的意图和工作区的实际实体是唯一依据;你的任务是忠实地实现,而非自行创造或优化。
- 严格按照用户要求构建。用户指定的层级名称、节点结构、分支逻辑和选择的是固定约束——而非可优化的素材。若你的推理认为某个选择冗余或次优——通常是基于对层级或引擎行为的错误假设(例如,链式层级不会重新采集已收集的数据——请参阅references/workflow-patterns.md)——严格按照用户要求构建,并在报告中标记该问题,切勿擅自替换为你偏好的设计。验证仅能发现无效内容;“次优”不等于“无效”,绝不能以此为理由更改用户的设计。
kind - 关联现有实体,而非自行创建。节点引用的每个层级、客户列表和标签必须对应真实存在的实体——用户提供的名称、你通过确认的名称,或明确正在创建的实体(由你、配套技能或用户创建)。未关联的引用是终止信号,而非空白填充:复用现有实体、等待创建完成,或明确标记为待处理——切勿为了让图看起来完整而编造合理名称(即使缺失的引用仅阻止发布而非草稿创建,也不能成为编造的理由)。主动关联——在询问或创建之前,先读取现有层级并复用真实存在的层级。
GET /resources/applicants/-/levels - 以实际情况为基准,而非追求验证通过。结构/构建错误由你修复——迭代直至验证通过。但缺失实体错误(、
levelNotFound、未知客户列表)仅能通过关联真实实体解决——切勿将引用指向方便的实体以通过验证。并且报告实际情况,而非你期望的情况:存在未解决引用的草稿是未完成的,为适配现有实体而修改的结构是对用户策略的更改——请明确指出这两点,切勿伪装成完美成功。tagDoesNotExist
Procedure
流程
-
Resolve the workflowfrom the user's own words, and state it back. If the user named no kind, it is
kind(the spec default). Usedefaultortestonly when the user explicitly names them. Never infer, default to, or substitute a kind the user didn't ask for. State the kind as an assumption and proceed — say e.g. "treating this asactionssince you didn't specify a kind" so it's visible and correctable; don't turn the kind into a multiple-choice question. Never offerdefaultas a "safe place to build" atestworkflow to avoid touching an existing draft:defaultis for manual, per-applicant test runs, has its own separate draft, cannot be published, and has no promotion path intotest. An existingdefaultdraft is never a reason to build elsewhere — just overwrite it (step 3).default -
Translate the user's flowchart into the compact spec (below). One node per box / decision diamond, one edge per arrow. Uses that read like labels (
id,country-check,route,idv). If the user gives a policy rather than a flowchart ("build me a KYC/KYB workflow"), don't start from a blank page — decompose it with the canonical pipeline spine and primitives in references/workflow-patterns.md, then stamp that spine per segment. Build only what the user specified, and ground every level / list / tag reference before building on it (Principles 1–2).reject-region -
Validate the graph: every edge/
fromresolves to a node id; everyto/applicantLevelreferences a real level by name; everyactionApplicantLevelhas ≥1 branching out-edge (exclusiveChoiceorcondition:); final-rejection nodes carryon:orlabels. (A level — and every non-choice node — has a single, unconditional out-edge; only anbuttonIdsmay branch — see EdgeexclusiveChoice.) The graph is a directed acyclic forest: edges point only forward (no cycles/back-edges — document resubmission is not a back-edge; the engine retries in place), while fan-in and shared downstream nodes are fine and several entry levels may root independent flows. Never bridge different applicant types (KYC individual vs KYB company) with an edge — an applicant's type can't change. Full graph rules: Constraints in references/workflow-schema.md; resubmission in references/workflow-patterns.md.on: -
Check current state withfor the target workflow
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py state <name>, then pick the write mode: no revisions → create (POST withoutname); a draft already exists → update it in place (POST with thatid; a no-idPOST wouldid); a published revision but no draft → seed a draft with409, or just POST a fresh one. Overwriting an existing draft is the normal, expected operation — do it silently. The draft is a scratch surface the skill evolves across multiple runs, so don't ask before overwriting and don't back it up by default. Only if the user explicitly asks to preserve their current draft, first read it with${CLAUDE_SKILL_DIR}/scripts/workflow_api.py fork <id>and save the JSON body to a file (the command appends a trailing${CLAUDE_SKILL_DIR}/scripts/workflow_api.py get <id>line — keep only the JSON), then overwrite. When editing, preserve existing node and edge ids —HTTP <code>the revision you're basing on and carry its ids through, minting new ids only for new nodes/edges (the engine matches in-flight runs on those ids; rationale in Revision continuity, references/workflow-schema.md).get <id> -
Generate payload with— spec on stdin → full payload on stdout. The builder assembles the payload from the real node types and edge
${CLAUDE_SKILL_DIR}/scripts/build_workflow.pyASTs, and validates node types, operators, and actionconditionagainst its built-in known sets — so typos and unsupported constructs error here, before any network call (the API's validate is authoritative for the rest — levels, client lists, labels). Set the draft'stargetTypein the spec when updating in place (step 3).id -
Validate (dry-run, stores nothing). Pipe the payload to→
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py validate.notices[]= advisory;info= non-blocking but usually a real bug;warning= publish-blocker. Look up any notice's meaning, severity, params, and how to act in references/workflow-notices.md. Clear a missing-entity error by grounding the reference, not by repointing it to pass validation (Principle 3). Always validate before persisting.error -
POST via— create (no
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py post) or update in place (with theidfrom step 3). Response carries the persisted draft withid,id,clientId.notices[] -
Build the dashboard link fromin the response body:
clientIdhttps://cockpit.sumsub.com/checkus/sdkIntegrations/workflows/active/applicant?clientId=<clientId>&sbx=truetargets the Sandbox workspace. ⚠️ Not a per-sbx=truedeep link — it shows the current draft; tell the user to click promptly.id -
Publish — the default end of a build, gated by exposure. Skip only if the user opted out ("just save a draft") — a draft routes no traffic, so it's not a useful end state on its own. The draft must have last validated clean (step 5) — the API refuses to publish a revision with anynotice. Run
error; it owns the live-exposure gate (defined in Danger) and does one of two things:${CLAUDE_SKILL_DIR}/scripts/workflow_api.py publish <draft-id>- Low exposure → it publishes directly; you're done.
- High exposure → it refuses before changing anything, printing the exposure count and the currently-published revision's (your one-command rollback point). To proceed, run the guarded flow: diff the draft against that published revision, disclose the change and that count to the user ("makes revision N live for ALL real production traffic; the current published revision — whose levels have ~M recent applicants — will be auto-archived"), get an explicit "yes", then re-run prefixed with
id.SUMSUB_ALLOW_WORKFLOW_PUBLISH=1
Rollback: re-publish the previously-published—id(keep the latch — once it's live again, the revision you're replacing may itself be over-threshold).SUMSUB_ALLOW_WORKFLOW_PUBLISH=1 ${CLAUDE_SKILL_DIR}/scripts/workflow_api.py publish <previous-published-id>is never published — see Danger.test -
Report (lead with the human-readable identity) — and report the territory, not the map you wish were true: flag any unresolved reference or bent structure plainly, never as a clean success (Principle 3). Lead with the workflow(
name/default/actions) andtest— e.g. «default» revision 14 — thenrevision; node/edge counts and the sorted unique level names referenced (so misspellings show); the dashboard link (step 7) as a clickable markdown link. If you published, say so, name the now-revisionStatusrevision, note the auto-archived previous revision, and give its id as the rollback point. End with a singlepublishedline — the one place a raw id belongs. On failure, surface Sumsub'sWorkflow ID: <id>/descriptionverbatim (builder validation errors already print a precise message).errorName
-
从用户表述中解析工作流,并反馈给用户。若用户未指定类型,则默认为**
kind(规范默认值)。仅当用户明确指定时,才使用default或test。切勿推断、默认或替换用户未要求的类型。将类型作为假设陈述并继续——例如,说*“由于你未指定类型,将此视为actions类型”*,以便用户可见并纠正;不要将类型变成选择题。切勿将default作为test工作流的“安全构建场所”以避免修改现有草稿:default用于手动、针对单个申请人的测试运行,有自己独立的草稿,无法发布,且没有升级为test的路径**。现有default草稿绝不是在其他地方构建的理由——直接覆盖即可(步骤3)。default -
将用户的流程图转换为紧凑规范(如下所示)。每个框/决策菱形对应一个节点,每个箭头对应一条边缘。使用类似标签的(如
id、country-check、route、idv)。若用户给出的是策略而非流程图(“帮我构建一个KYC/KYB工作流”),不要从零开始——使用references/workflow-patterns.md中的标准流程框架和原语分解策略,然后按段构建。仅构建用户指定的内容,并在构建前关联每个层级/列表/标签引用(原则1-2)。reject-region -
验证图结构:每条边缘的/
from都对应有效的节点id;每个to/applicantLevel都按名称引用真实层级;每个actionApplicantLevel至少有一条分支边缘(exclusiveChoice或condition:);最终拒绝节点带有on:或labels。(层级——以及所有非选择节点——有唯一、无条件的输出边缘;仅buttonIds可分支——请参阅边缘exclusiveChoice)。图是有向无环森林:边缘仅向前指向(无循环/反向边缘——文档重新提交不是反向边缘;引擎会在原地重试),而扇入和共享下游节点是允许的,多个入口层级可作为独立流程的根节点。切勿用边缘连接不同类型的申请人(KYC个人 vs KYB企业)——申请人类型无法更改。完整图规则:references/workflow-schema.md中的Constraints;重新提交规则在references/workflow-patterns.md中。on: -
检查当前状态:使用查看目标工作流
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py state <name>的状态,然后选择写入模式:无修订版本→创建(无name的POST请求);已有草稿→原地更新(带该id的POST请求;无id的POST请求会返回id);有已发布修订版本但无草稿→使用409生成草稿,或直接提交新草稿。覆盖现有草稿是正常、预期的操作——无需告知用户。草稿是技能在多次运行中迭代的临时区域,因此覆盖前无需询问,默认无需备份。仅当用户明确要求保留当前草稿时,先使用${CLAUDE_SKILL_DIR}/scripts/workflow_api.py fork <id>读取草稿并将JSON主体保存到文件(命令末尾会追加${CLAUDE_SKILL_DIR}/scripts/workflow_api.py get <id>行——仅保留JSON部分),然后覆盖。编辑时,保留现有节点和边缘id——获取你基于的修订版本的HTTP <code>结果并沿用其id,仅为新节点/边缘生成新id(引擎通过这些id匹配正在进行的运行;理由见references/workflow-schema.md中的Revision continuity)。get <id> -
生成负载:使用——标准输入传入规范→标准输出输出完整负载。构建器从真实节点类型和边缘
${CLAUDE_SKILL_DIR}/scripts/build_workflow.pyAST组装负载,并根据内置已知集合验证节点类型、运算符和操作condition——因此拼写错误和不支持的结构会在此处报错,无需网络请求(API的验证对其余内容——层级、客户列表、标签——具有权威性)。更新时在规范中设置草稿的targetType(步骤3)。id -
验证(试运行,不存储数据)。将负载通过管道传递给→返回
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py validate。notices[]=建议;info=非阻塞但通常是真实错误;warning=发布阻止项。在references/workflow-notices.md中查找任何通知的含义、严重性、参数和处理方法。通过关联真实实体解决缺失实体错误,而非修改引用以通过验证(原则3)。始终在持久化前进行验证。error -
提交(POST):使用——创建(无
${CLAUDE_SKILL_DIR}/scripts/workflow_api.py post)或原地更新(带步骤3的id)。响应包含持久化的草稿,带有id、id、clientId。notices[] -
构建控制面板链接:从响应体中的生成:
clientIdhttps://cockpit.sumsub.com/checkus/sdkIntegrations/workflows/active/applicant?clientId=<clientId>&sbx=true指向沙箱工作区。⚠️ 这不是针对单个sbx=true的深层链接——它显示当前草稿;请告知用户及时点击。id -
发布——构建流程的默认终点,受曝光量管控。仅当用户选择退出(“仅保存草稿”)时跳过——草稿不路由流量,因此本身不是有用的最终状态。草稿必须在最后一次验证中通过(步骤5)——API会拒绝发布带有任何通知的修订版本。运行
error;它负责实时曝光管控(定义见“危险”部分),并执行以下操作之一:${CLAUDE_SKILL_DIR}/scripts/workflow_api.py publish <draft-id>- 低曝光→直接发布;流程完成。
- 高曝光→在更改前拒绝,打印曝光量计数和当前已发布修订版本的(你的一键回滚点)。要继续,执行管控流程:对比草稿与已发布版本的差异,向用户披露更改内容和曝光量计数(“将修订版本N投入所有实际生产流量使用;当前已发布的修订版本——其层级近期约有M个申请人——将被自动归档”),获取用户明确的“是”,然后在命令前添加
id重新运行。SUMSUB_ALLOW_WORKFLOW_PUBLISH=1
回滚:重新发布之前已发布的——id(保留开关——重新发布后,你替换的修订版本本身可能已超过阈值)。SUMSUB_ALLOW_WORKFLOW_PUBLISH=1 ${CLAUDE_SKILL_DIR}/scripts/workflow_api.py publish <previous-published-id>类型永远不会发布——请查看“危险”部分。test -
报告(以人类可读的标识开头)——报告实际情况,而非你期望的情况:明确标记任何未解决的引用或修改的结构,切勿伪装成完美成功(原则3)。开头说明工作流(
name/default/actions)和test——例如,«default»修订版本14——然后是revision;节点/边缘数量和引用的唯一层级名称排序(以便发现拼写错误);控制面板链接(步骤7)作为可点击的markdown链接。若已发布,请说明,指出当前已发布的修订版本,提及自动归档的之前版本,并给出其id作为回滚点。最后以单独一行revisionStatus结束——这是唯一适合显示原始id的地方。失败时,直接显示Sumsub的Workflow ID: <id>/description(构建器验证错误已打印精确消息)。errorName
Compact spec format (JSON or YAML on stdin)
紧凑规范格式(标准输入为JSON或YAML)
selects which workflow you're editing (see Concepts for what each kind means and how revisions/drafts work). It is set only by the user's explicit request (see step 0); the default iskind. ⚠️default/defaultintercept real traffic once published — see the Danger section. (The schema has aactionsfield, but the engine ignores it — the skill neither sets nor relies on it.)title
Nodes use the real schema node-type in ; edges carry the real Sumsub
AST directly (no expression mini-language). is a shorthand for
.
typeconditionon:reviewDecisionsyaml
kind: default # optional — default | test | actions (the workflow to edit)
revisionStatus: draft # optional; only `draft` on create. Publishing is a separate, gated step (see Danger).
nodes:
- id: country-check
type: applicantLevel
levelName: "Country Selector"
- id: route
type: exclusiveChoice
name: "Country router"
- id: idv
type: applicantLevel
levelName: "IDV"
- id: idv-result
type: exclusiveChoice
name: "IDV result"
- id: reject-region
type: finalRejection
labels: ["WRONG_USER_REGION"]
- id: manual
type: manualReview
edges:
# A level's out-edge is single & unconditional — branch only at an explicit exclusiveChoice.
- { from: country-check, to: route } # plain: level → choice
- from: route # embargo first (any decision)
to: reject-region
condition: { or: [ { and: [ { op: in, args: [ {exp: "applicant.country"}, {lit: "[\"UZB\", \"DEU\"]"} ] } ] } ] }
- from: route # approved & allowed → IDV
to: idv
on: approved
condition: { or: [ { and: [ { op: eq, args: [ {exp: "applicant.country"}, {lit: "\"USA\""} ] } ] } ] }
- { from: idv, to: idv-result } # plain: level → choice
- { from: idv-result, to: manual, on: rejected } # resubmission unhandled → engine retries (the default)选择你要编辑的工作流(请参阅“概念”部分了解每种类型的含义以及修订版本/草稿的工作方式)。它仅由用户的明确请求设置(请参阅步骤0);默认值为kind。⚠️default/default类型发布后会拦截真实流量——请查看“危险”部分。(规范中有actions字段,但引擎会忽略它——本技能既不设置也不依赖该字段。)title
节点在中使用真实规范节点类型;边缘直接携带真实SumsubAST(无表达式迷你语言)。是的简写。
typeconditionon:reviewDecisionsyaml
kind: default # 可选——default | test | actions(要编辑的工作流类型)
revisionStatus: draft # 可选;创建时仅支持`draft`。发布是单独的管控步骤(请参阅“危险”部分)。
nodes:
- id: country-check
type: applicantLevel
levelName: "Country Selector"
- id: route
type: exclusiveChoice
name: "Country router"
- id: idv
type: applicantLevel
levelName: "IDV"
- id: idv-result
type: exclusiveChoice
name: "IDV result"
- id: reject-region
type: finalRejection
labels: ["WRONG_USER_REGION"]
- id: manual
type: manualReview
edges:
# 层级的输出边缘是唯一且无条件的——仅在显式exclusiveChoice处分支。
- { from: country-check, to: route } # 普通:层级→选择
- from: route # 先拦截禁运国家(任意决策)
to: reject-region
condition: { or: [ { and: [ { op: in, args: [ {exp: "applicant.country"}, {lit: "[\"UZB\", \"DEU\"]"} ] } ] } ] }
- from: route # 批准且允许→IDV
to: idv
on: approved
condition: { or: [ { and: [ { op: eq, args: [ {exp: "applicant.country"}, {lit: "\"USA\""} ] } ] } ] }
- { from: idv, to: idv-result } # 普通:层级→选择
- { from: idv-result, to: manual, on: rejected } # 未处理重新提交→引擎重试(默认行为)Node types
节点类型
A node is , where is the real schema node
type. Body fields may be flattened (shown below) or written in the real nested
form ().
{ id, type, name?, <body> }typeapplicantLevel: { levelName }Standard verification workflow ():
kind: default|test | Body |
|---|---|
| |
| — (branches via outgoing edges) |
| |
| — |
| |
Action workflow ( only): each type mirrors its standard counterpart — , , , , take the same body as the names without the prefix. Plus one extra: ( — the -workflow level to hand the verification back into; it must exist in the published workflow, or validate raises ).
kind: actionsaction*actionApplicantLevelactionExclusiveChoiceactionActionsactionManualReviewactionFinalRejectionactionactionApplicantTransitionlevelNamedefaultdefaultmissingLevelsForTransitionaction*defaultdefaulttest节点格式为,其中是真实规范节点类型。主体字段可以扁平化(如下所示)或使用真实嵌套格式()。
{ id, type, name?, <body> }typeapplicantLevel: { levelName }标准验证工作流():
kind: default|test | 主体 |
|---|---|
| |
| ——(通过输出边缘分支) |
| |
| —— |
| |
操作工作流(仅):每个类型对应标准类型的镜像——、、、、的主体与不带前缀的类型相同。额外一种类型:(——将验证转回工作流的层级;该层级必须存在于已发布的工作流中,否则验证会返回错误)。
kind: actionsaction*actionApplicantLevelactionExclusiveChoiceactionActionsactionManualReviewactionFinalRejectionactionactionApplicantTransitionlevelNamedefaultdefaultmissingLevelsForTransitionaction*defaultdefaulttestActions block
操作块
Inside an or node:
actionsactionActionsyaml
- id: tag-and-note
type: actions
actions:
- tag: ["High risk", "Manual review"] # adds tags
- note: "Flagged by workflow" # adds applicant note
- sourceKey: "manual-flow" # overrides applicant source keyEach item must contain exactly one of: , , . Tag/note also take a
richer object form for the niche cases:
tagnotesourceKeyyaml
- tag: { add: ["VIP"], remove: ["pending"], target: applicantAction }
- note: { text: "Auto-note", target: applicantAction }- (
target) istargetTypeby default.applicanttargets the triggering action and is only valid in an action workflow (applicantAction).kind: actions - Not supported: (postponed — no public API for its
kytCaseyet),blueprintId,riskScore. (The oldrecheckaction never existed — removed.)riskLevel
在或节点内:
actionsactionActionsyaml
- id: tag-and-note
type: actions
actions:
- tag: ["High risk", "Manual review"] # 添加标签
- note: "Flagged by workflow" # 添加申请人备注
- sourceKey: "manual-flow" # 覆盖申请人源密钥每个项必须包含以下之一:、、。标签/备注也支持更丰富的对象格式,用于特殊场景:
tagnotesourceKeyyaml
- tag: { add: ["VIP"], remove: ["pending"], target: applicantAction }
- note: { text: "Auto-note", target: applicantAction }- (
target)默认值为targetType。applicant针对触发操作,且仅在操作工作流中有效(applicantAction)。kind: actions - 不支持:(推迟——尚无公开API获取其
kytCase)、blueprintId、riskScore。(旧的recheck操作从未存在——已移除。)riskLevel
Edge conditions (condition:
)
condition:边缘条件(condition:
)
condition:An edge may carry a — the real Sumsub condition AST, written directly (there is no expression mini-language):
condition:yaml
condition:
or: # OR of AND-groups; branches evaluated in order, first match wins
- and:
- { op: eq, args: [ {exp: "applicant.country"}, {lit: "\"USA\""} ] }
- { op: in, args: [ {exp: "poi.country"}, {exp: "clientLists.high_risk"} ] }- — an expression path, verbatim. Anything the engine accepts works, including bracket/quoted segments:
{exp: "<path>"},applicant.metadata["balance"]. It's the left side of a comparison, and also the right side for field-to-field and client-list checks (clientLists["luhansk_people's_republic"]).{exp: "clientLists.high_risk"} - — a literal, supplied already JSON-encoded as a string (the form the API stores):
{lit: "<json>"},"\"USA\"","3","true". An un-encoded literal ("[\"UZB\", \"DEU\"]") is rejected by the API with{lit: "USA"}, so the encoding is required.syntaxError - is one of the comparison operators (
op; full set in references/workflow-schema.md). The builder validates everyeq ne lt lte gt gte in notIn contains notContains containsAny containsAll startsWith endsWith match empty notEmpty eqIgnoreCase …against its known set and rejects unknown or reserved ones (op).call - Negation is expressed with the operators (
not*,notIn,notContains,ne, …) — do not set anotEmptyflag; a top-levelnegateis rejected (UI-unsupported).Condition.negate
Client lists () are tenant-defined named lists maintained in the Sumsub dashboard, used as an expression RHS withclientLists.<name>/in/notIn. This skill does not create them. An unknown list validates as ancontainsAnyerror notice (a missing-entity error, likeinvalidExpression) — the construct is valid, the list just isn't defined in that workspace.levelNotFound
The full // AST, literal-encoding rules, and operator list live in references/workflow-schema.md; valid paths are in references/workflow-expressions.md.
ConditionAndCriterionexp边缘可携带——直接使用真实Sumsub条件AST(无表达式迷你语言):
condition:yaml
condition:
or: # AND组的OR;分支按顺序评估,第一个匹配项生效
- and:
- { op: eq, args: [ {exp: "applicant.country"}, {lit: "\"USA\""} ] }
- { op: in, args: [ {exp: "poi.country"}, {exp: "clientLists.high_risk"} ] }- ——表达式路径,原样使用。引擎接受的任何内容都有效,包括括号/引号段:
{exp: "<path>"}、applicant.metadata["balance"]。它是比较的左侧,也可作为字段间比较和客户列表检查的右侧(clientLists["luhansk_people's_republic"])。{exp: "clientLists.high_risk"} - ——字面量,需已编码为JSON字符串(API存储的格式):
{lit: "<json>"}、"\"USA\""、"3"、"true"。未编码的字面量("[\"UZB\", \"DEU\"]")会被API以{lit: "USA"}拒绝,因此必须编码。syntaxError - ****是比较运算符之一(
op;完整列表见references/workflow-schema.md)。构建器会验证每个eq ne lt lte gt gte in notIn contains notContains containsAny containsAll startsWith endsWith match empty notEmpty eqIgnoreCase …是否属于已知集合,并拒绝未知或保留的运算符(op)。call - 否定通过运算符表达(
not*、notIn、notContains、ne……)——请勿设置notEmpty标志;顶层negate会被拒绝(不支持UI)。Condition.negate
客户列表()是租户定义的命名列表,在Sumsub控制面板中维护,与clientLists.<name>/in/notIn一起用作表达式右侧。本技能不创建客户列表。未知列表会被验证为containsAny错误通知(缺失实体错误,类似invalidExpression)——结构有效,但列表在该工作区中未定义。levelNotFound
完整的//AST、字面量编码规则和运算符列表见references/workflow-schema.md;有效的路径见references/workflow-expressions.md。
ConditionAndCriterionexpEdge on:
(review-decision routing)
on:边缘on:
(审核决策路由)
on:on: approvedapprovedrejectedresubmissionreviewDecisions:on:condition:exclusiveChoiceactionExclusiveChoicereviewDecisionsexclusiveChoiceon:condition:on:condition:on:condition:Two primitives govern omitted decisions: (1) omitting makes an edge decision-agnostic — it doesn't check the decision; (2) a resubmitter stays on its level unless some branch on the immediate post-level choice names . Together, a no- edge in practice serves approved + rejected while resubmission retries by default (it's a deduction from these two, not a stored default set). Leave resubmission to the engine: don't name anywhere unless the user explicitly asks to cap or otherwise act on repeated attempts. It is not part of a standard workflow — the engine's automatic retry is what almost everyone wants. Only if asked, see Iteration without cycles: the resubmission gate in references/workflow-patterns.md (and note: naming on a choice changes how its other edges behave — that section explains the interlock).
on:resubmissionon:resubmissionresubmissionon: approvedapprovedrejectedresubmissionreviewDecisions:on:condition:exclusiveChoiceactionExclusiveChoicereviewDecisionsexclusiveChoiceon:condition:on:condition:on:condition:两个基本规则适用于未指定的决策:(1) 省略会使边缘与决策无关——不检查决策;(2) 重新提交的申请人停留在原层级,除非紧邻层级后的选择的某个分支指定了。因此,无的边缘实际上会处理approved + rejected,而重新提交默认由引擎重试(这是从上述两条规则推导而来,而非存储的默认集合)。让引擎处理重新提交:除非用户明确要求限制或处理重复尝试,否则不要在任何地方指定。它不是标准工作流的一部分——引擎的自动重试几乎是所有人想要的行为。仅当用户要求时,请参阅references/workflow-patterns.md中的Iteration without cycles: the resubmission gate(注意:在选择上指定会改变其其他边缘的行为——该部分解释了连锁反应)。
on:resubmissionon:resubmissionresubmissionWorked examples
示例
- — entry level → eligibility gate (region-reject vs IDV) → IDV → a second choice → manual-review on a rejected IDV. Two choices, because a level sits between them (the most common topology).
examples/country-routing.json - — small action-workflow that tags and notes after a level completes.
examples/action-workflow.json - — client-list geo gate + post-AML routing on
examples/aml-routing.json(PEP / SANCTIONS / ADVERSE_MEDIA) withapplicant.review.rejectLabelsand tag-driven fan-in (mirrors real-world AML topologies).containsAny
- ——入口层级→资格 gate(区域拒绝 vs IDV)→IDV→第二个选择→IDV拒绝后进入人工审核。两个选择,因为中间有一个层级(最常见的拓扑结构)。
examples/country-routing.json - ——小型操作工作流,在层级完成后打标签和添加备注。
examples/action-workflow.json - ——客户列表地域 gate + 基于
examples/aml-routing.json(PEP / SANCTIONS / ADVERSE_MEDIA)的AML后路由,使用applicant.review.rejectLabels和标签驱动的扇入(镜像真实世界AML拓扑结构)。containsAny
See also
另请参阅
- references/workflow-schema.md — reference for every node type's body, the action model, the AST + operators, the lifecycle, and the live-traffic guard.
Condition - references/workflow-expressions.md — the expression-path lookup dictionary.
- references/workflow-notices.md — the validation-notice catalog: every key the
notices[]/validateendpoints return, with severity, params, and how to act (ground a reference vs. fix the spec). Look up any notice you get here.post - references/workflow-patterns.md — composition patterns: how to decompose a KYC/KYB policy into the canonical pipeline spine and reusable primitives (strategy, not schema). Start here when the ask is "build me a workflow" rather than "fix this node".
- references/workflow-schema.md——每个节点类型的主体、操作模型、AST+运算符、生命周期和实时流量管控的参考文档。
Condition - references/workflow-expressions.md——表达式路径查找字典。
- references/workflow-notices.md——验证通知目录:/
validate端点返回的所有post键,包含严重性、参数和处理方法(关联引用 vs 修复规范)。遇到任何通知请在此查找。notices[] - references/workflow-patterns.md——组合模式:如何将KYC/KYB策略分解为标准流程框架和可复用原语(策略,而非规范)。当用户要求“帮我构建一个工作流”而非“修复这个节点”时,从此处开始。