- 第 1 部分:概念。 请先阅读本部分。v3 是重塑,而不是改名:如果您把旧端点一对一地映射过来,就会与 API 处处对抗。在这里花十分钟,可以为您日后节省数天时间。
- 第 2 部分:API。 逐个端点的映射、请求示例、状态机以及迁移检查清单。
api.deprecation webhook 事件以接收下线通知。
目录。 第 1 部分:1.1 为什么会有 v3、1.2 对象模型、1.3 按 capability 就绪、1.4 task 循环、1.5 资金流动、1.6 状态机、1.7 约定、1.8 黄金路径。第 2 部分:2.1 customers、2.2 capabilities、2.3 tasks 与 submissions、2.4 accounts、2.5 recipients 与 destinations、2.6 quotes 与 transfers、2.7 webhooks、2.8 沙箱、2.9 遗留端点、2.10 迁移顺序、2.11 常见陷阱清单。
第 1 部分:概念
1.1 为什么会有 v3
v1 和 v2 中演化出了四种彼此重叠的方式来让客户具备支付能力:/rails、/banks、/accounts/applications,以及企业客户的 rail-applications 接口,每种都有自己的一套状态词汇。文档采集(/documents、KYC 导入、验证 SDK token)也与它真正解锁的对象脱节。v3 把所有这些整合为六个资源,即客户加上其所拥有的五种事物:
1.2 对象模型
需要内化的两条结构性规则:- Capability 决定一切。 account 只能在
ready状态的 capability 下开立;quote 基于 capability 定价。入驻等同于让您需要的 capability 达到ready。 - Task 可以附着在任何地方。 capability、account 或进行中的 transfer 都可能携带
openTaskIds。无论出现在哪里,处理循环都相同:读取 task,提交答案,等待审核,再重新读取父资源。
1.3 就绪状态是按 capability 计算的,而不是按客户
v1 把/rails 的就绪状态和面向客户整体的 KYC 门禁纠缠在一起。在 v3 中没有客户级 status:客户可以在 stablecoin_transfers 上完全可用,而其 sepa capability 仍有未完成的 task。汇集账户型(pooled)capability 通常比命名账户型(named)更快进入 ready,因此应在已 ready 的 capability 上先开始交易,而不是等到所有都就绪。
如果您的 v1 或 v2 代码依据客户验证状态驱动 UI 徽章,请重写它:
- 「他们能在 X 上交易吗?」变为 capability X 的
status == "ready"。 - 「他们是否需要做什么?」变为存在任何状态为
action_required的 task(此时 capability 通常显示为restricted,statusReason.resolution: "complete_tasks")。 - 「我们是否在等 Swipelux?」变为 task 处于
in_review、capability 处于pending。
1.4 task 循环
原有的文档与 KYC 接口所做的一切,现在都归结为这一个循环: 关键特性:- 一个 task 携带
requirements[],即具体的问询项。每项都有 task 内唯一的requirementId、一个用于命名问询的稳定key(例如地址证明,UI 中据此去重),以及一个类型化的request,精确描述所需输入的类型(text、date、select、document、attestation 等)。 - 提交是受审核约束的:提交本身绝不会直接改变 capability 或 account 的状态,只有被接受才会。有一个例外:
profile类答案在提交时会直接写入客户档案(见 2.3)。提交后,请轮询 task 或其父资源。 taskRevision(对 taskrevision的回显)是并发保护:如果自您读取以来 task 已发生变化,需重新读取并重建答案。absence是一等公民的答案(「我没有这个,因为……」),请使用它,而不要让 requirement 悬空。
1.5 资金流动
payin、payout 与稳定币划转共用一个流程。没有方向输入,您绝不需要声明是 payin 还是 payout。进出币种的形态派生出 quote 与 transfer 上一个只读的direction:fiat_to_stablecoin(payin)、stablecoin_to_fiat(payout)或 stablecoin_move。
1.6 每个资源一台状态机
每个带状态的资源都有自己的枚举,并且所有非「顺利」状态都会携带一个结构化的原因。account、application 与 transfer 共用形态{ code, message, actor, retryable }:account 与 application 通过 statusReason 暴露,transfer 通过 stateDetail 暴露。actor 指出应由谁行动(customer、developer、provider、network、swipelux),retryable 说明重试是否有意义。capability 使用 { code, resolution, message },其中 resolution(complete_tasks、wait、contact_support、none)说明如何推动 capability 前进。code 的取值是开放的、只增不减的目录:请按 resolution(或 actor 加 retryable)来分支,并对您从未见过的 code 保持容忍。
本指南未展开的状态(
rejected、suspended、disabled、failed、canceled)是终态或需要支持团队介入的状态;各资源的具体定义详见规范。
Transfer 的详细状态:
1.7 约定
在动手写代码之前值得内化的幂等性规则:
- 使用相同的 key 但不同的请求体,在该 key 的保留期内(至少 7 天)会返回
409 idempotency_conflict,因此绝不要计划复用 key。请为每个逻辑操作生成一个新的 UUID,并与您的任务一起持久化。 - 重放同样适用于错误:如果原请求以终态 4xx 结束,同样的 key 加请求体会再次返回相同的问题响应。
- 两个使用相同 key 的并发请求:一个胜出,另一个得到
409。请在胜者落定后重试失败的那个;重放会返回原始响应。
1.8 黄金路径
第 2 部分:API
2.1 Customers
创建时按
type 区分(示例值,字段名以规范为准):
- 创建是渐进式的:仅
{ "type": "individual" }也是有效的创建请求。缺失的信息永远不会使客户无效,它们会在稍后作为需要它的 capability 上的 intake task 呈现出来。 - 企业客户携带
business及注册数据。v1 的股东 CRUD 被映射到 related parties,并扩展为涵盖董事、高管和所有人:可以在创建客户时以内联方式创建(每个都会获得一个稳定的rp_id),也可以通过专用的 related-parties 端点进行管理。 - 没有客户
status字段,参见 1.3。 - 已存在的客户可延续使用:在 v1 或 v2 上创建的客户,可通过相同的 id 在 v3 端点上访问。v3 读取的是一个_已净化的视图_,无法通过 v3 校验的历史值会以缺失形式返回。在您的第一次 v3 写入之后,该视图将永久固化:缺失的值不会自行回来。因此请尽早补全数据,规划一次一次性的操作,用您自己的记录
PATCH完整档案,然后再依赖 v3 读取。v1 的metadata属于独立的命名空间,不会被延续,请在 v3 上重新设置。 externalId是一等字段,在 v3 上在您的客户之间保持唯一,按环境隔离(409 duplicate_external_id)。归档客户并不会释放其externalId,若您打算复用,请在 DELETE 之前用 PATCH 将其清空。DELETE是归档级联操作(不可恢复;id 永不复用)。当存在任何未归档的 account 或进行中的 transfer 时会被阻止,返回409 customer_has_active_resources并附带blockingResources[]。- PATCH 合并规则:显式
null会清空可空字段;数组整体替换(inline related parties 例外,按 id upsert);metadata的键会合并。完整 schema 与列表过滤条件详见 OpenAPI 规范。
2.2 /rails、/banks、application 演变为 Capabilities
- 一个 capability 由
method(ach、wire、rtp、pix、sepa、swift、spei、pse、transfers_3_0、faster_payments、sepa_instant、uaefts、card、stablecoin_transfers等)加accountType(pooled或named,非银行方式为null)加directions(payin或payout)构成。公开的capabilityId是限定组合(sepa_pooled、ach_named),或对card与stablecoin_transfers而言就是方式本身。 - 每次请求 capability 都会派生出一个 application,即
.../capabilities/{capabilityId}/applications之下的按次尝试记录(还有/{applicationId}/history),它有自己的状态(见 1.6)和statusReason。它是一次请求的审计轨迹;日常轮询请针对 capability 本身。 capabilities/supported返回可用性(available、beta或disabled)、资格以及可选的机构。银行选择在请求时通过可选的institutions数组进行,不再有单独的/banks资源。省略该字段(或发送[])即选中所有默认机构;isDefault: true是针对特定客户与特定 capability 的标志,并非全局标志。非空列表将覆盖默认值;如果某个基于银行的 capability 没有适用的默认值,将返回422 capability_institutions_required。机构 id 是不透明的,请对新的 id 保持容忍。stablecoin_transfers在创建客户时被自动授予,出生即为ready(因此永远不需要请求,也不可取消)。card仅限个人客户。- capability 上的
openTaskIds是您的「下一步做什么」指针。开放即action_required或in_review,且该汇总包含通过活跃依赖关系涉及到的客户级共享 task。 cancel仅可在pending或restricted状态下且没有阻塞资源时使用,否则返回409 capability_not_cancelable,其问题响应体列出blockingResources。取消后如需再次请求,需带一个新的幂等性 key 全新创建。- 轮询 GET。 capability 状态在您读取时刷新;请轮询
GET .../capabilities/{capabilityId}或订阅capability.status_changed,不要缓存。 - 请求某一种方式可能会一次性使多种相关方式变为可用,请将 capability 视为一个需要整体重新读取的集合,而不是单独跟踪的一行。
- 使用
tasks-preview可以在承诺一次请求之前先展示入驻问询。 - 验证不是一次性的:即便一个 capability 已处于
ready状态,仍可能出现新的 task(周期性或事件驱动的再验证)。请让 task 循环在客户的整个生命周期内保持运转,而不仅仅是入驻期间。
2.3 文档与 KYC 演变为 Tasks 和 Submissions
命名说明。 这些端点曾短暂发布为
requirements 和 fulfillments。自 2026-08-02 起,公开名称为 tasks 与 submissions。改名仅涉及资源和端点路径,task 内部的 requirements[] 数组及其 requirementId 名称保持不变。GET /v3/customers/{customerId}/tasks,通过 POST .../tasks/{taskId}/submissions 提交作答。
围绕这个循环还有:
- 原始文件存储:
POST/GET/DELETE /v3/customers/{customerId}/documents(以及/{documentId}),使用您的 API key 一次性上传,然后在 submission 答案中引用 document id。它取代了所有的 upload-token 和 direct-upload 上传入口。 - 全新的读取:
GET /v3/tasks(商户级收件箱)、GET /v3/transfers/{transferId}/tasks、GET .../tasks/{taskId}/history、GET .../tasks/{taskId}/submissions(以及/{submissionId})。
- 答案类型:
profile、text、date、single_select、multi_select、boolean、attestation、document、resource_reference、absence。每个 requirement 的request对象会告诉您它期望的类型。 - 一次 submission 必须回答当前轮次中每一个可作答的 requirement,且携带您所读取的准确
taskRevision。部分回答将被拒绝。 profile类答案直写生效:它们通过常规校验路径更新客户档案,并立即重新评估引用相同 intake 工作的每个 capability。所有 requirement 都已满足的同级 intake task 会自动关闭。- Requirement 可能构成互斥分组(
alternativeKey):请仅在该组中提交其中之一。 changes_requested会递增remediationRound并携带reviewFeedback。请重新读取 task,并用一个新的幂等性 key再次提交。- 托管验证 URL 仅出现在客户维度的 task 详情(
GET /v3/customers/{customerId}/tasks/{taskId})上,且仅在会话可作答时出现;列表和GET /v3/tasks/{taskId}有意不含 URL。 - 服务条款也是一个 task:
openTaskIds可能包含category: "terms_of_service"的 task,其托管接受页以同样方式提供(仅客户维度详情)。普通 submission 不能用来接受条款,KYC 通过也永远不代表接受条款。 - Task 是按 capability 划分的,因此「相同」的问询(例如地址证明)可能每个 capability 都会出现一次。请在 UI 中按 requirement
key去重。 - 没有翻译层:向
/v1/documents发送内容不会解锁 v3 capability。一旦客户处于 v3 上,请通过 task 驱动所有问询。
2.4 Accounts 与 wallets
创建时按
origin 加 type 区分。issued 银行 account 使用单个 method;external 银行 account 使用 methods 数组(在此发送 method 会被拒绝):
- issued 银行 account 上的
country是可选的(按 method 有默认值);external 银行 account 需明确提供。钱包类 account 完全不携带国家/地区。 - 在 issued 银行 account 上
settlement.accountId是必需的:它指定接收该银行 account 存款结算资金的 issued 钱包 account。 - issued account 暴露
details(IBAN 或 routing 加 account 或 address)、带版本的routing(存款坐标可能会更新,请始终展示最新一次读取)、fees、balances。 - 网络:
polygon、ethereum、base、arbitrum、optimism、bsc、avalanche。 - capability 门槛仅适用于 issued account:在非 ready 状态的 capability 下创建将失败并返回一个 capability 类错误码,请先请求该 capability(见 2.2)。external account 不需要 capability(也不需要客户批准);它只会经过请求 schema 与银行详情校验。
- issued 银行 account 出生时状态为
provisioning,details: null。请轮询该 account 或监听account.status_changed直至ready。 DELETE归档,绝不硬删除。被进行中的 transfer 引用的 account 会返回409 account_has_active_transfers。在这些 transfer 达到终态后再重试。- v3 新增:Rules,即 issued 钱包 account 上的常设指令(
POST/GET /v3/customers/{customerId}/rules、GET/PATCH/DELETE .../rules/{ruleId}),可自动将到账资金归集到另一个 account 或钱包 destination。v1 或 v2 无等效功能。
2.5 Recipients 与 destinations
v2 没有 recipient 概念。如果您在 v2 上向第三方付款,这是新增接口,不是改名。
- Recipient 即「谁」:
individual(姓与名)或business(公司名),并需要relationship(employee、contractor、vendor、subsidiary、merchant、customer、landlord、family、other)。recipient 与 destination 只用于第三方。一笔第一方付款完全不使用 recipient:请将 quote 的destinationId指向该客户自己的某个acc_account(见 2.6)。 - Destination 即「在哪里」:按 method 分类型,
sepa(iban,bic 可选)、ach或wire(routing 加 account)、swift(完整坐标加可选中间行)、spei(clabe)、pse、transfers_3_0(cbu)等,以及钱包 destination。每个 destination 都有自己的状态。请监听destination.status_changed。 - 法币 destination 在创建之前需要 recipient 完整的
address(街道、城市、邮编、国家/地区)。缺失部分会以422 recipient_address_required失败。钱包 destination 不需要地址,但需要顶层ownership(self_custodied,或custodial并附上托管机构名称)。 - 收款人姓名的准确性至关重要:接收银行按 account 的法定姓名进行匹配。请发送准确的法定姓与名或公司名,而非展示昵称。
- 各 method 的 destination 字段 schema 详见 OpenAPI 规范。
2.6 Quotes 与 transfers
destinationId接受acc_(客户自有 account)或dst_(recipient destination)id。法币出资的 quote(payin)必须指向acc_account,指向dst_总是表示 payout(否则返回422 quote_direction_invalid)。- quote 与 transfer 上的
externalId是非唯一的关联引用(在读取时回显,可在列表中过滤)。按环境唯一性的规则(见 2.1)仅适用于客户的externalId。 - 每个 quote 必须在
expiresAt之前恰好执行一次。已过期的 quote 会以409 quote_expired失败;二次执行会以409 quote_already_executed失败(其问题响应携带已有的transferId)。 - 尚未支持 transfer 取消:
POST .../cancel在任何状态下都会返回409 transfer_not_cancelable。目前的canceled状态 transfer 来自 payin 未在出资窗口内到账,而非此端点。 - payin 从
awaiting_funds开始:请向付款人展示GET .../instructions,包括银行坐标加参考号或备注码(法币)或存款地址(加密货币)。参考号是匹配存款的方式。请始终展示它。 state加stateDetail提供机器可读的子状态;action_required表示附加了一个合规 task(openTaskIds、GET .../tasks),请通过 submission 作答。- 在 issued account 上检测到的入金存款会作为
origin: "inbound_deposit"的 transfer 出现(而非"quoted")。 - 支付网络引用统一在
references下:transactionHash、traceNumber、imad、uetr、explorerUrl、returnedTransferId。
两条迁移警告:
- Transfer 不跨版本。 在 v1 或 v2 上创建的 transfer 无法通过 v3 读取。列表会略过它们,
GET /v3/transfers/{transferId}返回 404。请先切换_创建_路径,并保留 v1 的读取路径,直到那些 transfer 到达终态后再移除。 - 不支持币种兑换。
stablecoin_move要求进出币种相同:USDC 到 USDT 会以422 recipient_destination_invalid失败,并附带currency_mismatch字段错误。两侧必须同网络,不做跨链,且钱包到钱包目前仅支持零费递送:任何平台费或开发者费不为零的 quote 会以422 amount_not_deliverable失败。
2.7 Webhooks
事件目录:
customer.created、customer.updated、customer.archived、capability.created、capability.status_changed、application.status_changed、recipient.status_changed、destination.status_changed、account.created、account.status_changed、account.details_changed、transfer.created、transfer.state_changed、api.deprecation。
GET /v3/webhooks/portal返回一个托管管理门户的 URL,可用于投递日志、重试和手动重放。transfer.created当前仍以旧的 v1 载荷形态投递(v3 信封将在 v1 webhook 下线时启用)。请将其单纯视为提示并 GET 该 transfer;不要依赖其载荷来构建业务。- 事件是提示:收到后请 GET 该资源,并基于读取结果行动。绝不要基于事件载荷或投递顺序构建状态。投递至少一次,可能延迟或乱序。请按事件 id 去重,并使用每个列表的包含式
updatedAfter过滤器补齐遗漏的事件。 - 请订阅
api.deprecation,这是版本下线的机器通道。 - 目前没有
task.*事件:提交之后请轮询 task 或其父资源。
2.8 沙箱
同一基础 URL;沙箱 API key 决定环境。
v3 沙箱端到端地模拟审核循环:创建一个 task,向其提交,
review 为 accepted 或 rejected,观察 capability 解锁。在上线前先演练您的补救 UX。沙箱创建的 task 与在已请求的 capability 上出现的常规 intake task 都可以这样审核;与生产一致,不会有 task webhook,请轮询(见 2.7)。
2.9 没有 v3 替代品的遗留端点
以下端点没有 v3 替代品。大多数保留在 v1 上不变(继续沿用您现有的调用);有两个已经完全下线(见「处置」):
其他所有的公开 v1 或 v2 端点,都在上面的映射表中出现过。
2.10 建议的迁移顺序
每一步都可独立发布;v1 或 v2 与 v3 可以在同一客户群上并行运行。请先用您的沙箱 key 演练每一步(见 2.8),再到生产环境重复执行。1
基础设施
对所有具有副作用的请求携带
Idempotency-Key(POST、PATCH、PUT、DELETE;沙箱端点除外);金额使用字符串;实现游标分页辅助函数。2
Webhooks
按事件注册 v3 端点,包括
api.deprecation。v1 的单端点配置是独立接口,请保留原样;两者会并行运行,直到第 9 步的收尾阶段。3
档案补全
使用您掌握的完整档案对
PATCH /v3/customers/{id} 发起写入(v1 采集的字段少于 v3 暴露的),并重新设置 metadata。请刻意将其作为每个客户的第一次 v3 写入:这会在净化视图永久固化之前先补齐它(见 2.1)。4
读取
将 customer、capability 与 account 的读取指向 v3;按 1.3 改写客户状态逻辑。仅在第 3 步之后才安全,否则未补全的读取会返回缺失了旧版无效字段的结果。
5
入驻写入
通过
POST /v3/customers 创建;用请求 capability 取代 /rails、/banks 或 application;构建 task 循环(这是新增 UI 工作量最大的部分,tasks-preview 有助于提前展示问询)。从此起,请对使用 v3 的客户不再向 /v1/documents 提交,那不会解锁 capability(见 2.3)。6
Accounts
通过 v3 开立;将导入迁移到
origin: external。7
Payouts
先建立 recipient 和 destination,然后进行 quote 与 transfer。
8
Payins
进行 quote、transfer、instructions;持续展示参考号。
9
收尾
Transfer 不跨版本(见 2.6)。请为在 v1 或 v2 上创建的 transfer 保留 v1 或 v2 的读取路径以及 v1 的 webhook 端点,双读直至它们到达终态,然后再下线旧客户端与 v1 的 webhook 配置。
2.11 常见陷阱清单
- 每个逻辑操作使用新的 UUID,与您的任务一起持久化并在重试时复用;绝不要用相同的 key 搭配已变更的请求体(
409 idempotency_conflict)。沙箱端点无需该请求头。 - 在进行任何其他 v3 写入之前先补全已存在的客户档案(
PATCH完整档案,重新设置metadata,它不会被延续),第一次 v3 写入会使净化视图永久固化。 -
externalId按环境唯一,归档并不释放它,若您计划复用,请在DELETE前用PATCH清空。 - 没有客户
status字段,请按 capability 推导就绪状态。 -
action_required与in_review都表示存在一个开放的 task。 - submission 受审核约束(提交并不等于解锁),且必须以准确的
taskRevision回答每一个可作答的 requirement。不匹配时请重新读取并重建。 - 没有
task.*webhook,每次提交后请轮询 task(或其父资源)。 -
changes_requested的重试意味着重新读取 task、给出新答案,并使用新的幂等性 key。 - 向
/v1/documents发送内容永远不会解锁 v3 capability,一旦客户切到 task 上,请通过 task 驱动每一个问询。 - 即便一个 capability 已是
ready,仍可能出现新的 task,请在入驻之后依然保持 task 循环运转,而不仅仅是在入驻期间。 - capability 的
cancel仅可在pending或restricted且无阻塞资源时使用(409 capability_not_cancelable);取消后如需再次请求,需用新的幂等性 key 全新创建。 - 在其下开立 account 或对其报价之前,capability 必须处于
ready。 - quote 只能在一侧提供金额;没有方向字段;必须在
expiresAt之前恰好执行一次(409 quote_expired或409 quote_already_executed)。 - 稳定币划转仅支持同币种、同网络(USDC 到 USDT 会
422失败);钱包到钱包目前仅支持零费递送。 -
DELETE归档,绝不硬删除。account 删除会被进行中的 transfer 阻止(409 account_has_active_transfers);客户删除还会被任何未归档的 account 阻止(409 customer_has_active_resources,blockingResources[]会列出这些资源)。 - v1 或 v2 的 transfer 对 v3 读取不可见(列表略过、GET 返回 404),请双读至清空,然后再移除旧路径。
- 存款
routing与 instructions 可能会变化,请始终展示最新一次 GET,并始终展示参考号。 - webhook 是提示;GET 才是事实,请按事件 id 去重,并用
updatedAfter找回遗漏事件。