> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# FATF Travel Rule API：VASP 合规

> Didit 为加密 VASP 和 PSP 提供 FATF Travel Rule 消息传递：交换汇款人和收款人数据，跟踪协议并自动修复。每次交换 $0.02。

Didit 为虚拟资产转账运行 FATF/欧盟《资金转账条例》（TFR）规定的汇款人与收款人信息交换：它会寻找能够触达交易对手 VASP 的通道，通过该通道协商所需数据，通过包含 13 个状态的生命周期跟踪交换过程，并在交换结束时通知您，以便您在链上放行资金。

## 概述

FATF Travel Rule（建议 16）在欧盟通过《资金转账条例》（Transfer of Funds Regulation，TFR）实施，要求发起方虚拟资产服务提供商（**originator VASP**）在虚拟资产转账结算之前，收集汇款人和收款人信息，并将其传输给接收方虚拟资产服务提供商（**beneficiary VASP**）。

与大多数司法辖区不同，欧盟 TFR 适用**没有最低金额门槛**：每一笔加密货币转账都在适用范围内，而不仅限于超过报告门槛的转账。

Didit 的 Travel Rule 产品是一项托管式的信息交换服务，而不仅仅是一个数据字段：

* 您以 `transaction_category: "travelRule"` 提交转账。
* Didit 按优先级顺序尝试各条通道以联系交易对手 VASP，并在第一条能够联系到对方的通道上完成信息交换。如果对方返回了收款人姓名，Didit 会将其与您发送的收款人姓名进行匹配。
* 只有当交换达到 `COMPLETED` 状态后，您才应在链上广播该转账。
* 随后，您将链上交易哈希回报给 Didit，使交换以 `FINISHED` 状态关闭。

本版本共提供七条通道，严格按优先级顺序尝试，第一条能够联系到交易对手的通道胜出：

| 优先级 | 通道 | 工作方式 |
| - | - | - |
| 1 | `INTERNAL` | 当另一位 Didit 客户曾通过签名消息或测试充值证明其控制该收款钱包时使用。Didit 会在该客户的应用中创建一笔对应的入账转账，并完全在平台内协商交换，不发起任何对外网络调用。即时完成。 |
| 2 | `TRP` | 与开放的 Travel Rule 协议（TRP / OpenVASP）原生集成。当转账包含 [Travel Address](#travel-address)、目标钱包的地址簿条目中存有 Travel Address，或交易对手 VASP 在[目录](#目录)中解析到带有 TRP endpoint 时使用。支持 [TRP 资产标识符](#trp-资产标识符)中列出的资产。Didit 以机器对机器的方式交换汇款人和收款人数据，无需邮件往返。 |
| 3 | `GTR` | 全球 Travel Rule（GTR）网络。交易对手通过显式的 `counterparty_gtr_vasp_code` 或带有该代码的目录条目解析。在转账等待期间，Didit 从 GTR 的响应中读取交易对手的回复，或每 5 分钟轮询一次请求状态。 |
| 4 | `TRUST` | TRUST 网络。交易对手通过 `counterparty_trust_membership_id` 或带有该 ID 的目录条目解析。仅支持发送：TRUST 不会返回 Didit 能够读取的任何内容，因此转账会一直处于 `AWAITING_COUNTERPARTY` 状态，直至过期。 |
| 5 | `VERIFYVASP` | VerifyVASP 网络。交易对手通过 `counterparty_verifyvasp_id` 或带有该 ID 的目录条目解析。Didit 每 5 分钟轮询一次验证状态，以读取交易对手的回复。 |
| 6 | `SYGNA` | Sygna Bridge 网络（2026 年被 VerifyVASP 收购，仍为现有成员提供服务）。Sygna 协议的原生实现，采用 ECIES 加密的 IVMS101，并附带已签名的权限请求。交易对手通过 `counterparty_sygna_vasp_code` 或带有该代码的目录条目解析。交易对手的接受或拒绝回复会完成或拒绝转账：Didit 每 5 分钟轮询一次转账状态，并且在 Didit 的公共 TRP 主机上线后，也会通过 Sygna 回调接收该回复。 |
| 7 | `EMAIL` | 备用通道，适用于没有任何协议或网络通道能够联系到交易对手的情况。此时您需在 `counterparty_vasp_email` 中填写其合规联系人，或交易对手的目录条目中含有[合规联系人](#联系人)。Didit 会向该联系人发送一次性领取链接，使其无需 Didit 账户即可确认或拒绝交换。这是应对“日出问题”（即尚无法通过任何网络联系到的交易对手）的主力方案。 |

网络通道（`GTR`、`TRUST`、`VERIFYVASP`、`SYGNA`）需要发送方拥有网络成员身份，目前该成员身份必须是**您自己的**。
在控制台的 **Marketplace**（Travel Rule 网络类别）中连接您的成员凭据，您的转账将以您自己的网络身份发出。

**Didit 目前未持有这四个网络的任何平台成员身份，正在作为提供方接入这些网络。**
在接入完成之前，这四个网络的 `platform_available` 均为 `false`，设置中的 `didit` 模式（通过 Didit 的平台成员身份路由）对任何网络都不可用，`disabled_networks` 也没有可关闭的内容。
您自行连接的成员身份始终优先于平台成员身份。
默认不包含任何网络成员身份，GTR 也不例外。
启用 Travel Rule 并不会激活 GTR、TRUST、VerifyVASP 或 Sygna。
请查看[获取 Travel Rule 设置](https://docs.didit.me/zh/management-api/travel-rule/get-settings.md)，了解应用当前生效的 `networks` 模式；并查看 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)，了解各网络目前能够联系到的范围。
如果四个网络均返回 `unavailable`，则没有任何网络能够路由交换：请在 **Marketplace** 中连接您自己的成员身份，或改用可联系到的 `INTERNAL`、`TRP` 或 `EMAIL` 交易对手。

保存成员身份时，其参与方代码必须是您自己的。如果该代码是 Didit 在该网络上的代码，或是其他应用的成员身份已占用的代码，保存将返回 `400`。
GTR 成员身份需要提供其 ed25519 私钥（base64 格式），且该私钥对应的公钥必须已在 GTR 门户中注册：该私钥用于加密您发送的个人数据，并解密您接收的数据。
VerifyVASP 成员身份指向您自己的 VerifyVASP Enclave：其 URL 为您的 Enclave 地址，其 token 为您的 Enclave 向 Didit 出示的 token。

即使使用您自己的成员身份，只有当网络通道的 `beneficiary_data` 携带该网络上交易对手的 ID（`counterparty_gtr_vasp_code`、`counterparty_trust_membership_id`、`counterparty_verifyvasp_id` 或 `counterparty_sygna_vasp_code`），或交易对手的目录条目在该网络上列出了其 ID（参见[路由](#路由)）时，该网络通道才会路由转账。

`platform_available` 表示是否已配置平台成员身份，而不是实时的网络健康检查。同样，`connected` 表示您的成员凭据已保存并通过本地验证；首次实时交换时才会在网络上验证这些凭据。`didit` 或 `own` 模式并不保证交易对手可以联系到，也不保证成功投递。

凭借你自己的会员身份，Didit 会从网络的响应中读取交易对手通过 `GTR`、`VERIFYVASP` 和 `SYGNA` 发送的回复，或每 5 分钟轮询一次网络，由此完成或拒绝该转账。
`TRUST` 仅用于发送：通过它发出的转账会保持在 `AWAITING_COUNTERPARTY` 状态，直到 `confirmation_timeout_hours` 指定的时长结束，随后转为 `EXPIRED`，并适用你的 `timeout_outcome`。
接收其他 VASP 通过网络发来的转账功能尚未开通，也尚未在实际网络上完成过任何网络交换。详见 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)。

CODE（韩国联盟）、TRISA、Notabene 和 Veriscope 等其他网络已在控制台的 **Marketplace** 中以“申请访问”卡片的形式列出。
Didit 尚未与它们集成，因此目前任何转账都无法通过这些网络路由。

当七条通道都无法识别交易对手时，Didit 会在放弃之前再执行一个识别步骤：区块链分析归因。
系统会将目标钱包与区块链分析数据进行比对；若该钱包属于已知的交易所或托管机构，Didit 会在转账上记录归因得到的 VASP，将其添加到[目录](#目录)中，并使用新名称重新运行一次通道。
完整行为和定价详见[区块链分析归因](#区块链分析归属)。

若转账的目标为[声明为自托管钱包](#已声明的自托管钱包)的地址，则会跳过全部七条通道，因为不存在可与之交换信息的交易对手 VASP，并直接进入终端用户的钱包所有权证明环节。

> [!NOTE]
>
> Didit 还运营着交易对手一侧的交换服务：供其他 VASP 调用以联系你的公开的 TRP 身份和查询端点、回调端点以及电子邮件取件流程。
> 你无需自行调用这些接口，它们由 Didit 统一托管。但 [Travel Rule 互操作性](https://docs.didit.me/zh/transaction-monitoring/travel-rule-interoperability.md)页面记录了其中的每一项，方便交易对手 VASP 与你对接。
> 你只能通过本页所述的 `/v3/` 端点使用 Travel Rule。

## 提交转账时会执行什么

检查在一次 `POST /v3/transactions/` 调用内完成：您收到响应时，筛查已经运行，转账已经存储，规则也已评估完毕。
信息交换本身在响应之后才开始：Didit 在您的请求之外选择通道并联系交易对手，因此任何网络或交易对手都无法拖慢或导致创建失败。
此后的每次状态变更都会以 [`travel_rule.status.updated`](#webhook) Webhook 的形式送达，并注明所用通道；Travel Rule 转账端点返回的转账对象包含 [`delivery`](#投递与重试) 字段，用于显示消息是否送达。
没有需要轮询的流程；异步部分包括信息交换、交易对手的回复，以及终端用户的操作。

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant You as Your platform
    participant Didit as Didit API
    participant CP as Counterparty VASP
    participant User as Your customer

    You->>Didit: POST /v3/transactions/ (travelRule)
    Note over Didit: Validate + bind subject + blocklist check
    Note over Didit: Device & IP enrichment
    Note over Didit: Crypto screening (when crypto + monitoring on)
    Note over Didit: Threshold check -> transfer stored (AWAITING_COUNTERPARTY)
    Note over Didit: Rules engine (EU TFR bundle + your rules)
    Didit--)You: transaction.created webhook
    Didit-->>You: Transaction + travel_rule.status + action_required
    Note over Didit: After the response: rails in priority order -> analytics attribution
    Didit->>CP: Exchange over the first reachable rail
    Didit--)You: travel_rule.status.updated (each status change)
    User->>Didit: Proves wallet control (when action_required)
    CP-->>Didit: Confirms beneficiary data
    Didit--)You: travel_rule.status.updated (COMPLETED)
    You->>You: Broadcast on-chain
    You->>Didit: PATCH .../travel-rule/ { payment_txn_id }
    Didit--)You: travel_rule.status.updated (FINISHED)
```

分步说明：

1. **验证与主体绑定。** 系统会校验 payload。
   您应用中已使用的 `transaction_id` 会被以 `400` 拒绝；若两个相同 id 的请求发生竞争，其中一个会收到 `409`。
   `subject` 通过 `vendor_data` 关联到您的用户或企业档案，以便该转账显示在该档案下用于监控。
   列入黑名单的主体或交易对手 `vendor_data` 会被以 `400` 拒绝，且不会创建任何交易。
   黑名单中的钱包、银行账户、IP 与设备会在第 6 步检查，并在通道路由运行之后于该步骤拒绝交易。
2. **设备与网络信息补充。** 参与方 IP 会进行地理信息补充，任何设备上下文（通过 [SDK 提交](https://docs.didit.me/zh/transaction-monitoring/sdk-transaction-submission.md) 自动获取）都会被规范化后写入设备图谱。这与 KYC 会话写入的图谱相同，因此"按设备关联"可同时用于入驻与交易。
3. **加密货币筛查。** 对于启用了监控的加密货币转账，相关钱包（或链上哈希）会在任何信息交换之前，通过区块链分析进行筛查，包括制裁风险敞口、混币器关联和被盗资金。参见 [加密货币与钱包筛查](https://docs.didit.me/zh/transaction-monitoring/aml-screening.md)。
4. **Travel Rule 转账。** 信息交换引擎检查阈值，并认可 [自托管钱包声明](#已声明的自托管钱包)，然后存储转账：低于您阈值的转账为 `NOT_APPLICABLE`；已声明的自托管钱包为 `UNCONFIRMED_OWNERSHIP`；对方钱包为您本人已验证的钱包时为 `COMPLETED`；其他情况为 `AWAITING_COUNTERPARTY`，此时其通道仍为空。
   每次状态变更都会排入一个 `travel_rule.status.updated` 事件，Didit 会在交易的 `transaction.created`（第 7 步）之后发送该事件。
5. **钱包所有权组件生成。** 如果转账需要终端用户证明控制权，且 `auto_wallet_verification` 已开启，则会生成 [钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md) 会话，并在 `action_required` 中返回。
6. **规则评估。** 所有启用的规则都会运行：[EU TFR 规则包](#eu-tfr-规则包) 对第 4 步设定的转账状态作出反应；您的自定义规则读取第 3 步的筛查输出；规则可以挂起交易（`IN_REVIEW`）、拒绝交易，或要求 [加强验证](https://docs.didit.me/zh/transaction-monitoring/sdk-transaction-submission.md#用户所需操作)（`AWAITING_USER`）。
7. **计费与通知。** 出站交易按一条计费项计费：若启动了信息交换，则按 [Travel Rule 计费项](#定价) 计费，否则按 Transaction Screening 计费。入账转账不收取任何费用，沙盒应用永不计费。
   系统返回响应，并向您的 Webhook 目标发送 `transaction.created`，随后发送第 4 步的 `travel_rule.status.updated` 事件。
8. **路由，在响应之后。** Didit 按优先级依次尝试 [七条通道](#概述)；若均无法解析，则 [区块链分析归因](#区块链分析归属) 会识别交易对手，通道随后重新运行一次。第一条可达的通道承载信息交换：在交易对手回复期间，转账保持在该通道上的 `AWAITING_COUNTERPARTY`；若匹配到 `INTERNAL` 交易对手，则立即结算（`COMPLETED` 或 `COUNTERPARTY_MISMATCHED_DATA`）；若没有任何通道能够连接交易对手，则变为 `COUNTERPARTY_VASP_NOT_FOUND`。每次变更都会触发 `travel_rule.status.updated`，且只要状态或通道发生变化，您的规则就会再次运行。路由后仍处于 `COUNTERPARTY_VASP_NOT_FOUND` 的转账，此时会生成钱包所有权组件会话（需开启 `auto_wallet_verification`）；您可以通过 [获取交易](https://docs.didit.me/zh/management-api/transactions/get.md) 从交易的 `action_required` 中读取该会话。

### 每个步骤对应的义务

| 流程步骤 | 所履行的义务 |
| - | - |
| 您在 `originator_data` 中提供的汇款人数据 | 汇款人信息准确（FATF R.16；EU TFR 第 14 条）。在承载个人数据的通道（`INTERNAL`、`TRP` 及网络通道）上，当主体是拥有已通过 Didit KYC 验证的用户时，Didit 传输已验证身份（参见 [已验证数据与来源](#已验证数据与来源)），否则完全按原样传输您发送的 `originator_data`。 |
| 通道在结算前进行信息交换 | 汇款人与收款人信息在转账结算**前**传输给交易对手 VASP（FATF R.16；EU TFR 第 14–16 条） |
| 收款人姓名匹配 | 核实收款人数据与目的地一致（EU TFR 第 16 条，收款方 CASP 义务） |
| 自托管钱包所有权证明 | EU TFR 自托管钱包义务：对未托管地址验证所有权，而非与 VASP 进行信息交换 |
| 加密货币筛查 + EU TFR 规则包 | 针对信息缺失或不完整转账的基于风险的程序，以及制裁风险敞口控制 |
| 广播前的 `COMPLETED` 门控 | 信息交换完成之前不会释放资金，这正是该规则的核心 |
| 附链上哈希与时间线的 `FINISHED` 收尾 | 记录留存：每笔转账都保存完整的协商时间线，以便审计 |

> [!NOTE]
>
> **测试说明。** 沙盒应用会跳过规则评估和计费，且不发送 `transaction.created` 或 `transaction.status.updated` Webhook。除非到期的 Travel Rule 转账应用了您的 `timeout_outcome`，沙盒交易将保持 `APPROVED`，且不收取任何费用。
> 启用 Travel Rule 后，Travel Rule 信息交换引擎本身也会在沙盒应用上运行，且仅与一个交易对手交互：所有**出站**交换都会发往 `TEST_VASP` 通道上的 [Didit 测试 VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md)。它会在 Didit 内部以您所选的场景作出回应，因此转账可能完成、被拒绝、不匹配、变为不可达、过期，或等待钱包所有权验证。
> 任何合规消息都不会到达真实的交易对手 VASP，`travel_rule.status.updated` Webhook 会以 `environment: sandbox` 投递。
> **入账**方面，测试 VASP 可以向您发送转账（[触发测试 VASP 入账转账](https://docs.didit.me/zh/management-api/travel-rule/trigger-test-vasp-inbound.md)），注册（`POST /v3/travel-rule/inbound/`）会像在正式应用上一样与您的钱包地址簿结算，结果为 `ON_HOLD`、`UNCONFIRMED_OWNERSHIP`、`COUNTERPARTY_MISMATCHED_DATA`，或 `COMPLETED` 后再 `FINISHED`。
> 参见 [在沙盒中测试 Travel Rule](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md)。

## 生命周期

每笔转账的 `status` 取自 13 个值之一。
常规情况下，交换从 `AWAITING_COUNTERPARTY` 开始。当金额低于你的 `threshold_amount`，或闪电支付请求未指明收款方时（参见 [Lightning](#lightning)），交换从 `NOT_APPLICABLE` 开始。当目标钱包已被[声明为自托管钱包](#已声明的自托管钱包)时，交换从 `UNCONFIRMED_OWNERSHIP` 开始。交换最终进入以下终态之一：`FINISHED`、`CANCELLED` 或 `EXPIRED`。

转账拥有自己的状态机，与底层交易的审核状态**相互独立**。
交换状态本身从不改变交易状态，唯一例外是过期：过期时会应用你配置的 `timeout_outcome`（若适用，则应用[sunrise 或无响应策略](#sunrise-与无响应策略)）。
你的规则会在每次状态转换时重新运行，因此 [EU TFR 规则](#eu-tfr-规则包)或任何基于 `travel_rule_status` 的规则都可能移动交易，例如将其转为 `IN_REVIEW`。
需要终端用户证明钱包控制权的转账，会在交易上添加一个 `action_required` 块，但其 `status` 保持为你的规则所设定的状态。
请从 `travel_rule.status` 读取交换状态；从交易自身的 `status` 读取放行、挂起或拒绝的判定结果。

每次状态转换（以及每个被动上报的状态）也会同步写入交易的自定义值，以便[规则](https://docs.didit.me/zh/transaction-monitoring/rules.md)据此设置条件：`custom_values.travel_rule_status`（规范化状态）、`custom_values.travel_rule_rail`（承载此次交换的通道，选定前为空），以及 `custom_values.travel_rule_regime`（由你的[设置](#设置)中的 `jurisdiction` 推导：`SG` → `singapore`，`GB` → `uk`，任何欧盟或欧洲经济区成员代码（如 `DE` 或 `FR`）→ `eu`；未映射的代码会被转为小写，例如 `US` → `us`。你自行提交的 `props.travel_rule_regime` 始终优先于推导出的值）。

| 状态 | 含义 |
| - | - |
| `AWAITING_COUNTERPARTY` | 转账正在等待交易对手：先是 Didit 选择通道并发送交换请求的阶段（创建之后立即开始），随后是等待对方回复的阶段。转账的[`delivery`](#投递与重试)显示消息是否送达。`TRUST` 只负责发送，因此通过该通道的转账会一直停留在此状态，直至过期。 |
| `UNCONFIRMED_OWNERSHIP` | 转账正在等待钱包控制权证明。当交易对手钱包在提交时（出站或入站）已被[声明为自托管](#已声明的自托管钱包)，或当入站转账匹配到的[地址簿条目](#地址簿)其所有权尚未验证时，会进入此状态。通过[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)或 [`POST .../travel-rule/ownership/`](#确认钱包归属) 推进。 |
| `ON_HOLD` | Didit 仅在入站转账上设置此状态：当[事后入账登记](#存款后的-sunrise-登记)或入站 `TRP` 查询的目标钱包不在你的[地址簿](#地址簿)中，或入站 `TRP` 查询正在等待你的决定（自动接受已关闭，或无法送达自动应答）时，会设置此状态。使用 [`POST .../travel-rule/ownership/`](#确认钱包归属) 解决。它本身不会改变交易状态；可选择启用的规则库中的 `travel-rule-pending-action` [规则](https://docs.didit.me/zh/transaction-monitoring/rules-library.md)会把此类交易移至 `AWAITING_USER`。 |
| `COMPLETED` | 交换已成功完成：交易对手（或在入站转账中为你）已批准，或 Didit 在该路径上执行的检查已通过（收款人姓名匹配、钱包所有权证明）。可以安全地在链上广播该转账。 |
| `FINISHED` | 交换已结束，并附有其链上交易哈希。对于出站转账，你通过 [`PATCH .../travel-rule/`](#发送转账) 上报哈希。对于入站转账，当存款已结算（[事后入账登记](#存款后的-sunrise-登记)）、汇款人确认 `TRP` 转账，或 `INTERNAL` 出站一方完成时，由 Didit 设置此状态。 |
| `CANCELLED` | 在任何非终态下通过 `action: "cancel"` 手动取消，或由入站 `TRP` 转账的汇款人取消。 |
| `EXPIRED` | 在 `AWAITING_COUNTERPARTY` 状态下，`confirmation_timeout_hours` 已到期。清理程序（每 15 分钟运行一次）会使转账过期，并将你配置的 `timeout_outcome` 应用于底层交易：`REJECT` 拒绝该交易，`HOLD` 将其转入审核，`PROCEED` 则保持不变。若适用，你的[sunrise 或无响应策略](#sunrise-与无响应策略)会替代 `timeout_outcome`。 |
| `COUNTERPARTY_MISMATCHED_DATA` | 收款人姓名与另一方姓名进行比对时未达到配置的 `name_matching_strictness`：另一方姓名可能是对方返回的姓名，也可能是匹配的地址簿条目的 `holder_name`。 |
| `COUNTERPARTY_VASP_NOT_FOUND` | 七条通道以及[区块链分析归因](#区块链分析归属)备用方案，均无法为此转账解析出可达的交易对手 VASP。任何被归因的实体仍会记录在转账和目录中。对于出站转账，当 `auto_wallet_verification` 开启时，Didit 会自动生成一个[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)会话，使转账仍可作为自托管钱包转账完成。`reason` 和 `next_actions` 说明原因，以及推动转账继续的操作：参见[转账为何卡住](#为什么转账会卡住)。 |
| `COUNTERPARTY_VASP_NOT_REACHABLE` | 已识别出交易对手 VASP，但所选通道无法完成投递：其 `TRP` 身份或查询端点无法访问；其报告的身份与 `counterparty_vasp_lei` 或 `counterparty_vasp_name` 不匹配；网络拒绝了消息，或六次尝试后仍未接收（参见[投递与重试](#投递与重试)）；或邮件服务商拒绝投递合规邮件。`reason` 用于区分这些情况，`next_actions` 说明推动转账继续的操作：参见[转账为何卡住](#为什么转账会卡住)。待其再次可达后，使用 `action: "resend"` 重试。 |
| `COUNTERPARTY_VASP_GENERAL_DECLINE` | 交易对手拒绝了此次交换：拒绝证明钱包所有权、拒绝通过邮件领取，或 `TRP` 或 Sygna 拒绝。 |
| `NOT_ENOUGH_COUNTERPARTY_DATA` | 入站 `TRP` 查询既未提供姓名也未提供地址来标识收款人，因此 Didit 无法将其与你的地址簿匹配。所有权端点和 `resend` 都不接受此类入站转账：你只能取消它，而取消并不会回应交易对手的查询。Didit 从不在出站转账上设置此状态；若你自己在某笔出站转账上记录了此状态，则 `action: "resend"` 会重新运行路由。 |
| `NOT_APPLICABLE` | 转账金额低于已配置的 `threshold_amount`，或出站的 [Lightning](#lightning) 付款请求未指明收款方（`chain_details.not_applicable_reason` 会说明原因），或者您在未启用 Travel Rule 的情况下自行记录了此状态（包括旧版 `UNKNOWN` 和 `EXEMPT` 值，见下文）。 |

> [!WARNING]
>
> **在 `COMPLETED` 之前切勿广播。**
> 如果转账处于 `AWAITING_COUNTERPARTY`、`UNCONFIRMED_OWNERSHIP` 或任何不匹配或被拒绝的状态，就在链上释放资金，这会使信息交换失去意义。
> 请以 `status == "COMPLETED"` 作为广播步骤的前置条件，然后使用返回的 `payment_txn_id` 调用 `PATCH .../travel-rule/`，将其切换为 `FINISHED`。

各状态转换概览（`CANCELLED` 还可通过 `action: "cancel"` 从每个非终态进入）：

```mermaid theme={null}
stateDiagram-v2
    [*] --> NOT_APPLICABLE: below threshold
    [*] --> UNCONFIRMED_OWNERSHIP: self-hosted wallet declared
    [*] --> AWAITING_COUNTERPARTY: transfer stored, routing queued
    [*] --> COMPLETED: your own verified wallet
    [*] --> ON_HOLD: inbound with no matching wallet entry
    [*] --> NOT_ENOUGH_COUNTERPARTY_DATA: inbound TRP inquiry names no beneficiary
    AWAITING_COUNTERPARTY --> COUNTERPARTY_MISMATCHED_DATA: INTERNAL rail, verified entry, name mismatch
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_FOUND: no rail resolved
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_REACHABLE: delivery failed
    AWAITING_COUNTERPARTY --> COMPLETED: counterparty confirmed, names match where checked
    AWAITING_COUNTERPARTY --> COUNTERPARTY_MISMATCHED_DATA: name match failed
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_GENERAL_DECLINE: counterparty declined
    AWAITING_COUNTERPARTY --> EXPIRED: confirmation timeout
    AWAITING_COUNTERPARTY --> COUNTERPARTY_VASP_NOT_REACHABLE: TRP inquiry not delivered
    UNCONFIRMED_OWNERSHIP --> COMPLETED: ownership proven, names match
    UNCONFIRMED_OWNERSHIP --> COUNTERPARTY_MISMATCHED_DATA: name match failed
    UNCONFIRMED_OWNERSHIP --> COUNTERPARTY_VASP_GENERAL_DECLINE: ownership denied
    ON_HOLD --> COMPLETED: you confirm the wallet, names match
    ON_HOLD --> COUNTERPARTY_MISMATCHED_DATA: you confirm the wallet, name mismatch
    ON_HOLD --> COUNTERPARTY_VASP_GENERAL_DECLINE: you deny the wallet
    COUNTERPARTY_MISMATCHED_DATA --> COMPLETED: you approve an inbound TRP inquiry
    COUNTERPARTY_MISMATCHED_DATA --> COUNTERPARTY_VASP_GENERAL_DECLINE: you decline an inbound TRP inquiry
    COUNTERPARTY_VASP_NOT_FOUND --> COMPLETED: wallet ownership proven in the widget
    COUNTERPARTY_VASP_NOT_FOUND --> AWAITING_COUNTERPARTY: action resend
    COUNTERPARTY_VASP_NOT_REACHABLE --> AWAITING_COUNTERPARTY: action resend
    COMPLETED --> FINISHED: payment_txn_id reported or deposit already settled
    FINISHED --> [*]
    EXPIRED --> [*]
```

### 投递与重试

Didit 会在引发该转账的请求之外发送转账所需的每条消息，并持续重试，直到对方接收为止。
通过 [完成、取消或重新发送](https://docs.didit.me/zh/management-api/travel-rule/update-transfer.md)、[确认所有权](https://docs.didit.me/zh/management-api/travel-rule/confirm-ownership.md) 和 [sunrise 注册](#存款后的-sunrise-登记) 返回的转账对象，会在 `delivery` 中携带最新一条消息的信息；交易中嵌入的 `travel_rule` 对象也包含该信息。如果转账从未需要投递消息，则其值为 `null`。

| 字段 | 含义 |
| - | - |
| `kind` | `SEND`（发送给交易对手的第一条消息）、`ANSWER`（您的接受或拒绝，须回复给发起询问的网络）、`TXID`（链上哈希，在您完成转账后发送）、`CANCEL`（您取消转账后发送）或 `POLL`（向提供此功能的网络发起的状态查询，转账等待交易对手期间每 5 分钟一次）。 |
| `status` | `QUEUED`、`SENDING`、`RETRYING`、`DELIVERED` 或 `FAILED`。 |
| `rail` | 承载该消息的通道；在 Didit 为首条消息选定通道之前为空。 |
| `attempts` | 目前已进行的尝试次数。 |
| `last_error` | 上一次尝试失败的原因，若无失败则为空。这是一条简短的说明，指明失败的步骤和故障类型，例如 `sygna permission request failed: HTTP 503` 或 `sygna status query failed: no answer in time`；当 GTR 返回错误时，会包含其自身的状态码和消息。响应正文永远不会包含在内。 |
| `next_attempt_at` | 消息处于 `QUEUED` 或 `RETRYING` 状态时，下一次尝试的执行时间。 |
| `delivered_at` | 对方接收该消息的时间。 |

对于网络可能仍会恢复的故障（超时未响应、连接失败、HTTP `408`、`429` 或 `5xx`），会在 30 秒、2 分钟、10 分钟、30 分钟和 1 小时后重试。
如果首条消息最终失败，即网络拒绝了它，或第六次尝试也失败了，转账会进入 `COUNTERPARTY_VASP_NOT_REACHABLE` 状态，失败原因（`last_error`）记录在其 `timeline` 中，您可以使用 `action: "resend"` 重试。
其他最终失败的消息会记录在 `timeline` 中。
`delivery` 永远不会取代转账的 `status`：请以 `status` 判断信息交换的状态，并使用 `delivery` 查看转账仍在等待的原因。

### 旧版状态值

如果您在本次发布之前就已在发送 `travel_rule_details.status`，这些值仍然有效，并会自动映射。
任何状态的驼峰式写法（例如 `counterpartyVaspNotFound`）在提交时以及 [规则](https://docs.didit.me/zh/transaction-monitoring/rules.md) 条件值中同样可以接受，并会规范为标准形式：

| 旧版值 | 映射为 |
| - | - |
| `UNKNOWN` | `NOT_APPLICABLE` |
| `COMPLIANT` | `COMPLETED` |
| `PENDING_ACTION` | `ON_HOLD` |
| `PENDING_COUNTERPARTY` | `AWAITING_COUNTERPARTY` |
| `FAILED` | `CANCELLED` |
| `EXEMPT` | `NOT_APPLICABLE` |

## 设置

请在每个应用中配置一次 VASP 资料和协商策略，使用 `GET`/`PUT /v3/travel-rule/settings/`。

```bash theme={null}
curl -X GET https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY"
```

```json theme={null}
{
  "is_enabled": false,
  "jurisdiction": "EU",
  "name_matching_strictness": "DEFAULT",
  "confirmation_timeout_hours": 48,
  "timeout_outcome": "HOLD",
  "threshold_amount": "0.00",
  "inbound_auto_accept": true,
  "allow_self_declaration": true,
  "allow_screenshot_proof": true,
  "auto_wallet_verification": true,
  "vasp_attribution_enabled": true,
  "counterparty_screening_enabled": false,
  "sunrise_policy": "",
  "sunrise_jurisdictions": ["CO", "KH", "VN"],
  "non_responsive_policy": "",
  "legal_name": "",
  "lei": "",
  "compliance_email": "",
  "is_discoverable": true,
  "travel_address": "",
  "disabled_networks": [],
  "networks": {
    "gtr": { "rail": "GTR", "mode": "unavailable", "platform_available": false, "connected": false },
    "trust": { "rail": "TRUST", "mode": "unavailable", "platform_available": false, "connected": false },
    "verifyvasp": { "rail": "VERIFYVASP", "mode": "unavailable", "platform_available": false, "connected": false },
    "sygna": { "rail": "SYGNA", "mode": "unavailable", "platform_available": false, "connected": false }
  }
}
```

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "is_enabled": true,
    "legal_name": "Origin CASP SL",
    "jurisdiction": "EU",
    "name_matching_strictness": "DEFAULT",
    "confirmation_timeout_hours": 48,
    "timeout_outcome": "HOLD",
    "is_discoverable": true
  }'
```

`PUT` 为部分更新，只需发送要修改的字段。

| 字段 | 类型 | 说明 |
| - | - | - |
| `is_enabled` | 布尔值 | 为此应用的出站 `travelRule` 交易启用托管协商流程。需要非空的 `legal_name`（已保存，或包含在同一请求中），否则请求返回 `400`。 |
| `legal_name` | 字符串 | 您的 VASP 法定名称。会在邮件领取页面向交易对手显示，并在 [VASP 目录](#目录)中返回。 |
| `lei` | 字符串 | 您的法人实体识别码（LEI），会在 VASP 目录中返回。 |
| `jurisdiction` | 字符串 | 您的 VASP 营业辖区代码，最多 8 个字符（默认值为 `"EU"`）。 |
| `name_matching_strictness` | 字符串 | `NONE`、`STRICT`、`CONTAINS`、`DEFAULT` 或 `FUZZY`，详见下文。 |
| `confirmation_timeout_hours` | 整数 | `AWAITING_COUNTERPARTY` 转账可等待的小时数，超时后自动过期。最小值为 `1`，默认值为 `48`。 |
| `timeout_outcome` | 字符串 | 转账过期时，底层交易的处理方式：`HOLD` 将其转入审核，`REJECT` 拒绝该交易，`PROCEED` 保持其状态不变。默认值为 `HOLD`。当适用时，`sunrise_policy` 和 `non_responsive_policy` 优先。 |
| `sunrise_policy` | 字符串 | `HOLD`、`REJECT`、`PROCEED`，或默认值 `""`（遵循 `timeout_outcome`）。当交易对手是 `sunrise_jurisdictions` 中所列辖区内的目录条目时，已过期转账的处理方式。参见[过渡期与无响应策略](#sunrise-与无响应策略)。 |
| `non_responsive_policy` | 字符串 | `HOLD`、`REJECT`、`PROCEED`，或默认值 `""`（遵循 `timeout_outcome`）。当交易对手是[响应情况](#响应能力)为 `UNRESPONSIVE` 的目录条目时，已过期转账的处理方式。两者同时适用时，`sunrise_policy` 先生效。 |
| `sunrise_jurisdictions` | 字符串数组 | 只读。Didit 视为处于 Travel Rule 过渡期的国家或地区，当前为 `["CO", "KH", "VN"]`。其他辖区遵循 `timeout_outcome`。 |
| `compliance_email` | 字符串 | VASP 资料的合规联系人邮箱。只有此地址通过邮件链接确认后，[Didit 网络](#didit-网络)才会列出您的资料；更改该地址需要重新确认。 |
| `is_discoverable` | 布尔值 | 您的 VASP 资料是否可以列入 [Didit 网络](#didit-网络)：其他 Didit 客户可通过 `GET /v3/travel-rule/vasps/` 找到，并通过 INTERNAL 通道联系。列入还需满足其他条件，设置中的 `didit_network` 会说明缺少哪些条件。默认值为 `true`。 |
| `inbound_auto_accept` | 布尔值 | 为 `true` 时，若入站 `TRP` 询问的收款钱包是您地址簿中已验证的条目且名称匹配，则自动批准。为 `false` 时，该询问会保持 `ON_HOLD` 状态，等待您的决定。其他入站路径不读取此设置。默认值为 `true`。 |
| `allow_self_declaration` | 布尔值 | 为 `true` 时，[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)会提供自我声明作为证明方式，并且您可以使用 `self_declared: true` 创建[钱包地址簿](#地址簿)条目。默认值为 `true`。 |
| `allow_screenshot_proof` | 布尔值 | 为 `true` 时，[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)会提供上传截图作为所有权证据。截图本身永远无法验证钱包，您的合规团队需要审核并确认该转账。其证明强度低于加密签名。如果您的政策要求签名级别的证明，请将其关闭。默认值为 `true`。 |
| `auto_wallet_verification` | 布尔值 | 为 `true` 时，需要终端用户证明钱包控制权的出站转账会自动创建一个与该交易绑定的[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)会话，并将该会话显示在交易的 `action_required` 块中。Didit SDK 会在[客户端提交](https://docs.didit.me/zh/transaction-monitoring/sdk-transaction-submission.md)时自动启动它。默认值为 `true`。 |
| `counterparty_screening_enabled` | 布尔值 | 可选启用的交易对手 VASP 制裁和负面新闻[筛查](#交易对手筛查)，按每次筛查以公司 AML 筛查价格计费。为 `true` 时，Didit 会在出站交换开始时筛查该交换所联系的交易对手 VASP，并复用 30 天内的筛查结果。默认值为 `false`：不进行筛查，也不计费。 |
| `vasp_attribution_enabled` | 布尔值 | 为 `true` 时，无法通过任何通道路由的出站转账会经由[区块链分析](#区块链分析归属)进行归属：目标钱包会被解析到其所属 VASP，该实体会被添加到目录中，并使用归属名称重新运行一次各路由通道。默认值为 `true`。 |
| `threshold_amount` | 字符串（小数） | 触发托管交换的最低转账金额，以应用的默认币种计。Didit 会将其与转账金额换算为该币种后的值（或与您发送的 `amount_in_default_currency`）比较，而不是与转账自身币种的金额比较。低于该金额的转账直接解析为 `NOT_APPLICABLE`，且从不进行协商。如果 Didit 无法换算金额，则不应用该阈值，转账将直接交换。沙盒应用不换算金额，因此仅当您发送 `amount_in_default_currency` 时阈值才会生效。默认值为 `0.00`，与欧盟 TFR 不设最低金额的框架一致，因此每笔转账无论金额大小都会被检查。 |
| `ownership_validation_mode` | 字符串 | `FULL`、`SIMPLIFIED` 或 `AUTOMATED`，即验证钱包所有权所需的证明程度。参见[所有权验证模式](#所有权验证模式)。默认值为 `AUTOMATED`。 |
| `simplified_validation_threshold` | 字符串（小数） | 在 `SIMPLIFIED` 模式下，金额低于此值的转账接受自我声明。默认值为 `0.00`。 |
| `data_sharing_fields` | 对象 | 您按实体类型针对自身用户发送的字段。参见[数据共享](#数据共享)。 |
| `matching_rules` | 对象 | 按实体类型，比较交易对手数据中各字段时的严格程度。参见[逐字段匹配](#按字段匹配)。 |
| `disabled_networks` | string\[] | 您的交易**不得**通过 Didit 的平台会员资格路由的网络通道（`GTR`、`TRUST`、`VERIFYVASP`、`SYGNA`）。您为某网络连接了自己的会员资格（控制台 Marketplace）时，该网络不受此列表影响。Didit 目前尚未持有平台会员资格，因此该列表当前没有路由效果（列出的通道会报告 `mode: off`）。默认值 `[]`。 |
| `networks` | object | 只读。按 `gtr` / `trust` / `verifyvasp` / `sygna` 索引的各网络状态：`{rail, mode, platform_available, connected}`。其中 `mode` 为 `own`（您已连接的会员资格）、`didit`（Didit 的平台会员资格，目前任何网络均不可用）、`off`（您已禁用）或 `unavailable`。 |
| `travel_address` | string | 只读。您的 VASP 自身的 Travel Address，即其他 VASP 用于联系您的已编码入站 `TRP` 询问 endpoint。启用 Travel Rule 后填充，例如 `ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeS5uNDp3dGW3bweBWLKws442chRsyNxmmJnyWc7frxjjSeABVsYR11UuBePkw6E`。 |

### 姓名匹配严格度

| 值 | 行为 |
| - | - |
| `NONE` | 接受任何收款人姓名，不进行比较。 |
| `STRICT` | 两个姓名标准化后的词元集合必须完全相同。 |
| `CONTAINS` | 一个姓名的所有词都出现在另一个姓名中，无论词数多少。 |
| `DEFAULT` | 较短姓名的每个词元都必须出现在较长姓名中（子集匹配）。除非两个姓名完全相同，否则较短姓名至少需要两个词元。 |
| `FUZZY` | 完整标准化字符串的相似度比率必须至少为 `0.85`。 |

### 所有权验证模式

`ownership_validation_mode` 决定在转账依赖某个钱包的所有权之前，需要多少证明：

| 模式 | 行为 |
| - | - |
| `FULL` | 始终需要证明：已签名消息、测试入金或经审核的截图。终端用户的声明永远不计入。小组件不提供 `SELF_DECLARATION`，发起此类声明会返回 `403`，使用 `self_declared: true` 创建的地址簿条目会返回 `400`。 |
| `SIMPLIFIED` | 转账金额低于 `simplified_validation_threshold` 时接受自我声明；高于该阈值则需要证明。入站转账（TRP 询问、网络消息或您报告的入金）按其金额折算为应用的默认货币（按当前汇率）后进行判定。没有汇率的资产，以及沙盒应用的每笔转账，均视为高于阈值。 |
| `AUTOMATED` | 地址簿和终端用户先前的证明可自行作出判定，`allow_self_declaration` 为 `true` 时包括自我声明。这是默认模式，也是所有应用在引入这些模式之前的行为。 |

钱包经验证后，会针对每个终端用户记住该钱包。
在 `FULL` 和 `SIMPLIFIED` 模式下，后续转账只有在该模式对该转账接受的证明支持该钱包时才会复用它。
当 `inbound_auto_accept` 在地址簿中找不到匹配的钱包时，您恰好在一位终端用户的交易中记录过的加密钱包可用于识别持有人。
Didit 会将该钱包作为您的自我声明添加到该终端用户的地址簿中；除非模式为 `FULL`，否则标记为已验证。若地址簿已将同一链的某个钱包列在其他名称下（`eth` 和 `ethereum`），包括被记录为其他 VASP 的钱包，则不会再添加该钱包。
其他 VASP 在发送前进行的地址检查（通过 `GTR`、`Sygna` 和 `VerifyVASP`）同样遵循该模式：在 `FULL` 模式下，终端用户仅作出声明的钱包不会被报告为您的钱包。

### 数据共享

`data_sharing_fields` 决定 Didit 就您自己的用户向交易对手发送哪些字段，个人和公司分别设置：

```json theme={null}
{
  "data_sharing_fields": {
    "individual": ["name", "wallet_address", "address", "date_of_birth"],
    "company": ["name", "wallet_address", "address"]
  }
}
```

* 个人可以共享 `name`、`wallet_address`、`address`、`national_id`、`customer_identification`、`date_of_birth` 和 `place_of_birth`。公司只能共享前五项。
* `name` 和 `wallet_address` 始终会被发送。
* 您从未选择过的实体类型会共享所有字段。空列表也是一种选择：它只共享姓名和钱包。
* `PUT` 按实体类型合并，因此发送 `individual` 不会改变已存储的 `company` 选择。
* 若某项选择无法满足您所在 `jurisdiction` 监管制度（EU TFR 或 FATF 建议第16条）的汇款人义务，包括空列表在内，将返回 `400`。错误信息位于 `data_sharing_fields` 下，并以实体类型为键。

### 按字段匹配

`matching_rules` 设置交易对手数据的每个字段与您所持数据比较时的严格程度：

```json theme={null}
{
  "matching_rules": {
    "individual": { "name": "DEFAULT", "date_of_birth": "STRICT", "address": "NONE", "account": "STRICT" },
    "company": { "name": "FUZZY", "address": "NONE", "account": "STRICT" }
  }
}
```

每个值为 `NONE`、`STRICT`、`CONTAINS`、`DEFAULT` 或 `FUZZY` 之一。
默认值：`name` 遵循 `name_matching_strictness`，`date_of_birth` 和 `address` 为 `NONE`，`account` 为 `STRICT`。
省略的字段保持其默认值。实体类型不进行比较的字段（例如公司没有 `date_of_birth`）会返回 `400`。

### 按司法辖区划分的阈值

`threshold_amount` 之所以存在，是因为 Travel Rule 的适用范围因监管机构而异。
Didit 默认值为 `0.00`，即欧盟的做法，因此除非您主动提高该值，否则每笔转账都会进行信息交换：

| 制度 | 阈值 | 说明 |
| - | - | - |
| **欧盟**，资金转移条例（EU）2023/1113 | **无下限** | 每笔 CASP 对 CASP 的加密货币转账都在适用范围内。转账金额超过 1,000 欧元时，必须验证自托管钱包的所有权。 |
| **FATF 建议第16条**（全球基线） | 1,000 美元/欧元 | 低于此金额时，FATF 允许使用精简数据集（姓名和钱包地址，无需验证）。 |
| **美国**，BSA Travel Rule | 3,000 美元 | FinCEN 长期适用的资金转账阈值，适用于可兑换虚拟货币。 |
| **英国** | 1,000 欧元等值 | 超过此金额时提供完整信息；低于此金额时使用精简数据集（MLRs 第7A部分）。 |
| **新加坡**，MAS PSN02 | 1,500 新加坡元 | 低于阈值时适用较少的要求。 |
| **瑞士**，FINMA | **无下限** | 所有转账均需提供信息；对自托管钱包，预期提供所有权证明。 |

将 `threshold_amount` 设置为您所在监管机构规定的金额。低于该金额的转账将解析为 `NOT_APPLICABLE`，且不交换任何身份数据。
阈值会变化，请与您的合规法律顾问确认当前金额，不要将本表视为法律建议。

### IVMS 101 数据模型

Travel Rule 要求交换的数据所采用的行业标准是 **IVMS 101**（interVASP Messaging Standard）。
`travel_rule_details.originator_data` 和 `travel_rule_details.beneficiary_data` 是遵循 IVMS 101 格式的人员 payload。Didit 会将它们封装进每个通道原生的信封中：TRP 通道交换 `IVMS101` 数据块（`ivms101.2023`，在转账的 `ivms_version` 字段中体现），Sygna 通道使用其 ECIES 加密的 IVMS 变体，而 `GTR`、`TRUST` 和 `VERIFYVASP` 通道则使用各自的加密信封。因此，同一种 payload 结构可以按各协议要求的格式送达每一个交易对手。

Didit 在构建 IVMS 101 消息时，会从每个人员 payload 中读取以下键：`first_name` 和 `last_name`（或单个 `name` 或 `full_name`）、`date_of_birth`、`place_of_birth`、`residence_country`（或 `country`）、`vendor_data`（或 `customer_identification`），以及 `wallet_address`。

两条实用规则：

* **拉丁字母。** IVMS 101 要求使用拉丁字母书写的值，因此原本使用其他文字书写的姓名需要音译。Didit 的[名称匹配](#姓名匹配严格度)会比较标准化后的词元集合，因此词序和大小写差异不会导致交换失败，但双方使用不同文字则会失败。
* **发送已验证的数据。** 当 `vendor_data` 所对应的主体是持有已通过的 Didit KYC 验证的用户时，Didit 传输的是已验证的身份信息，而不是你输入的值。详见[已验证数据与来源](#已验证数据与来源)。
  对于其他主体，Didit 会原样传输你发送的 `originator_data`，因此请根据入驻时已验证的身份信息来构建它，而不要使用未经验证的表单字段。
  这正是 FATF 对“必需且**准确**的汇款人信息”（required and **accurate** originator information）的要求。
  在 `INTERNAL` 通道上，收款方的应用还会以入账交易中交易对手的身份，接收你发送的 `subject`（包括姓名、`vendor_data`、出生日期、国家/地区和地址）。

#### 已验证数据与来源

当转账的主体是你的某位用户，即 KYC 验证已通过的个人时，Didit 会根据已验证的数据构建该方的信息交换内容：

* 你通过 `POST /v3/transactions/` 创建的转出交易中的汇款人；
* 你通过 `POST /v3/transactions/` 报告的转入交易中的收款人，或当匹配钱包的 `holder_vendor_data` 指向该用户时，通过 `POST /v3/travel-rule/inbound/` 报告的转入交易中的收款人。

在 `POST /v3/transactions/` 中，当 Travel Rule 开启时，两者都适用于 `transaction_category` 为 `travel_rule` 的交易。

已验证的姓名、出生日期和出生地、证件号码和类型、地址，以及居住国家/地区（由该地址得出），优先于你发送的值。验证中没有的字段保留你的值。如果已验证的地址不足以完成传输，则你发送的完整地址同样保留。

转账的 `metadata`（位于交易内嵌的 `travel_rule` 对象中）记录了每个字段的来源，以及你的值与已验证值不一致的地方：

```json theme={null}
{
  "metadata": {
    "originator_provenance": { "name": "verified", "date_of_birth": "verified", "customer_identification": "caller" },
    "originator_verified_differences": {
      "name": { "caller": "Jane Doe", "verified": "Jane Elizabeth Doe" }
    }
  }
}
```

`*_provenance` 将每个字段映射为 `verified` 或 `caller`。`*_verified_differences` 列出你发送的值与已验证值存在差异的每个字段，比较时忽略大小写、空格以及两位或三位国家代码的写法差异。当转入交易的数据处于[已脱敏](#脱敏的交易对手数据)状态时，差异不会列出。这两项均由 Didit 自行计算，因此你在 `travel_rule_details.metadata` 中发送的这些键的副本，以及 `name_match` 的副本，都会被丢弃。

#### 直接发送 IVMS 101

除了扁平的 `originator_data` 和 `beneficiary_data` 对象之外，`POST /v3/transactions/` 中的 `travel_rule_details` 以及 `POST /v3/travel-rule/inbound/` 还接受完整的 IVMS 101 数据：

* 一个 `originator` 块和一个 `beneficiary` 块，其中 `originatorPersons` 或 `beneficiaryPersons` 包含一个 `naturalPerson` 或一个 `legalPerson`，并包含 `accountNumber`；
* 同样的块放在 `ivms101`（或 `IVMS101`）对象中；
* 一个 IVMS 101 人员，或一整个数据块，作为 `originator_data` 或 `beneficiary_data` 传入。

每一方只能包含一个人员：一个 `naturalPerson`，或一个 `legalPerson`（这会使该方成为公司）。非空的扁平 `originator_data` 或 `beneficiary_data` 会覆盖同一方的数据块。

```json theme={null}
{
  "travel_rule_details": {
    "status": "AWAITING_COUNTERPARTY",
    "beneficiary": {
      "beneficiaryPersons": [
        {
          "naturalPerson": {
            "name": { "nameIdentifier": [{ "primaryIdentifier": "Ruiz", "secondaryIdentifier": "Carlos", "nameIdentifierType": "LEGL" }] }
          }
        }
      ],
      "accountNumber": ["0xBeneficiaryWallet01"]
    }
  }
}
```

格式错误的数据块会返回 `400`，并以其路径作为键，例如 `{"travel_rule_details": {"originator.originatorPersons": ["must be a non-empty list of originator persons."]}}`。在 `POST /v3/travel-rule/inbound/` 中，相同的键位于顶层，若你使用了包装器，则位于 `ivms101.` 之下。

#### 按监管制度的必填数据

Travel Rule 开启时，金额达到或超过你的阈值的转出交易必须包含其所适用监管制度要求的数据，否则返回 `400`。只检查你发送的各方，已验证的数据计入汇款人一方。错误信息位于 `travel_rule_details.originator_data` 或 `travel_rule_details.beneficiary_data` 之下，例如 `"EU TFR requires the originator name."` 或 `"FATF Recommendation 16 requires the beneficiary wallet address."`：

| 监管制度 | 汇款人 | 收款人 |
| - | - | - |
| EU TFR（你的 `jurisdiction` 为欧盟成员国、冰岛、列支敦士登、挪威、`EU` 或 `EEA`；`EU` 为默认值） | 姓名，加上地址、官方证件号码和客户身份识别号码三者同时具备，或出生日期和出生地。公司没有出生信息的替代方式。 | 姓名和地址 |
| FATF Recommendation 16（其他所有司法辖区，包括空白的司法辖区） | 姓名，加上地址、官方证件号码或客户身份识别号码之一，或出生日期和出生地同时具备。公司没有出生信息的替代方式。 | 姓名和地址 |

对于汇款人，检查会读取你的应用在 [`data_sharing_fields`](#数据共享) 中共享的字段，因为这些是通道实际发送的全部内容；收款人则按你发送的内容进行检查。当转账只有依靠你不共享的字段才能满足监管要求时，`400` 错误会先说明该监管制度的要求，然后列出你的设置所共享的字段。

官方证件号码为 `national_id`（或 `document_number`），客户身份识别号码为 `customer_identification`（或 `vendor_data`），出生日期为 `date_of_birth`。在检查之前，空的 `beneficiary_data.wallet_address` 会由 `counterparty.payment_method.account_id` 填充。若 UTXO 提现的收款输出是 Didit 从 `travel_rule_details.utxo` 推断出的，则视为已包含该地址。不适用 Travel Rule 的 Lightning 转账不做检查。

### Travel Address

**Travel Address** 是一个紧凑的编码字符串，指向某个 VASP 的入站 `TRP` 查询 endpoint。它从不暴露原始 URL 或任何钱包地址。

开启 Travel Rule 后，Didit 会生成您自己的 `travel_address`（在设置中以只读方式返回），以便您将其分享给通过 `TRP` 通道联系您的其他 VASP。

您的每个地址簿钱包还会获得一个单独的 `receiving_travel_address`。发送到该地址的查询会指明对应的钱包，因此 Didit 无需通过账户号或户名来查找钱包。

Didit 的公共 TRP 主机目前尚不响应其他 VASP，因此在该主机能够响应之前，无法通过 `TRP` 接收转账。请参阅 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)。

当您知道交易对手的 Travel Address 时，请将其存入收款方的[地址簿条目](#地址簿)。这样，Didit 会直接通过 `TRP` 路由该收款人的转账，而不会退回到电子邮件。

Didit 会验证您存入地址簿条目或在 `originating_vasp` 中发送的 Travel Address。每个地址都必须以 `ta` 开头，并解码为带有 `t=i` 的查询 URL。因此，`ta...` 这样的占位符会返回 `400`。

交易中的 `beneficiary_data.travel_address` 不做验证。如果它无法解码，则跳过 `TRP` 通道。

编码方式为带 `ta` 前缀的 Base58Check：包含端点主机和路径（路径始终带有查询参数对 `t=i`），加上四字节的双重 SHA-256 校验和，并进行 base58 编码。一个真实地址如下所示，长度约为 100 个字符：

```text theme={null}
ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeSEp1h9tduJcNDZ5SgKLEHeyGMcS1s2HgbM6undFrMQknhM5aTXw3G9KBzi16hx
```

Didit 验证 Travel Address 时，如果值不是格式正确且校验和有效的地址，将返回 `400` 和 `"Invalid travel address."`。

请从您自己的设置响应中获取真实地址，或直接向交易对手索取。

## 无需网络凭证的测试

您无需 GTR、TRUST、VerifyVASP 或 Sygna 的会员资格即可测试不依赖会员资格的路径。您仍需要所测应用的 Didit API 密钥。

* **沙盒出站：** 从沙盒应用提交 `travelRule` 交易。通过阈值和自托管钱包检查后，[Didit 测试 VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md) 会在 Didit 内部按您根据收款钱包或姓名选择的场景作出响应：接受、拒绝、姓名不匹配、无法访问、超时或自托管。不会有任何网络、电子邮件或其他 Didit 客户收到任何内容。
* **沙盒入站：** 让测试 VASP 通过 [触发测试 VASP 入站转账](https://docs.didit.me/zh/management-api/travel-rule/trigger-test-vasp-inbound.md) 向您发送转账，或自行使用 [注册入站 Travel Rule 转账](https://docs.didit.me/zh/management-api/travel-rule/register-inbound.md) 登记一笔转账。测试未知钱包、未验证钱包、收款人姓名不匹配，以及已验证且匹配的钱包。这些测试会执行真实的入站决策，但不会联系任何网络。
* **两个受控的生产环境应用：** 在您自有的两个生产环境应用之间使用 `INTERNAL` 通道。接收方应用可被发现，并通过签名消息或测试入账证明其对目标钱包的控制权。您也可以使用受控的 TRP 交易对手。这样无需商业网络凭证即可测试双方交换。生产环境应用可能产生正常费用和通知，因此请使用您可控的测试身份和交易对手。

有关每个场景及其确切结果，请参阅[在沙盒中测试 Travel Rule](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md)。沙盒或本地协议测试成功，并不能验证商业网络的凭证、会员资格或生产环境互通性。这些需要经授权的网络沙盒，或使用有效会员凭证进行受控的生产环境交换。

## 发送转账

在 [`POST /v3/transactions/`](https://docs.didit.me/zh/transaction-monitoring/transactions) 上发起转账，设置 `transaction_category` 为 `travelRule`（或 `travel_rule`），并附加 `travel_rule_details` 对象（别名：`travelRule`）。

`travel_rule_details` 块本身是可选的，但一旦附加该块，`travel_rule_details.status` 即为必填项：缺少该字段的块会返回 `400`。
当您的应用已**启用** Travel Rule 时，Didit 会忽略您发送的 `status` 值，并自行推进信息交换：

1. 如果金额低于您配置的 `threshold_amount`，转账会立即变为 `NOT_APPLICABLE`，不进行任何协商。
2. 如果交易对手的付款方式将目的地声明为自托管钱包（`payment_method.method_type: "unhosted_wallet"`），则不尝试任何通道，转账直接进入 `UNCONFIRMED_OWNERSHIP`，并自动生成钱包所有权小组件（参见[已声明的自托管钱包](#已声明的自托管钱包)）。
3. 否则，按优先级顺序运行各通道，第一个能够触达交易对手的通道承载信息交换：
   * `INTERNAL`：恰好有一个其他 Didit 客户为 `beneficiary_data.wallet_address` 持有签名消息或测试存款证明，且该证明是在钱包上次更换持有人之后生成的。您自己的应用从不计入。当您的终端用户在[钱包所有权小组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)中选择了某个 Didit 网络成员时，只有该成员的证明才计入。该客户的应用须为生产环境（非沙盒），已开启 Travel Rule，已设置 `is_discoverable`，并且是您在发送 `counterparty_vasp_lei` 或 `counterparty_vasp_name` 时指定的 VASP。仅自行声明、以截图作为证明、未经验证或只存在于地址簿中的钱包，永远不会通过 `INTERNAL` 收到任何内容。
   * `TRP`：转账的资产在该网络上具有数字代币标识符映射（参见 [TRP 资产标识符](#trp-资产标识符)），并且负载中包含 `beneficiary_data.travel_address`，或目的地自身的地址簿条目保存了该地址，或交易对手可通过[目录](#目录)（依据 `counterparty_vasp_lei` 或 `counterparty_vasp_name`）解析出带有 TRP endpoint 的记录。
   * `GTR`、`TRUST`、`VERIFYVASP`、`SYGNA`：您已在 **Marketplace** 中为该网络连接了自己的成员资格，且交易对手能解析为网络 ID（通过显式字段或目录条目，参见[交易对手解析字段](#交易对手解析字段)）。
     Didit 目前尚未在这些网络上持有平台成员资格（正以服务提供方身份入驻），因此目前 `disabled_networks` 不会改变此步骤。
   * `EMAIL`：提供了 `beneficiary_data.counterparty_vasp_email`，或 `counterparty_vasp_name` 能解析为带有合规邮箱的目录条目。
4. 如果没有任何通道能够解析交易对手，Didit 会运行[交易对手发现](#交易对手发现)流程。开启 `vasp_attribution_enabled` 后，该流程包括[区块链分析归因](#区块链分析归属)：目的钱包会被解析为其所属的 VASP，归因得到的名称会写入 `beneficiary_data.counterparty_vasp_name`（仅当其为空时）以及[目录](#目录)，然后通道重新运行一次。因此，归因到某个 VASP，且该 VASP 的目录条目带有 TRP endpoint、网络 ID 或合规邮箱的钱包，会在这第二轮中完成路由。
5. 如果交易对手仍未解析，转账会进入 `COUNTERPARTY_VASP_NOT_FOUND`。对于转出转账，Didit 会（在启用 `auto_wallet_verification` 时）自动生成[钱包所有权小组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)，使转账仍可作为自托管钱包转账完成。待获得更完整的交易对手数据后，您可以使用 `action: "resend"` 稍后重新运行路由。

第 1 步和第 2 步在请求内部完成。第 3 至第 5 步在响应返回后立即运行：转账返回时状态为 `AWAITING_COUNTERPARTY`，且 `protocol: null`，每个结果都会通过一条 [`travel_rule.status.updated`](#webhook) Webhook 送达。

### TRP 资产标识符

`TRP` 通过 ISO 24165 数字代币标识符（DTI）来标明转账资产。
一个 DTI 只标识单条区块链上的一种代币，因此多链代币在每个网络上各有一个 DTI。

Didit 对以下资产通过 `TRP` 发送转账：

| 资产 | 网络 | DTI |
| - | - | - |
| `BTC` | Bitcoin | `4H95J0R2X` |
| `ETH` | Ethereum | `X9J9K872S` |
| `USDC` | Ethereum | `CK9PW1MFH` |
| `USDC` | Celo | `2ZTG3NNVB` |
| `USDC` | Base | `CP6817N4B` |
| `USDC` | Optimism | `898NNTPZJ` |
| `USDC` | Arbitrum One | `4ZSZS1F4F` |
| `USDC` | Polygon PoS | `LS71LQK3L` |
| `USDC` | Avalanche C-Chain | `45J6XJX7H` |
| `USDC` | Solana | `5R3XFPZSM` |
| `USDC` | Plume | `N064ZKXDM` |

`USDC` 的标识符与该代币发行方在其 MiCA 白皮书中公布的标识符一致。

对于多链代币，转账必须在 `transaction_details.crypto_params.crypto_chain` 中指明其网络（例如 `CELO`、`BASE`、`OP`、`ARB` 或 `POL`）。
未指明网络，或网络没有映射时，转账不会通过 `TRP` 发送，Didit 会尝试其余通道：先尝试互操作网络，再尝试邮件。

`USDT` 尚未映射。
没有 DTI 映射的代币（例如企业自有的稳定币），需要其发行方在 ISO 24165 注册机构注册 DTI，每种代币在每条区块链上各注册一个。
DTI 生成后，请联系 Didit 支持，提供每个 DTI、其所在区块链以及代币的小数位数，Didit 会添加该映射。

带有已映射的、按网络区分的 DTI 的入站 `TRP` 查询，会按该代币的货币、以代币小数位表示的金额以及网络进行记录。

### 交易对手发现

仅携带地址的转账，只要有人能识别其交易对手，仍能抵达该交易对手。
在判定出站转账无法路由之前，Didit 会按以下顺序进行查询，并在第一个识别出具有路由的 VASP 的步骤处停止：

| 步骤 | 作用 |
| - | - |
| `internal_address_book` | 另一位 Didit 客户已证明其对该钱包的控制权（通过签名或测试存款），因此 `INTERNAL` 通道会将信息投递给该客户。未经证明的地址簿条目永远不计入。 |
| `wallet_attribution` | 钱包的区块链分析归因，与具有路由的目录 VASP 相匹配。已存储的结果会被复用，且只计费一次。需要启用 `vasp_attribution_enabled`。 |
| `network_lookup` | 一个 Travel Rule 网络，可回答“此地址归属于谁”，前提是你的应用具有访问权限。目前只有 GTR 提供此功能，并且仅在 GTR 通道发送时运行。否则，该步骤会被记录为 `skipped`，并附带原因。 |
| `customer_hint` | 你在 `beneficiary_data` 中发送的 VASP 名称、LEI 或司法辖区，会与带有路由的目录 VASP 进行匹配。`travel_address` 或合规邮箱则通过其各自的通道联系。 |
| `end_user` | 没有人指定交易对手，因此会询问终端用户。详见下文。 |

转账的 `discovery` 字段列出了每个已运行的步骤及其 `outcome`（`match`、`miss` 或 `skipped`）、机器可读的 `reason`、找到的 `vasp` 和 `rail`，以及时间戳。
你或终端用户选择的 VASP 优先于所有自动步骤：发现过程永远不会用它自己的猜测替换它。

如果没有任何步骤指定交易对手，转账会进入 `COUNTERPARTY_VASP_NOT_FOUND` 状态；如果指定了某个 VASP，但其路由都无法被你的应用访问，则进入 `COUNTERPARTY_VASP_NOT_REACHABLE` 状态。
随后，转账会带有 `reason`（`awaiting_end_user`、`no_directory_match`、`directory_match_no_route`，或终端用户所选成员尚未证明钱包所有权时的 `picked_member_without_proven_wallet`），以及 `next_actions` 列表：

| `action` | 含义 |
| - | - |
| `select_counterparty_vasp` | 终端用户可在[钱包组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)中选择目标 VASP，或者你可以通过[转账端点](#发送转账)使用 `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}` 自行指定。转账随后会路由到该 VASP 并重新发送。 |
| `declare_self_hosted` | 终端用户在同一组件中声明该钱包为自托管。 |
| `add_vasp_hint` | 在 `beneficiary_data` 中发送 VASP 名称、LEI 或 `travel_address`，然后重新提交。 |
| `update_compliance_email` | 当首次提供的合规邮箱被拒绝时，重新提供交易对手的合规邮箱；参见[为什么转账被卡住](#为什么转账会卡住)。 |
| `resend` | 结合已变更的内容重新运行发现过程。 |

组件链接即为终端用户执行操作时的 `url`。
当钱包所有权操作待处理时，交易的 `action_required` 块（类型为 `wallet_ownership`）会携带相同的 `reason` 和 `next_actions`，因此已经遵循 `action_required` 的客户端无需额外工作即可显示选择器。

### 交易对手解析字段

Didit 掌握的关于交易对手的所有信息都来自 `travel_rule_details.beneficiary_data`。
以下字段驱动通道解析，均为可选：

| 字段 | 作用 |
| - | - |
| `wallet_address` | `INTERNAL` 地址簿匹配、`TRP` 地址簿查询以及钱包所有权流程。若你省略该字段，Didit 会使用 `counterparty.payment_method.account_id`。 |
| `chain` | 限定两次地址簿查询的范围：仅当条目的 `chain` 与此值相等（不区分大小写）或条目没有 `chain` 时才会匹配。若你省略该字段，Didit 会依次从 `transaction_details.crypto_params.crypto_chain`、`custom_properties.crypto_chain` 或转账币种中取值，转为大写后填入，但仅当该值是 Didit 可识别的链时（例如 `ETH`、`BTC`、`SOL`、`TRX`）才会填入；否则，转账不携带链信息，并匹配任意链上的条目。对于未明确指定链的 `USDT` 或 `USDC` 转账，Didit 会从 `transaction_details.payment_reference_id` 中的交易哈希推断链（`0x` 哈希对应 `ETH`）。 |
| `memo` | `wallet_address` 在备注网络上的目标标签或备注（memo）。凡是匹配 `wallet_address` 之处，它都是钱包的一部分（参见[备注和标签网络](#memo-与标签网络)）。别名：`destinationTag`。 |
| `travel_address` | 直接通过 `TRP` 路由，这是你可以传入的最强信号。 |
| `counterparty_vasp_lei` | 按 LEI 在目录中查找，适用于 `TRP`、`GTR`、`TRUST`、`VERIFYVASP` 和 `SYGNA`。同时会与 TRP 端点返回的身份进行交叉核对：若不一致，或身份中没有 LEI，则交换中止，状态为 `COUNTERPARTY_VASP_NOT_REACHABLE`。 |
| `counterparty_vasp_name` | 为 `TRP`、`GTR`、`TRUST`、`VERIFYVASP`、`SYGNA` 和 `EMAIL` 进行目录查找：依次尝试完全相同的名称、以其他写法表示的同一名称（例如 `B.V.` 对应 `BV`，或带重音符号的写法），然后是条目的别名。两者同时存在时，会与 TRP 身份进行模糊核对。 |
| `counterparty_vasp_jurisdiction` | 当指定的 VASP 是拥有多个受监管实体的品牌，或多个目录条目共享同一名称时，此字段为接收实体的 ISO 3166 国家代码，参见[品牌及其实体](#品牌及其实体)。 |
| `counterparty_gtr_vasp_code` | 明确指定 `GTR` 网络 ID，跳过目录查找。 |
| `counterparty_trust_membership_id` | 明确指定 `TRUST` 网络 ID，跳过目录查找。 |
| `counterparty_verifyvasp_id` | 明确指定 `VerifyVASP` 网络 ID，跳过目录查找。 |
| `counterparty_sygna_vasp_code` | 明确指定 `Sygna Bridge` VASP 代码，跳过目录查找。 |
| `counterparty_vasp_email` | 为 `EMAIL` 通道明确指定合规联系人，跳过目录查找。 |

### 链的结构形式

在账户型链（EVM 链、Solana、Tron）上，转账会将资金从一个地址转移到另一个地址，Didit 直接从交易中读取两个钱包。
其他三种结构需要更多信息，Didit 为其解析出的结果会体现在转账的 `chain_details` 中（在账户型链上为空，即 `{}`）。
`chain_details.originator` 和 `chain_details.beneficiary` 各自包含 `address`、其 `memo` 以及来源 `source`；匹配和路由会优先从这里读取钱包。

#### UTXO 转账

比特币、莱特币、比特币现金或狗狗币交易会花费来自任意数量地址的输入，并支付任意数量的输出，其中部分输出会作为找零退回给发送方。
将输入和输出作为 `travel_rule_details.utxo` 发送（金额以该币种自身的单位表示，为十进制字符串），Didit 会推断你未声明的内容：

* **汇款人**：若你发送了 `originator_data.wallet_address`，则使用它；否则使用贡献金额最多的输入地址（`source: largest_input`）。
* **收款人**：使用已声明的钱包；否则使用唯一一个不作为找零退回给输入地址的输出（`only_non_change_output`）；再否则使用支付转账金额的那个输出（`amount_match`）。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "btc-withdrawal-0042",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "amount": "0.25",
      "currency": "BTC",
      "currency_kind": "crypto"
    },
    "subject": { "vendor_data": "user-042", "full_name": "Ana Diaz" },
    "counterparty": { "full_name": "Luis Perez" },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "beneficiary_data": { "name": "Luis Perez", "counterparty_vasp_name": "Destination Exchange" },
      "utxo": {
        "inputs": [
          { "address": "bc1qsenderhotwallet01", "amount": "0.30" },
          { "address": "bc1qsendercoldwallet02", "amount": "0.05" }
        ],
        "outputs": [
          { "address": "bc1qbeneficiarydeposit01", "amount": "0.25" },
          { "address": "bc1qsenderhotwallet01", "amount": "0.0999" }
        ]
      }
    }
  }'
```

随后，该转账会携带：

```json theme={null}
{
  "chain_details": {
    "chain": "BTC",
    "shape": "utxo",
    "originator": { "address": "bc1qsenderhotwallet01", "memo": "", "source": "largest_input" },
    "beneficiary": { "address": "bc1qbeneficiarydeposit01", "memo": "", "source": "only_non_change_output" },
    "utxo": {
      "inputs": [
        { "address": "bc1qsenderhotwallet01", "amount": "0.300000000000000000" },
        { "address": "bc1qsendercoldwallet02", "amount": "0.050000000000000000" }
      ],
      "outputs": [
        { "address": "bc1qbeneficiarydeposit01", "amount": "0.250000000000000000" },
        { "address": "bc1qsenderhotwallet01", "amount": "0.099900000000000000" }
      ],
      "change_output_indexes": [1],
      "beneficiary_output_index": 0
    }
  }
}
```

`chain_details.warnings` 会标记不一致之处：`declared_originator_not_an_input`、`declared_beneficiary_not_an_output`、`beneficiary_output_ambiguous`（无法区分出任何单一输出）和 `beneficiary_output_amount_differs`。
在其他任何链上使用 `utxo` 会返回 `400`，且每个列表最多包含 1,000 条记录。
已报告的入金使用相同的 `utxo` 对象（参见 [入金后 sunrise 注册](#存款后的-sunrise-登记)）。

#### Memo 与标签网络

在 XRP、Stellar（XLM）、EOS、Cosmos（ATOM）和 Hedera（HBAR）上，交易所会在同一个共享地址上接收所有客户的款项，并通过目标标签（destination tag）或 memo 来区分他们。
因此，memo 是钱包的一部分：请在参与方上以 `payment_method.memo` 发送，或在 `originator_data` / `beneficiary_data` 中以 `memo` 发送（别名 `destinationTag`），Didit 会同时比对地址和 memo。

* 带有 memo 的地址簿条目只匹配指定该 memo 的转账。不带 memo 的条目匹配整个地址。未指定 memo 的转账永远不会匹配带有 memo 的条目：在共享地址上，没有标签的入金不属于任何人。
* XRP X 地址内含标签：Didit 会将其拆分为经典地址和标签（`source: x_address`），因此以任一形式注册的钱包都能匹配另一形式的转账。
* 共享地址上的每个 memo 在该交易中都是一种独立的付款方式。
* 各网络的接受范围：XRP 目标标签为 0 到 4294967295 之间的整数（接受 JSON 数字）；Stellar memo 最多 28 字节文本、64 位 ID 或十六进制的 32 字节哈希；EOS 最多 256 字节，Cosmos 最多 256 个字符，Hedera 最多 100 字节。在没有 memo 的链（Bitcoin、EVM 链、Solana、Tron、Litecoin、Bitcoin Cash、Dogecoin、Lightning）上使用 memo 会返回 `400`。

#### Lightning

Lightning 付款没有链上收款地址：付款方持有一个付款请求。
在 bitcoin 或 Lightning 加密转账中，`payment_method.account_id` 可以是该付款请求，Didit 无需连接任何网络即可据此识别收款方：

| 您发送的内容 | 收款人 |
| - | - |
| BOLT11 发票（`lnbc...`） | 收款节点，由发票中指明，或从其签名中恢复。 |
| BOLT12 offer 或发票（`lno1...`、`lni1...`） | 发行方节点，除非其隐藏在盲化路径之后。 |
| Lightning Address（`name@domain`） | Lightning Address 本身。 |
| 节点 ID | 节点本身。 |
| LNURL（`lnurl1...`、`lnurlp://`） | 无：它指向一项服务，而非收款方。 |

收款方会作为账户保存，并保存为 `chain_details.beneficiary`（`source: invoice_payee`），所在链为 `LIGHTNING`；请求中的内容则保存在 `chain_details.lightning` 中。
如果请求未指定收款方，出站转账会变为 `NOT_APPLICABLE`，原因记录在 `chain_details.not_applicable_reason` 以及其 `timeline` 中，取值为 `LIGHTNING_NO_PAYEE_IDENTIFIER`、`LIGHTNING_BOLT12_BLINDED_PAYEE` 或 `LIGHTNING_LNURL_NAMES_NO_PAYEE` 之一。
只有在 bitcoin 链上、`LIGHTNING` 链上，或未指定链时，某个值才会被识别为 Lightning（Lightning Address 仅限 bitcoin 或 `LIGHTNING` 链）；在其他任何链上，它都是该链自身的地址。

> [!NOTE]
>
> 只有通过该客户已用签名消息或测试入金证明其控制权的钱包，`INTERNAL` 通道才能触达另一位 Didit 客户；目前这类证明仅支持 Bitcoin、EVM 链、Solana 和 Tron。
> 因此，在 memo 网络、Lightning、Litecoin、Bitcoin Cash 和 Dogecoin 上，转账永远不会通过 `INTERNAL` 触达另一位 Didit 客户。
> memo、标签和 Lightning 匹配仍然适用于您自己的地址簿：发往您钱包的入站转账、已报告的入金以及 `TRP` 查询。

### 更新汇款人钱包

提现通常以客户的主钱包名义申报，但实际广播时使用的则是持有币的任意地址。
请使用 `PATCH /v3/transactions/{transaction_id}/travel-rule/` 记录资金实际转出的钱包：

```bash theme={null}
curl -X PATCH "https://verification.didit.me/v3/transactions/$TRANSACTION_ID/travel-rule/" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "update_originator_wallet", "wallet_address": "bc1qsendercoldwallet02" }'
```

* 仅适用于出站转账，且绝不适用于 `CANCELLED` 或 `EXPIRED` 状态的转账（返回 `409`）。
* 若已知转账的 UTXO 输入，则该钱包必须是其中之一（否则返回 `400`）。在 memo 网络上，还需同时发送 `memo`。
* 该操作仅修改记录：不会再次向交易对手发送任何内容，状态保持不变。`chain_details.originator` 会变为新钱包，并带有 `source: updated`；`timeline` 会记录之前和新的钱包；该转账对应的 `INTERNAL` 转账也将以相同方式更新。

### 区块链分析归属

如果您只提供一个钱包地址，Didit 仍然可以查明另一方是谁。
如果没有任何通道能够路由该转账，Didit 会将目标钱包与区块链分析归属数据进行比对。这与驱动 [加密交易筛查](https://docs.didit.me/zh/transaction-monitoring/aml-screening.md) 的分析相同，能够识别哪些钱包属于哪些交易所和托管机构。
归属分析仅对同时携带目标钱包和 `chain` 的出站转账执行（参见 [交易对手解析字段](#交易对手解析字段)）；若没有链，则跳过该步骤。

当钱包归属于已知实体时，Didit 会：

1. 在转账上记录归属信息：填入 `beneficiary_data.counterparty_vasp_name`（若您未提供该字段），并且转账的元数据中包含一个 `vasp_attribution` 块，其中包含实体名称、实体类型、国家/地区，以及 `source: "blockchain_analytics"`。
2. 将该实体添加到 [VASP 目录](#目录) 中（或与已有条目匹配），以确保归属信息在各次转账之间保持稳定。
3. 使用归属名称重新运行一次通道路由：若目录条目已包含 TRP 端点，转账将立即完成路由，而不会进入 `COUNTERPARTY_VASP_NOT_FOUND`。

归属调用同时也是一次完整的加密筛查：交易会获得完整的筛查结果，包括风险评分、钱包风险类别和网络快照，效果与加密监控已运行时完全相同。交易的 `custom_values.aml_screening_trigger` 会被设置为 `travel_rule_attribution`，以便您看到此次筛查是由 Travel Rule 路由所触发的。
若加密监控已对本交易中的同一钱包进行过筛查，则复用已存储的结果，不会再次调用分析接口，也不会再次收费。
归属查询绝不会阻止交易创建：若分析查询失败，或您的余额不足以支付该查询费用，转账将直接进入 `COUNTERPARTY_VASP_NOT_FOUND`，且本次尝试不收取任何费用。
当 `action: "resend"` 时，将复用先前记录的归属结果，而不再重新查询。

使用 [设置](#设置) 中的 `vasp_attribution_enabled: false` 可关闭整个步骤。
有关归属识别调用的计费方式，请参阅 [定价](#定价)。

当 Travel Rule **已禁用**时，`travel_rule_details`（包括你提供的 `status`，无论是旧版还是当前版本）仍会记录在交易上，但只是被动记录，不会调度任何通道。
你可以用此功能回填已通过自有流程跟踪的合规数据。
被动的出站转账不会发起任何交换，因此按 Transaction Screening 计费，而不计入 [Travel Rule 计费项](#定价)。

启用 Travel Rule 后，此请求会通过 `EMAIL` 通道路由，因为它指定了交易对手的合规邮箱，而之前的任何通道都无法触达收款人：

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "trv-2026-07-05-001",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "action_type": "transfer",
      "amount": "0.25",
      "currency": "ETH",
      "currency_kind": "crypto"
    },
    "subject": {
      "entity_type": "individual",
      "vendor_data": "user-042",
      "full_name": "Jane Doe"
    },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "originator_data": {
        "first_name": "Jane",
        "last_name": "Doe",
        "date_of_birth": "1990-04-12",
        "residence_country": "ES",
        "customer_identification": "user-042",
        "wallet_address": "0x135908330861442a814796ae99feac507925782a"
      },
      "beneficiary_data": {
        "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
        "name": "Ana Diaz",
        "counterparty_vasp_email": "compliance@bene-casp.example"
      }
    }
  }'
```

响应（为突出关键字段已作删减）：

```json theme={null}
{
  "uuid": "22222222-3333-4444-5555-666666666666",
  "txn_id": "trv-2026-07-05-001",
  "transaction_type": "travelRule",
  "direction": "OUTBOUND",
  "status": "APPROVED",
  "amount": "0.25",
  "currency": "ETH",
  "currency_type": "crypto",
  "travel_rule": {
    "uuid": "33333333-4444-5555-6666-777777777777",
    "status": "AWAITING_COUNTERPARTY",
    "protocol": null,
    "required": true,
    "obligations_count": 0,
    "originator_data": {
      "first_name": "Jane",
      "last_name": "Doe",
      "date_of_birth": "1990-04-12",
      "residence_country": "ES",
      "customer_identification": "user-042",
      "wallet_address": "0x135908330861442a814796ae99feac507925782a"
    },
    "beneficiary_data": {
      "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
      "name": "Ana Diaz",
      "counterparty_vasp_email": "compliance@bene-casp.example",
      "chain": "ETH"
    },
    "metadata": {
      "compliance_email_source": "caller_supplied"
    }
  },
  "action_required": null
}
```

* `originator_data` 即你发送的内容，因为示例中的主体没有已通过的 KYC 验证；对于已验证的用户，Didit 会根据已验证数据构建该字段（参见 [已验证数据与来源](#已验证数据与来源)）。
  `EMAIL` 通道只会向收件人展示你在 [`data_sharing_fields`](#数据共享) 中共享的汇款人字段。
* `beneficiary_data.chain` 为 `ETH`，因为请求中未指定链，所以 Didit 根据币种确定了该值。
* `status` 为 `APPROVED`，因为没有规则对该交易采取任何操作；你自己的规则可能会设置其他状态。
* 合规联系人会收到领取链接。联系人回复后，转账可能完成、因姓名匹配失败，或被拒绝；若对方未回复，则会过期（参见 [邮件领取](#针对无法触达的交易对手的邮件领取)）。

`protocol` 仍为 `null`，因为通道是在响应之后选定的（例如，当你提供了 `counterparty_vasp_email` 时选定 `EMAIL`）。选定后，[获取交易](https://docs.didit.me/zh/management-api/transactions/get.md) 会显示该值，此后每个 `travel_rule.status.updated` Webhook 都会在 `rail` 中携带它。
如果收款钱包属于另一位已证明控制该钱包的 Didit 客户，`INTERNAL` 通道会在响应后立即完成整个交换，且该 Webhook 已报告 `COMPLETED`（或 `COUNTERPARTY_MISMATCHED_DATA`）。姓名不匹配时，你的转账状态为 `COUNTERPARTY_MISMATCHED_DATA`，另一位客户不会收到任何内容。
若未提供 `counterparty_vasp_email`，且通道和[归属识别](#区块链分析归属)都无法触达收款人，转账稍后会以 `COUNTERPARTY_VASP_NOT_FOUND` 结束，并在交易上带有一个 `wallet_ownership` 操作。

> [!NOTE]
>
> 这笔交易内嵌的 `travel_rule` 对象包含转账自身的字段（`uuid`、`status`、`protocol`、`required`、`obligations_count`、`originator_data`、`beneficiary_data`、`metadata`）、Didit 已解析并比对的内容（`chain_details`、`name_match`、`data_match`、`mismatched_fields`，以及[脱敏](#脱敏的交易对手数据)字段 `data_masked` 和 `data_unmasked`），以及说明转账当前进展的字段：`direction`、`rail`、`counterparty_vasp`、`delivery`、`discovery`、`reason` 和 `next_actions`（参见[为什么转账会卡住](#为什么转账会卡住)）。
> [`GET /v3/transactions/`](https://docs.didit.me/zh/management-api/transactions/list) 的每一行都带有同样的数据块；对于没有转账的交易，则为 `null`。
> 下文的专用转账端点同样会返回这些字段，另外还会返回 `ivms_version`、`ownership_confirmed`、`deadline_at`、`timeline` 和 `created_at`，但不返回 `obligations_count` 或 `metadata`。

要结束一笔已在链上广播的 `COMPLETED` 出站转账：

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payment_txn_id": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060"}'
```

此操作要求该转账为 `COMPLETED` 状态的**出站**转账，否则 API 返回 `409`。
成功后，出站转账及其 `INTERNAL` 通道的对应转账都会变为 `FINISHED`，且交易的 `payment_txn_id` 会被设为你提供的哈希值。
在 `TRP` 通道上，Didit 还会将该哈希值发送到交易对手的确认回调。

若要取消尚未进入终态的转账，请向同一端点发送 `{"action": "cancel"}`。
`INTERNAL` 通道的对应转账也会被取消；在 `TRP` 通道上，一旦交易对手批准了查询，Didit 会将取消信息发送到其确认回调。
已处于 `FINISHED`、`CANCELLED` 或 `EXPIRED` 状态的转账返回 `409`。
既未提供 `payment_txn_id`、也未提供受支持的 `action` 的请求返回 `400`。

对于通过 `GTR`、`VERIFYVASP` 或 `SYGNA` 发送的转账，Didit 会在响应之后将链上哈希（或取消信息）报告给网络，并采用[相同的重试机制](#投递与重试)；响应中的 `delivery` 会显示该消息。

若要重试一笔未能送达交易对手的出站转账（即当前处于 `COUNTERPARTY_VASP_NOT_FOUND`、`COUNTERPARTY_VASP_NOT_REACHABLE` 或 `NOT_ENOUGH_COUNTERPARTY_DATA` 状态的转账），请先完善交易对手数据，例如为目标钱包条目添加 `travel_address`，为交易对手的目录条目添加[联系人](#联系人)，或将该 VASP 编入[目录](#目录)。
然后向同一端点发送 `{"action": "resend"}`。
Didit 会重新开始确认期限，并使用转账上存储的数据，从头重新排队路由流程。响应会显示转账回到 `AWAITING_COUNTERPARTY` 状态（其 `timeline` 中会有 `resend queued`），结果将通过 `travel_rule.status.updated` Webhook 送达。
若较早的尝试仍在运行，则重新发送请求会在该尝试结束后再发送。
其他状态的转账，或入站转账，返回 `409`。

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "resend"}'
```

若[发现流程](#交易对手发现)未能确定交易对手，你可以自行指定：发送 `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}`（`id` 取自 `GET /v3/travel-rule/vasps/`）。
转账会被路由至该 VASP 并重新发送。
只有处于 `COUNTERPARTY_VASP_NOT_FOUND`、`COUNTERPARTY_VASP_NOT_REACHABLE` 或 `NOT_ENOUGH_COUNTERPARTY_DATA` 状态的出站转账才接受此操作；其他任何状态返回 `409`，未知 id 返回 `404`。

### 为什么转账会卡住

处于 `COUNTERPARTY_VASP_NOT_FOUND` 或 `COUNTERPARTY_VASP_NOT_REACHABLE` 状态的转账会说明其卡住的原因以及下一步的操作，且每次读取其交易时都会返回这些信息。
[`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/zh/management-api/transactions/get) 的 `travel_rule` 对象、[`POST /v3/transactions/`](https://docs.didit.me/zh/management-api/transactions/create) 的 `201` 响应，以及 [`GET /v3/transactions/`](https://docs.didit.me/zh/management-api/transactions/list) 的每一行都包含该信息，通过[转账端点](https://docs.didit.me/zh/management-api/travel-rule/update-transfer.md)返回的转账对象中也包含该信息：

* `reason` 是机器可读的原因，其他状态下为 `""`。
* `next_actions` 列出可推动转账继续的操作，其他状态下为 `[]`。
* `delivery` 是转账需要向对方发送的最新消息，包含 `kind`、`status`（`QUEUED`、`SENDING`、`RETRYING`、`DELIVERED` 或 `FAILED`）、`rail`、`attempts`、`last_error`、`next_attempt_at` 和 `delivered_at`；若无需向对方投递，则为 `null`。
* `discovery` 记录 Didit 查找交易对手的过程，每个已执行的步骤对应一条：`step`、`outcome`（`match`、`miss` 或 `skipped`）、`reason`、`vasp`、`rail` 和 `at`。

`email_undeliverable` 和 `delivery_failed` 来自失败的投递，且仅出现在 `COUNTERPARTY_VASP_NOT_REACHABLE` 状态下。
失败的投递会优先决定 `reason`，因此 Didit 已找到的交易对手，绝不会被报告为未找到。
其余四个原因来自查找记录，两种状态下均可出现。

| `reason` | 发生了什么 | `next_actions` |
| - | - | - |
| `email_undeliverable` | 邮件服务商拒收了发往合规邮箱的邮件，且已无可尝试的地址：无论是发送时、提醒时，还是之后才收到的永久退信。 | `update_compliance_email`，然后 `resend` |
| `delivery_failed` | 负责投递的通道最终失败：网络通道拒收消息或重试次数耗尽，`TRP` 身份主机未响应，或交易对手的 `TRP` 主机未接受询问。 | `resend` |
| `awaiting_end_user` | 没有任何发现步骤指向 Didit 可以联系的交易对手，因此请终端用户指定对方。 | `select_counterparty_vasp`、`declare_self_hosted`、`add_vasp_hint`、`resend` |
| `directory_match_no_route` | 某个步骤指向了一个[目录](#目录)条目，但该条目没有 Didit 可为你使用的路由。 | 同 `awaiting_end_user` |
| `picked_member_without_proven_wallet` | 终端用户选择了另一位 Didit 客户作为交易对手，但该客户尚未证明对该钱包的控制权。 | 同 `awaiting_end_user` |
| `no_directory_match` | 查找记录没有提供其他原因。 | 同 `awaiting_end_user` |

| 下一步操作 | 操作说明 |
| - | - |
| `update_compliance_email` | 使用 `{"action": "update_compliance_email", "compliance_email": "..."}` 在转账端点上重新提供交易对手的合规邮箱。 |
| `resend` | 出现变化后，使用 `{"action": "resend"}` 重新运行路由。 |
| `select_counterparty_vasp` | 终端用户在[钱包所有权验证组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)中选择目标 VASP。转账存在未关闭的组件会话时，`url` 即为该组件的链接。 |
| `declare_self_hosted` | 终端用户在组件中声明该钱包为自托管钱包，使用相同的 `url`。 |
| `add_vasp_hint` | 告知 Didit 交易对手是谁，可提供 VASP 名称、LEI 或 Travel Address（例如目标钱包[地址簿条目](#地址簿)上的 `travel_address`），然后重新发送。 |

重新发送会推动转账继续，因此其 `reason` 会被清空。
由失败投递引起的状态变更，其时间线条目在 `reason` 下带有相同的值。

若邮件服务商拒收发往[上文示例转账](#发送转账)合规邮箱的邮件，该交易的 `travel_rule` 块（节选）如下：

```json theme={null}
{
  "uuid": "33333333-4444-5555-6666-777777777777",
  "status": "COUNTERPARTY_VASP_NOT_REACHABLE",
  "protocol": "EMAIL",
  "direction": "OUTBOUND",
  "rail": "EMAIL",
  "counterparty_vasp": null,
  "delivery": {
    "kind": "SEND",
    "status": "FAILED",
    "rail": "EMAIL",
    "attempts": 1,
    "last_error": "email undeliverable",
    "next_attempt_at": null,
    "delivered_at": null
  },
  "reason": "email_undeliverable",
  "next_actions": [
    { "action": "update_compliance_email" },
    { "action": "resend" }
  ]
}
```

此处 `delivery` 与 `reason` 一致，因为发送本身被拒。
提醒邮件被拒或之后才收到退信时，`delivery.status` 仍为 `DELIVERED`：首封邮件确已发出。
要了解转账停滞的原因，应查看 `reason`，而非 `delivery`。

要修复此问题，请重新提供交易对手的合规邮箱，然后重新发送：

```bash theme={null}
curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "update_compliance_email", "compliance_email": "desk@bene-casp.example"}'

curl -X PATCH https://verification.didit.me/v3/transactions/22222222-3333-4444-5555-666666666666/travel-rule/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "resend"}'
```

`update_compliance_email` 会将该地址保存为本笔转账的 `beneficiary_data.counterparty_vasp_email`：它仅属于本笔转账，不会加入任何目录条目或联系人列表。
状态不变，时间线新增 `compliance email updated` 条目。在重新发送并将请求发往新地址之前，转账的 `reason: email_undeliverable` 保持不变。
只有处于 `COUNTERPARTY_VASP_NOT_FOUND`、`COUNTERPARTY_VASP_NOT_REACHABLE` 或 `NOT_ENOUGH_COUNTERPARTY_DATA` 状态的出站转账才接受此操作；其他转账返回 `409`，缺少或格式错误的 `compliance_email` 返回 `400`。

### 已声明的自托管钱包

当你的客户告知目标方是其本人的自托管（非托管）钱包时，请在交易上声明该钱包，而不是让路由自行查找，结果发现背后并无 VASP：将交易对手的支付方式类型设置为 `unhosted_wallet`。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "trv-2026-07-07-002",
    "transaction_category": "travelRule",
    "transaction_details": {
      "direction": "outbound",
      "action_type": "transfer",
      "amount": "0.25",
      "currency": "ETH",
      "currency_kind": "crypto"
    },
    "subject": {
      "entity_type": "individual",
      "vendor_data": "user-042",
      "full_name": "Jane Doe"
    },
    "counterparty": {
      "entity_type": "external",
      "full_name": "Jane Doe",
      "payment_method": {
        "method_type": "unhosted_wallet",
        "account_id": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9"
      }
    },
    "travel_rule_details": {
      "status": "AWAITING_COUNTERPARTY",
      "beneficiary_data": {
        "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
        "name": "Jane Doe"
      }
    }
  }'
```

此声明会完全跳过路由：

* 不会尝试任何通道，因为没有可与之交换数据的交易对手 VASP，转账将直接创建为 `UNCONFIRMED_OWNERSHIP` 状态。
* 当 `auto_wallet_verification` 开启时（默认开启），Didit 会自动生成一个与该交易绑定的[钱包所有权验证组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)会话，并在交易的 `action_required` 块中返回该会话；SDK 会在[客户端提交](https://docs.didit.me/zh/transaction-monitoring/sdk-transaction-submission.md)时自动启动该组件。
* 客户证明对该钱包的控制权后，转账会执行收款人姓名比对，并推进至 `COMPLETED` 状态（比对失败则进入 `COUNTERPARTY_MISMATCHED_DATA`）。

这是以 API 形式实现的欧盟 TFR 自托管钱包义务：验证非托管钱包的所有权，而不是与交易对手 VASP 交换数据。
该声明读取自出站转账的**目标**端、以及入站转账的**来源**端的支付方式，两种情况下，读取的都是交易对手一方。
已声明为非托管的转账已处于最终的无通道路径，因此 `UNCONFIRMED_OWNERSHIP` 不是可重新发送的状态：请完成所有权证明（或取消），而不是调用 `action: "resend"`。

## 接收

### 确认钱包归属

转账会在以下两种情况下停留在 `UNCONFIRMED_OWNERSHIP` 状态：

* 入站转账匹配到一条尚未验证归属的[地址簿](#地址簿)条目。
* 交易对手的钱包已声明为自托管钱包（`counterparty.payment_method.method_type: "unhosted_wallet"`），适用于出站提现或入站存款。参见[已声明的自托管钱包](#已声明的自托管钱包)。

您的应用需要确认或拒绝该转账：

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/{transaction_uuid}/travel-rule/ownership/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirmed": true}'
```

请求体接受 `confirmed`（必填，布尔值）和可选的 `wallet_address`。

该接口只接受处于以下任一状态的转账。对于其他状态，接口返回 `409`，响应为 `{"detail": "Transfer is not awaiting ownership confirmation."}`：

* `UNCONFIRMED_OWNERSHIP`，两个方向均适用。
* 入站转账处于 `ON_HOLD`。
* 入站 `TRP` 转账处于 `UNCONFIRMED_OWNERSHIP`、`ON_HOLD` 或 `COUNTERPARTY_MISMATCHED_DATA`，以便合规官手动处理交易对手的查询。

对于 Didit 自行创建的转账（`INTERNAL` 通道、抢先登记，或已声明的自托管钱包）：

* `confirmed: true` 会将匹配的地址簿条目标记为已验证归属，然后按该条目的 `holder_name` 进行姓名匹配：通过则为 `COMPLETED`，不通过则为 `COUNTERPARTY_MISMATCHED_DATA`。
  `INTERNAL` 通道的对应方（如有）会得到相同结果。
  对于已声明的自托管钱包，`confirmed: true` 表示您的客户已核实交易对手钱包的控制人，并完成转账（`COMPLETED`），无论出站还是入站。
  若入站转账的存款已结算，则会直接进入 `FINISHED`。
* `confirmed: false` 会将转账双方都更新为 `COUNTERPARTY_VASP_GENERAL_DECLINE`。
* 系统只按地址在您自己的地址簿中查找匹配条目，不涉及链。
  对于入站 `ON_HOLD` 转账，若没有匹配条目，则会根据转账的 `beneficiary_data.wallet_address` 创建条目，并标记为已验证。
  已声明的自托管钱包是例外：该钱包会按下文所述，以已验证归属的状态登记到您的地址簿中。
  其他情况下，若条目不存在，则返回 `409`，响应为 `{"detail": "wallet address entry no longer available"}`。已删除的条目也会返回同样的结果。

对于入站 `TRP` 转账，同一调用用于回复交易对手的查询：

* `confirmed: true` 需要一个用于返回的付款地址，按以下顺序取值：请求中的 `wallet_address`、匹配的条目、`beneficiary_data.wallet_address`。若三者都没有，请求返回 `400`，响应为 `{"wallet_address": ["wallet_address is required to approve."]}`。Didit 会创建或验证该条目，向交易对手发送查询结果回调，设置 `ownership_confirmed`，并将转账更新为 `COMPLETED`。
* `confirmed: false` 会发送拒绝结果，并将转账更新为 `COUNTERPARTY_VASP_GENERAL_DECLINE`。
* 若无法访问交易对手的回调端点，请求返回 `502`，转账保持当前状态。若为批准操作，地址簿条目仍会被创建或标记为已验证。

对于已声明的自托管钱包，您需要验证的钱包属于交易对手：出站提现时为提现的目标地址，入站存款时为来源钱包（即汇款人的钱包）。Didit 会将该钱包以已验证归属的状态添加到地址簿中，并以交易对手的姓名（入站存款时为汇款人的姓名）进行匹配。若地址簿中该地址已登记为其他持有人的姓名，则转账改为 `COUNTERPARTY_MISMATCHED_DATA`。

若转账未指定任何交易对手钱包，请在请求体中传入 `wallet_address`。若转账和请求中都没有提供地址，请求将被拒绝，并返回 `400`。

```json theme={null}
{"confirmed": true, "wallet_address": "0xCounterpartyWallet01"}
```

* 已在其网络上得到应答的网络转账（VerifyVASP 转账，或已裁定的 GTR 或 Sygna Bridge 转账）返回 `409`（参见[来自网络的转账](#来自网络的转账)）。

响应为上文所述的完整转账对象。
新的 `INTERNAL` 转账不会在此等待：它只会送达已证明钱包归属的客户。
在该规则生效之前已在等待的旧 `INTERNAL` 转账，会一直隐藏汇款人的信息，直到您证明对钱包的控制权（通过签名消息或测试存款）。在此之前确认该转账将返回 `409`。拒绝该转账仍然有效。
当交易对手回复该钱包为自托管钱包时（目前只有 [Didit Test VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md) 在沙盒中给出此答复），确认操作会以收款人的姓名将该钱包添加到您的地址簿，并进行姓名匹配：匹配则完成转账，并将钱包标记为已验证。

### 来自网络的转账

Didit 还可以接收交易对手通过 `GTR`、`VERIFYVASP` 或 `SYGNA` 发送的 Travel Rule 转账。
接收功能由 Didit 按网络分别开启，**目前尚未开启任何网络**：还需要各网络登记 Didit 的端点，而 Didit 目前尚未加入这些网络。
`TRUST` 转账无法接收。

网络开启后，其投递的消息按以下方式处理：

* **路由。** 当消息包含您在该网络上所连接会员身份的参与者代码时，消息会送达您的应用。只有当您是唯一一位证明了收款钱包归属的在用 Didit 客户时，发往 Didit 自身会员身份的消息才会送达您。其他消息一律拒绝，因此任何客户都不会收到他人的转账。
* **决策。** Didit 会在您的应用中创建入站转账，并按照 [TRP 查询](https://docs.didit.me/zh/transaction-monitoring/travel-rule-interoperability.md#trp-endpoints)的方式进行裁定：钱包匹配、归属验证、姓名匹配以及 `inbound_auto_accept`。
* **仅处理一次。** 网络会反复重发消息，直到得到应答。重复的消息会获得首次消息相同的应答，且不会重复创建或裁定任何转账。
* **您的审核，适用于 `GTR` 和 `SYGNA`。** 等待您处理的转账（`UNCONFIRMED_OWNERSHIP`、`ON_HOLD` 或 `COUNTERPARTY_MISMATCHED_DATA`）需通过 [`POST .../travel-rule/ownership/`](#确认钱包归属) 进行裁定：`confirmed: true` 表示批准（若转账未指定钱包，需传入 `wallet_address`），`confirmed: false` 表示拒绝。Didit 会在响应之后，按[相同的重试机制](#投递与重试)向网络应答。已应答的转账再次操作将返回 `409`。
* **`VERIFYVASP` 不支持事后应答。** VerifyVASP 会在其提出询问时即获得应答，因此需要您审核的转账会被立即拒绝，之后无法再裁定（返回 `409`）。

### 脱敏的交易对手数据

在确认钱包所有权之前，入站转账中的交易对手个人数据会以脱敏形式返回，而不是直接省略。
每个单词保留首字母，钱包、链和 VASP 路由信息保持可读。
`data_masked` 为 `true`，交易详情、列表和 CSV 导出中的交易对手信息同样脱敏。

确认所有权后，将显示真实数据，同时 `data_unmasked` 记录审计条目：

```json theme={null}
{
  "data_masked": false,
  "data_unmasked": { "at": "2026-10-17T10:00:00Z", "reason": "wallet_proof", "actor": "" }
}
```

`reason` 可为 `ownership_confirmed`、`wallet_proof`、`address_book_match`、`own_verified_wallet`、`inquiry_approved`（您已批准 `TRP` 查询，或已批准 `GTR` 或 `Sygna` 消息）、`known_payment_method`（您的记录中该终端用户的钱包已作出响应）或 `travel_address`（每个钱包的 Travel Address 指明了该钱包）。
`INTERNAL` 入站转账从创建时起即带有 `data_unmasked`，并带有 `reason: wallet_proof`，因为只有证明了钱包的收款方才会收到该记录。
`actor` 为控制台用户的邮箱或 API 密钥标签；自动决策时为空。
**Travel Rule 转账日志**报告（CSV 和 PDF）在确认所有权之前，同样对入站转账的姓名进行脱敏。您向监管机构提交的报告，例如 goAML XML 和监管报告 PDF，包含交易对手的真实数据。

### 不匹配反馈

`COUNTERPARTY_MISMATCHED_DATA` 转账现在会列出存在差异的字段。
`data_match` 保存比对结果，`mismatched_fields` 列出这些字段。
两者只包含字段名称和比对结果，绝不包含字段值，因此在数据脱敏时仍可读：

```json theme={null}
{
  "status": "COUNTERPARTY_MISMATCHED_DATA",
  "mismatched_fields": ["date_of_birth"],
  "data_match": {
    "matched": false,
    "mismatched_fields": ["date_of_birth"],
    "fields": {
      "name": { "result": "matched", "strictness": "DEFAULT" },
      "date_of_birth": { "result": "mismatched", "strictness": "STRICT" },
      "address": { "result": "not_compared", "strictness": "NONE" },
      "account": { "result": "matched", "strictness": "STRICT" }
    }
  }
}
```

时间线条目会列出字段名称，例如 "beneficiary date of birth does not match wallet holder"。
`TRP` 在不匹配后发出的拒绝会告知发起方 VASP 哪些字段存在差异，例如 "Beneficiary data does not match the wallet holder: name"。
对方 VASP 获知的信息是有意限制的：姓名不一致时，只报告姓名这一字段（`mismatched_fields: ["name"]`，其他所有字段为 `not_compared`）；姓名一致后，所有存在差异的字段都会被列出。这适用于 `INTERNAL` 交换中的发送方一侧，以及 `TRP` 拒绝。
双方均缺失的姓名显示为 `not_compared`，不视为不匹配；国家按其所指代的国家进行比对，因此 `ES` 与 `ESP` 视为一致。

#### 姓名匹配分数

每个经过姓名比对的转账都带有 `name_match`，做出判定的时间线条目会在其 `detail` 旁边携带同一对象：

```json theme={null}
{
  "name_match": {
    "score": 90,
    "matched": false,
    "method": "sequence_ratio",
    "strictness": "FUZZY",
    "compared_with": "wallet_holder",
    "transfer_name": "Ana Diaz",
    "compared_name": "Ana Diaz Garcia",
    "matched_names": ["Ana", "Diaz"],
    "mismatched_names": ["Garcia"]
  }
}
```

* `score` 是两个完整姓名的相似度，范围为 0 到 100；任一姓名为空时为 `0`。`matched` 是按 `strictness` 得出的结果，因此两者可能不一致：`FUZZY` 仅根据其自身的相似度比率 `0.85` 作出判定。
* `matched_names` 是转账姓名中在对方姓名里找到的单词；`mismatched_names` 是任一方拥有而另一方缺少的单词。
* `method` 可为 `none`、`exact_token`、`token_subset`、`token_containment` 或 `sequence_ratio`，分别对应 `NONE`、`STRICT`、`DEFAULT`、`CONTAINS` 和 `FUZZY`。
* `compared_with` 可为 `wallet_holder`（该钱包记录的持有人）或 `counterparty_vasp`（交易对手在邮件回复页面给出的姓名，或 Didit 测试 VASP 的回复）。
* 转账结果以 `data_match` 为准，它会比对所有字段，因此转账可能为 `COUNTERPARTY_MISMATCHED_DATA`，而 `name_match.matched` 仍为 `true`。
* `name_match` 会出现在 Travel Rule 端点返回的转账对象中，以及交易中嵌入的 `travel_rule` 中；在未比对姓名之前为 `null`。转账列表和 Webhook 不包含该字段。
* 当双方处于[脱敏](#脱敏的交易对手数据)状态时，以及在 `INTERNAL` 交换的另一位客户的副本上，姓名会被隐藏。

### 消息与证件请求

两家 VASP 可以在 Didit 内解决有争议的转账，无需通过邮件。
`GET` 和 `POST /v3/transactions/{transaction_id}/travel-rule/messages/` 用于列出和发送转账的消息：

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/{transaction_uuid}/travel-rule/messages/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind": "DOCUMENT_REQUEST", "body": "Please confirm the beneficiary date of birth.", "requested_documents": ["Proof of address"]}'
```

* `kind` 为 `MESSAGE`（默认值）或 `DOCUMENT_REQUEST`。证件请求至少需要 `requested_documents` 中的一项（最多 10 项）；消息则需要 `body`（最多 5000 个字符）或一个文件。
* `reply_to` 指向同一转账中的较早消息。对方回复中若附带证件，则会满足其所回应的证件请求，并在该消息上设置 `fulfilled_at`。您自己的回复不会满足您自己的请求。
* 使用 multipart 发送时，通过 `files` 最多可附加 5 个文件，每个文件最大 10 MB，格式为 PDF、PNG、JPEG 或 WebP。Didit 会检查每个文件的实际内容，而不仅是其声明的类型；任何其他文件（无论其声明的类型如何）都会使请求针对 `files` 返回 `400`（"Attach PDF, PNG, JPEG or WebP files only."）。
* 每笔转账最多包含 100 条消息，其中最多 20 条可带附件（每条最多 5 个文件），双方合计计算；超出任一上限时，请求返回 `400`。消息附件不计入交易本身的证件。
* 消息支持 `INTERNAL` 和 `EMAIL` 通道。在 `INTERNAL` 通道中，另一位 Didit 客户会在其一侧的配对转账中看到该消息。在 `EMAIL` 通道中，系统会向交易对手发送邮件，对方通过领取门户作出回复。若通知邮件无法发送，消息仍会保存，请求返回 `201`。
* 以下情况请求返回 `409`，并在 `detail` 中注明原因：经由 `TRP`、`GTR`、`TRUST`、`VerifyVASP` 或 `Sygna` 传输的转账（这些协议不支持消息）；尚无交易对手的转账；数据与钱包持有人不匹配的 `INTERNAL` 转账（收款方未收到任何内容）；收款方尚未证明对钱包的控制权的 `INTERNAL` 转账（在其证明之前，双方均受此限制）；交易对手已不再拥有合规邮箱地址的 `EMAIL` 转账；以及领取链接已过期或转账已关闭的 `EMAIL` 转账。
* 交易对手回复时不会触发任何 Webhook。请读取消息端点。
* 告知交易对手有新消息的邮件以英文发送。

### 地址簿

注册您控制的钱包，使入站转账（`INTERNAL` 通道、入站 `TRP` 查询，以及[存款后注册](#存款后的-sunrise-登记)）能够与之匹配。使用 `POST`/`GET /v3/travel-rule/wallet-addresses/` 以及 `PATCH`/`DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/` 进行管理。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
    "chain": "ETH",
    "holder_name": "Ana Diaz",
    "holder_vendor_data": "user-042",
    "self_declared": true
  }'
```

| 字段 | 类型 | 说明 |
| - | - | - |
| `address` | string | 地址。在同一应用、`chain` 和 `memo` 内唯一（地址比较不区分大小写），重复时返回 `400`。XRP X-address 会以其经典地址及对应标签存储。通过[哈希导入](#批量导入钱包)的条目，此字段为空。 |
| `address_hash` | string | 只读。小写地址的 SHA-256 哈希值，以小写十六进制表示。 |
| `memo` | string | 可选。在[需要 memo 的网络](#memo-与标签网络)上，属于该钱包的目标标签或备注；对整个地址而言为空。别名：`destinationTag`。 |
| `chain` | string | 可选的链标识符，最多 32 个字符。请使用转账中携带的相同标识（例如 `ETH` 或 `BTC`），或留空以匹配任意链上的转账，详见下方说明。登记在比特币上的 Lightning 收款方（节点 ID 或 Lightning Address）会以小写形式存储为 `LIGHTNING`，[Lightning](#lightning) 转账即通过该值匹配。 |
| `holder_name` | string | 钱包持有人的姓名，用于收款人姓名匹配。 |
| `holder_vendor_data` | string | 您用于内部标识该持有人的标识符。 |
| `entity_type` | string | 自由文本，最多 16 个字符，默认值为 `"individual"`。 |
| `travel_address` | string | 可选。交易对手 VASP 针对该钱包的 Travel Address（如您已知晓）。设置后，Didit 会通过 `TRP` 通道而非邮件，将转账路由至该收款人。 |
| `receiving_travel_address` | string | 只读。您为该钱包分配的专属 Travel Address。将其提供给向该钱包付款的 VASP，其 TRP 查询便会直接指向此钱包。对于记录为其他 VASP 的钱包、Travel Rule 关闭期间，以及您的 VASP 资料保存之前，此字段为空。钱包删除后，此地址即停止响应。 |
| `is_ownership_verified` | boolean | 只读。该地址的所有权是否已获证明（通过[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)或自我声明）。 |
| `self_declared` | boolean | 只写，仅限 `POST`。当值为 `true` 时，该条目会通过 `SELF_DECLARATION` 证明直接创建为已验证状态。发往该地址的入站转账将完全跳过 `UNCONFIRMED_OWNERSHIP`，直接进入姓名匹配。若您设置中的 `allow_self_declaration` 已关闭，则返回 `400`。 |

`PATCH` 接受 `holder_name`、`holder_vendor_data`、`entity_type` 和 `travel_address`，且只返回这四个字段。
`DELETE` 删除该条目（返回 `204`）。其地址随即释放，可重新登记，并立即停止匹配新的转账。

> [!NOTE]
>
> 地址匹配不区分大小写，在备注网络上还包括 `memo`，并受 `chain` 限定。
> 转账携带链信息时，只有当条目的 `chain` 与之相同（不区分大小写）或为空时，条目才会匹配。
> 链名称按文本比较，不会做翻译，因此存储为 `ethereum` 的条目不会匹配链为 `ETH` 的转账。
> 出站转账的链依次取自 `beneficiary_data.chain`、`transaction_details.crypto_params.crypto_chain`、`custom_properties.crypto_chain`，然后是币种（例如 `ETH`；对于 `USDT` 和 `USDC`，则由 `payment_reference_id` 推断出链）。仅当 Didit 能识别该链时才会设置。充值后登记的条目则取其 `chain` 字段，并转为小写。
> 入站 `TRP` 查询不考虑链：它们按地址匹配；若查询未指定钱包，则按收款人姓名与您已验证的条目匹配。
> [`POST .../travel-rule/ownership/`](#确认钱包归属) 同样仅按地址匹配。
> 多个条目匹配时，`INTERNAL` 通道和充值后登记会优先选择已验证所有权的条目，其次选择最早的条目；出站 `TRP` 查找则取存有 Travel Address 的最早条目。
> 入站 `TRP` 查询和所有权 endpoint 会取任一匹配条目，因此请尽量为每个地址只保留一个条目。
> 当同一地址在其他多个应用中存在已验证条目时，`INTERNAL` 通道将完全不匹配任何条目。

#### 批量导入钱包

使用 [`POST /v3/travel-rule/wallet-addresses/import/`](https://docs.didit.me/zh/management-api/travel-rule/import-wallet-addresses) 可在一次调用中导入最多 10,000 个钱包。数据格式可以是 JSON（`{"entries": [...]}` 或纯数组），也可以是 UTF-8 编码、不超过 5 MB、带表头行的 CSV 文件。
每一行通过 `address`、`address_hash`（小写地址的 SHA-256，64 位十六进制或 44 位 base64 字符），或两者同时指定钱包，并可包含上述任意条目字段。若只发送哈希，即可在不披露地址的情况下登记钱包。它会像其他条目一样匹配发往该地址的转账，并可通过[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)予以证明。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/import/ \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@wallets.csv;type=text/csv"
```

```text theme={null}
address,address_hash,chain,memo,holder_name,holder_vendor_data
0xBeneficiaryWallet01,,ethereum,,Ana Diaz,user-042
,4f7d2e1c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e,bitcoin,,,user-043
rExchangeSharedAddress01,,XRP,104729,Ana Diaz,user-042
```

导入是幂等的，并按发送顺序报告每一行的结果：`created`、`existing`（相同的钱包、链和 memo 已在名录中，保持不变）、`duplicate`（与前面某行重复，由 `duplicate_of_row` 指明）或 `failed`（附带其 `errors`）。
单行错误不会影响其他行：没有行失败时返回 `200`，有行失败时返回 `207`，并在 `total`、`created`、`existing`、`duplicate` 和 `failed` 中给出计数。
CSV 中的空单元格视为未发送，导入程序不读取的列会被忽略，因此来自其他工具的导出文件可原样导入。

在 `GET /v3/travel-rule/transfers/` 中，每一行除两个钱包外，还包含 `originator_memo` 和 `beneficiary_memo`。

### 针对无法触达的交易对手的邮件领取

当没有任何协议通道能够触达交易对手时，Didit 会回退到 `EMAIL`：向 `beneficiary_data.counterparty_vasp_email` 发送一次性领取链接。若未提供该邮箱，则根据 `beneficiary_data.counterparty_vasp_name`，从 VASP 目录注册表中解析出邮箱。
收件人无需 Didit 账户或 API 密钥，链接中的令牌即为身份验证。
请求的发送方式如下：

* **发往正确的地址。** 您传入的 `counterparty_vasp_email` 是唯一收件人。否则，Didit 最多向三个地址发送：交易对手的[联系人](#联系人)中未退信的地址（先是您自己的联系人，然后是平台联系人，之前已回复过的联系人优先），若仍有空位，则再发送至目录条目的合规邮箱。每个地址各收到一封邮件。
* **以您的名义。** 邮件由 Didit 的地址发出，发件人显示为“*您的法定名称* via Didit”，语言依据交易对手所在司法辖区确定（未知时使用英语）。链接会以该语言打开回复页面。您不会收到副本，请通过转账的时间线跟进。
* **附带提醒。** 在交易对手回复之前，Didit 会在截止时间前 72、24 和 6 小时提醒对方。提醒至少间隔上次请求或提醒后的 12 小时，且绝不会在截止时间之后发送。
* **处理退信。** 退信或垃圾邮件投诉会将其所触达的联系人标记为无效，并注明原因，转账时间线中也会记录。平台联系人随后对所有 Didit 客户均无效；您应用自己的联系人仅对您的应用无效。当 Didit 的邮件投递对某个地址完全停止发送时（在永久退信、垃圾邮件投诉或取消订阅之后），该地址上的每个联系人都会被标记为无效（`suppressed`）；恢复向该地址发送后，这些联系人会重新变为有效。当没有可发送的地址时，转账变为 `COUNTERPARTY_VASP_NOT_REACHABLE`，并在时间线中记录接收邮件服务器给出的原因。您传入的 `counterparty_vasp_email` 因为是唯一收件人，若出现永久退信或垃圾邮件投诉，会立即导致上述结果；而临时退信（`soft_bounce`）若出现在该地址或目录条目自身的合规邮箱上，则请求保持开放，并继续发送提醒。

该链接会打开一个托管页面，展示待处理的信息交换，并为收款方调用以下两个公开 endpoint。
以上方的[示例转账](#发送转账)为例，领取信息如下：

```bash theme={null}
curl https://verification.didit.me/v3/travel-rule/pickup/{token}/
```

```json theme={null}
{
  "reference": "33333333-4444-5555-6666-777777777777",
  "requested_at": "2026-07-05T10:00:00Z",
  "originator_vasp_name": "Origin CASP SL",
  "originator_vasp_lei": "529900EXAMPLEORIG001",
  "recipient_vasp_name": "Example Counterparty VASP",
  "language": "es",
  "amount": "0.250000000000000000",
  "currency": "ETH",
  "fiat_amount": "",
  "fiat_currency": "",
  "chain": "ETH",
  "beneficiary_wallet": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
  "status": "AWAITING_COUNTERPARTY",
  "ivms_version": "ivms101.2023",
  "deadline_at": "2026-07-07T10:00:00Z",
  "reminders_sent": 0,
  "contact": "",
  "decline_reasons": ["data_mismatch", "not_our_customer", "wallet_not_hosted", "sanctions", "risk_appetite", "jurisdiction", "insufficient_data", "other"],
  "join_invitation": { "eligible": false, "url": "" },
  "originator_data": { "name": "Jane Doe", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
  "beneficiary_data": { "name": "Ana Diaz", "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb" },
  "ivms_request": {
    "originator": {
      "originatorPersons": [{ "naturalPerson": { "name": { "nameIdentifier": [{ "primaryIdentifier": "Doe", "secondaryIdentifier": "Jane", "nameIdentifierType": "LEGL" }] } } }],
      "accountNumber": ["0x77ccff0cc624a53cd8b8d8703ced826710211ec7"]
    },
    "beneficiary": {
      "beneficiaryPersons": [{ "naturalPerson": { "name": { "nameIdentifier": [{ "primaryIdentifier": "Diaz", "secondaryIdentifier": "Ana", "nameIdentifierType": "LEGL" }] } } }],
      "accountNumber": ["0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb"]
    }
  },
  "data_masked": false
}
```

`reference` 为转账的 `uuid`。`originator_data` 以及由其构建的 IVMS 101 `ivms_request` 仅包含您在 [`data_sharing_fields`](#数据共享) 中共享的汇款人字段；姓名和钱包始终包含在内。当您未发送换算后的法币值时，`fiat_amount` 和 `fiat_currency` 为空；`contact` 返回链接中 `?contact=` 的值；`decline_reasons` 列出 `decline_reason` 可接受的值。当交易对手持有有效的 Didit 加入邀请时，`join_invitation.eligible` 为 `true`；邀请链接本身仅通过电子邮件发送。

该 endpoint 原样返回存储的金额，因此保留完整的 18 位小数，而不是交易详情对象所使用的截断格式。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/respond/ \
  -H "Content-Type: application/json" \
  -d '{"decision": "accept", "beneficiary_name": "Ana Diaz"}'
```

`decision` 的值为 `accept` 或 `decline`。
选择 `accept` 时，提交的 `beneficiary_name` 会按您的姓名规则与转账的 `beneficiary_data.name` 进行比对：匹配则状态为 `COMPLETED`，否则为 `COUNTERPARTY_MISMATCHED_DATA`。交易对手也可以改为通过 `ivms_response` 发送完整的 IVMS 101 响应；此时其中的收款人姓名优先于 `beneficiary_name`。
选择 `decline` 时，转账状态变为 `COUNTERPARTY_VASP_GENERAL_DECLINE`。交易对手可从 `decline_reasons` 中选择一个 `decline_reason`，并附上 `decline_detail`（最多 512 个字符，当原因为 `other` 时必填）。您转账的时间线会记录这些信息，例如 `counterparty declined: The beneficiary is not our customer`。
响应为结果 `{"status": "..."}`；拒绝时另外返回 `decline_reason`。交易对手作出答复后，两个 endpoint 均返回 `410`。
对于未知的 token，两个 endpoint 返回 `404`；时限结束后返回 `410`，即转账已离开 `AWAITING_COUNTERPARTY` 状态，或确认截止时间（自创建或最后一次重新发送起的 `confirmation_timeout_hours`）已过。

交易对手通过领取链接使用 `GET` 和 `POST /v3/travel-rule/pickup/{token}/messages/` 读取和回复。无需 API 密钥，且仅接受 PDF、PNG、JPEG 或 WebP 附件；链接过期或转账关闭后，返回 `410`。
您发送的消息会将您的 VASP 名称显示为 `author` 和附件的 `uploaded_by`，而不会显示背后的个人或 API 密钥。

### 未匹配的存款

入站存款可能早于发起方 VASP 的 Travel Rule 数据到达。
在数据到达之前，此类存款将保持为 `AWAITING_COUNTERPARTY` 或 `NOT_ENOUGH_COUNTERPARTY_DATA` 状态。
使用具有 transactions 权限的 API 密钥列出仍在等待的存款：

```bash theme={null}
curl "https://verification.didit.me/v3/travel-rule/unmatched/" \
  -H "x-api-key: YOUR_API_KEY"
```

响应使用与 `GET /v3/travel-rule/transfers/` 相同的行格式，仅限于等待发起方 VASP 数据的入站存款。

当数据以单独收到的消息形式到达时，请自行将两者配对：

```bash theme={null}
curl -X POST "https://verification.didit.me/v3/travel-rule/unmatched/{transaction_uuid}/match/" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transaction_id": "<uuid of the received message transaction>"}'
```

`{transaction_uuid}` 为该存款。
`transaction_id` 为所收到的 Travel Rule 消息对应的交易。
该调用返回 `{ "deposit": ..., "message": ... }`。
当任一交易已不存在时返回 `404`；当任一方已进入后续状态时，返回 `409` 及 `travel_rule_match_conflict`（"This deposit can no longer be matched."）。

### 存款后的 sunrise 登记

当加密货币存款在任何 Travel Rule 信息交换发生**之前**已在链上完成结算时（例如发送方 VASP 无法联系，或您需要回填历史存款），请使用 `POST /v3/travel-rule/inbound/` 事后登记该存款。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/inbound/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "eth",
    "txid": "0xf103cfc37f6d390b61827987b9dc631b5e1d2bfeb008a780c1e38047ae5d557b",
    "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb",
    "amount": "500.00",
    "currency": "USDC",
    "originator_data": { "name": "Carlos Ruiz", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
    "beneficiary_data": { "name": "Ana Diaz" },
    "originating_vasp": {
      "name": "Northwind Digital Assets Ltd",
      "lei": "5493001KJTIIGC8Y1R12",
      "travel_address": "tareGfwAzkySLkFzcqr7AKno3K4VsT3jayMwPvj3ovbFZHzpayHLmgCrAMoLqjfCYRPpvLqr9dSr2VVTFAncwChcMjL4AheNuHCJM8NRcUg"
    }
  }'
```

`originating_vasp.travel_address` 必须是真实且校验和有效的 [Travel Address](#travel-address)；占位符会返回 `400`，并附带 `"Invalid travel address."`。
`originating_vasp.lei` 必须为空，或为 LEI 格式的 20 个字符（前 18 位为字母或数字，后 2 位为数字）；其他任何值都会返回 `400`，并附带 `"Invalid LEI: expected 20 alphanumeric characters (ISO 17442)."`。
Didit 仅校验此格式，不校验 ISO 17442 的校验位。

| 字段 | 类型 | 说明 |
| - | - | - |
| `chain` | string | 存款完成结算所在的链，最多 32 个字符。Didit 会将其转为小写，并用它匹配您的[地址簿](#地址簿)条目，因此请使用与条目相同的 token（`eth` 可匹配以 `ETH` 存储的条目）。它还决定该存款能否进行筛查：`bitcoin`/`btc`、`ethereum`/`eth`、`solana`/`sol`、`tron`/`trx`、`polygon`/`matic`、`bsc`/`bnb`、`litecoin`/`ltc`、`ripple`/`xrp`、`bitcoin-cash`/`bch` 和 `dogecoin`/`doge`。 |
| `txid` | string | 链上交易哈希。用于去重：相同的 `txid` + `wallet_address` 会返回已有的转账，而不会创建重复记录。 |
| `wallet_address` | string | 您接收存款所在的目标钱包。XRP 的 X-address 会被拆分为经典地址和 tag。 |
| `memo` | string | 可选。[memo 网络](#memo-与标签网络)上存款的目标 tag 或 memo：在共享存款地址上，它指明该存款所属的客户。 |
| `amount` | string（十进制） | 存款金额。 |
| `currency` | string | 存款资产。 |
| `originator_data` | object | 发送方 VASP 提供的汇款人详情（如已知），为扁平对象：Didit 读取 `name`（或 `full_name`）、`first_name`、`last_name` 和 `vendor_data`。 |
| `beneficiary_data` | object | 收款人详情（您的客户），为扁平对象：Didit 将其 `name` 与匹配的地址簿条目的 `holder_name` 进行比对，并向其添加 `wallet_address` 和 `chain`。 |
| `utxo` | object | 可选，仅适用于 [UTXO 链](#utxo-转账)。存款交易的 `inputs` 和 `outputs`：若您声明了发送方钱包，则为 `originator_data.wallet_address`，否则为最大的输入。 |
| `originating_vasp` | object | 可选的 `{ name, lei, travel_address }`，用于描述发送方。`lei` 必须符合上述 LEI 格式，`travel_address` 必须为有效的 Travel Address，否则请求返回 `400`。它保存在本转账的 `metadata.originating_vasp` 中；它绝不会创建或更改共享的[目录](#条目如何进入目录)条目。 |

Didit 会创建一笔入账的 `travelRule` 交易（如果你已提交过相同哈希和钱包的入账充值，且该充值尚无 Travel Rule 转账，则复用该充值），并根据地址及其 memo，在你的[钱包地址簿](#地址簿)中解析该转账：

* 没有匹配条目 -> `ON_HOLD`。
  使用 [`POST .../travel-rule/ownership/`](#确认钱包归属) 解析它：`confirmed: true` 会将该钱包以已验证状态添加到你的地址簿中，并执行姓名匹配；若匹配成功，转账先变为 `COMPLETED`，再变为 `FINISHED`。
* 条目存在但所有权未经证明 -> `UNCONFIRMED_OWNERSHIP`。
* 收款人姓名与条目的 `holder_name` 不一致 -> `COUNTERPARTY_MISMATCHED_DATA`。
* 条目已验证且姓名匹配 -> `COMPLETED`，随后立即变为 `FINISHED`（该充值已完成结算）。

对于[上文示例](#地址簿)中已登记的已验证地址簿条目，响应如下：

```json theme={null}
{
  "transaction_id": "44444444-5555-6666-7777-888888888888",
  "txn_id": "tr-in-0xf103cfc37f6d390b61827987b9dc631b5e1d2bfeb008a780c1e38047ae5d557b-6151d81b366e",
  "created": true,
  "travel_rule": {
    "uuid": "55555555-6666-7777-8888-999999999999",
    "status": "FINISHED",
    "direction": "INBOUND",
    "rail": null,
    "protocol": null,
    "ivms_version": "ivms101.2023",
    "required": true,
    "ownership_confirmed": true,
    "deadline_at": null,
    "timeline": [
      { "status": "COMPLETED", "at": "2026-07-06T10:15:02.114208+00:00", "detail": "matched verified wallet address book entry" },
      { "status": "FINISHED", "at": "2026-07-06T10:15:02.131544+00:00", "detail": "on-chain deposit already settled" }
    ],
    "counterparty_vasp": null,
    "originator_data": { "name": "Carlos Ruiz", "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" },
    "beneficiary_data": { "name": "Ana Diaz", "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb", "chain": "eth" },
    "created_at": "2026-07-06T10:15:02.087412Z"
  }
}
```

新交易的 `txn_id` 为 `tr-in-` 加上 `txid` 的前 80 个字符，再加上由 `txid` 和钱包计算出的 12 位哈希。
当本次调用登记了该充值时，`created` 为 `true`（HTTP `201`）；当相同的 `txid` 和 `wallet_address` 已登记时，则为 `false`（HTTP `200`），此时原有转账原样返回。
在已验证条目的路径上，两次状态转换都在端点响应之前完成，因此同步响应已显示终态 `FINISHED`，中间的 `COMPLETED` 步骤仍会记录在转账的 `timeline` 中。
每次状态转换都会触发一个 `travel_rule.status.updated` Webhook（`COMPLETED` 和 `FINISHED` 各触发一次）。
若 Didit 创建了新交易，则先发送 [`transaction.created`](https://docs.didit.me/zh/integration/webhooks#事件类型)，之后再发送这些事件。
登记不收取任何 Travel Rule 或监控费用。
启用加密货币监控且 `chain` 为 Didit 可筛查的链时，新创建的充值也会被筛查，并按 Wallet Verification & Screening 费率计费一次。

### 钱包所有权小组件

若要将转账移出 `UNCONFIRMED_OWNERSHIP`，或在任何转账到达之前预先验证钱包，请通过 `POST /v3/travel-rule/widget-session/` 生成托管的钱包所有权小组件，并将客户引导至返回的 `url`。该小组件通过消息签名（包括 EVM 智能合约账户（通过 ERC-1271 和 ERC-6492））、Satoshi test、截图或自我声明来证明对钱包的控制权，然后将匹配的地址簿条目标记为已验证，以便交易所继续处理。
截图是例外：它本身永远无法验证钱包，因此你的合规团队需要审核截图，并使用 [`POST .../travel-rule/ownership/`](#确认钱包归属) 确认处于 `UNCONFIRMED_OWNERSHIP` 的转账。

有关完整流程、证明方法，以及小组件必须在真实浏览器中打开、绝不能在移动端 SDK 的 webview 内打开这一重要说明，请参阅[钱包所有权小组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)。

## 目录

`GET /v3/travel-rule/vasps/` 用于搜索 VASP 目录：即共享目录条目以及 [Didit 网络](#didit-网络) 的成员。每一行都会说明您的应用目前如何联系该 VASP。

```bash theme={null}
curl "https://verification.didit.me/v3/travel-rule/vasps/?search=counterparty" \
  -H "x-api-key: YOUR_API_KEY"
```

```json theme={null}
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91",
      "kind": "directory",
      "name": "Example Counterparty VASP",
      "lei": "529900EXAMPLECPTY001",
      "jurisdiction": "DE",
      "domains": ["counterparty.example"],
      "reachable": true,
      "aliases": [{ "name": "Example Counterparty", "kind": "trading_name" }],
      "networks": [
        {
          "network": "GTR",
          "code": "EXAMPLE01",
          "listed_name": "Example Counterparty VASP",
          "country": "DE",
          "verified_at": "2026-10-12T09:30:00Z",
          "last_seen_at": "2026-10-12T09:30:00Z",
          "source": "staff"
        }
      ],
      "routes": [
        { "rail": "GTR", "status": "unavailable", "reason": "no_membership", "via": "" },
        { "rail": "EMAIL", "status": "available", "reason": "", "via": "contact" }
      ],
      "umbrella": null,
      "entities": [],
      "contacts": [
        {
          "id": "6c1f9b8e-2d4a-4f7e-8b3c-5a9d0e1f2a3b",
          "email": "travelrule@counterparty.example",
          "name": "Travel Rule desk",
          "source": "staff",
          "scope": "platform",
          "verified_at": null
        }
      ],
      "dd_status": "NOT_STARTED",
      "dd_score": 62,
      "dd_score_breakdown": {
        "jurisdiction": { "points": 25.0, "max": 25, "reason": "jurisdiction_not_high_risk", "detail": "DEU is not on the high-risk jurisdiction list" },
        "verification": { "points": 3.8, "max": 15, "reason": "dd_status_not_started", "detail": "due diligence status NOT_STARTED" },
        "protocol": { "points": 6.0, "max": 15, "reason": "email_only", "detail": "reachable over the email fallback only" },
        "lei": { "points": 10.0, "max": 10, "reason": "lei_valid", "detail": "valid LEI on record" },
        "history": { "points": 5.0, "max": 10, "reason": "no_history", "detail": "no exchange history" },
        "essentials": { "points": 7.5, "max": 15, "reason": "no_essential_checks", "detail": "no decided essential check yet" },
        "responsiveness": { "points": 5.0, "max": 10, "reason": "responsiveness_unknown", "detail": "0 decided recent requests, too few to judge" }
      },
      "dd_assessed_at": "2026-10-12T09:30:00Z",
      "dd_score_semantic": "higher_is_safer",
      "risk_level": "MEDIUM",
      "responsiveness": { "status": "UNKNOWN", "answered": 0, "total": 0, "computed_at": "2026-10-12T09:30:00Z" },
      "essential_checks": {},
      "reachable_rails": ["GTR", "EMAIL"],
      "is_didit_customer": false,
      "decision": {
        "decision": "APPROVED",
        "reason": "Licensed in DE",
        "decided_by": "",
        "updated_at": "2026-10-14T10:05:00Z",
        "inherited_from": null
      },
      "entity_screening": {
        "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "status": "Approved",
        "total_hits": 0,
        "score": null,
        "screened_at": "2026-10-14T09:58:00Z",
        "fresh": true,
        "flagged": false
      },
      "questionnaires": []
    }
  ]
}
```

`search` 可匹配名称或别名片段、精确匹配的 LEI，或域名（`counterparty.example` 或其上的 URL）。使用以下参数缩小结果范围：

| 参数 | 作用 |
| - | - |
| `network` | `GTR`、`TRUST`、`VERIFYVASP`、`SYGNA` 或 `TRISA`：仅返回该网络成员目录当前列出的条目。 |
| `jurisdiction` | ISO 3166 alpha-2 或 alpha-3 国家/地区代码。 |
| `reachable` | `true`：仅返回您的应用有可用路由的 VASP。`false`：仅返回目前无法联系的条目。 |
| `kind` | `directory` 表示共享条目，`didit` 表示 Didit 网络成员。 |
| `risk` | `LOW`、`MEDIUM` 或 `HIGH`：仅返回 `risk_level` 属于该等级的目录条目。参见 [评分与风险等级](#评分与风险等级)。 |
| `responsiveness` | `RESPONSIVE`、`UNRESPONSIVE` 或 `UNKNOWN`：仅返回 `responsiveness.status` 为该值的目录条目。参见 [响应度](#响应能力)。 |

Didit 网络成员不参与评分，因此 `risk` 和 `responsiveness` 仅返回目录条目。结果按名称排序，并通过 `limit`（默认值为 `50`）和 `offset` 进行分页。未知的网络、国家/地区代码、风险等级或响应度状态会返回 `400`，并指明出错字段。`GET /v3/travel-rule/vasps/{vasp_id}/` 通过 `id` 返回一行数据。您自己的 VASP 资料永远不会出现在您自己的结果中。

`dd_status`、`dd_score`、`dd_score_breakdown`、`dd_assessed_at`、`dd_score_semantic`、`risk_level`、`responsiveness` 和 `essential_checks` 承载目录条目的共享尽职调查评估。`decision`、`entity_screening` 和 `questionnaires` 是您的应用对该 VASP 的自有记录，其他客户看不到。关于各字段的含义，参见 [交易对手尽职调查](#交易对手尽职调查)；应使用[交易对手规则字段](#规则字段与预设)据此设置门控，而不是在代码中内联分支判断。Didit 网络成员不参与评分：`dd_status` 为 `COMPLETED`，`dd_score` 为 `null`，`risk_level` 为空，明细也为空。

### 路由

`routes` 列出您的应用可向该 VASP 投递的方式，顺序即路由阶梯的尝试顺序；至少有一个为 `available` 时，`reachable` 为 `true`：

* `TRP`：当条目带有目录已验证的 TRP endpoint 时。
* `GTR`、`TRUST`、`VERIFYVASP`、`SYGNA`：当某个网络以某个代码列出该 VASP 时。仅当您的应用目前可以通过该网络发送时，路由才为 `available`（`via: own_membership` 表示您自己已连接的成员身份，`via: didit_membership` 表示 Didit 的成员身份）。否则路由为 `unavailable`，并附带 `reason: no_membership` 或 `reason: network_switched_off`，绝不会显示为联系该 VASP 的方式。Didit 自有的网络成员身份尚未上线，因此目前网络路由仅在您通过 Marketplace 连接的成员身份下可用。
* `EMAIL`：当该条目有合规联系人时。
* `INTERNAL`：适用于 Didit 网络成员。

`TRISA` 列表会出现在 `networks` 下，告诉您该 VASP 在 TRISA 上的身份，但 Didit 没有 TRISA 通道，因此它永远不会成为路由。

沙盒应用仅向 [Didit 测试 VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md) 发送，因此对它而言没有可用的目录路由：每条路由均为 `unavailable` 且附带 `reason: sandbox_application`，每一行的 `reachable: false`，`reachable=true` 不返回任何行。

### 品牌及其实体

通过多个受监管实体运营的品牌是一行数据，其 `entities` 为接收其转账的法律实体；实体行通过 `umbrella` 指回其所属品牌。指定品牌的转账会发往其某一实体，从所有实体中选择：优先选择 `beneficiary_data.counterparty_vasp_jurisdiction` 中声明的司法辖区内的实体；否则选择与目的钱包关联的[区块链分析归因](#区块链分析归属)所指向的实体；若未声明司法辖区，则选择品牌下唯一的实体。只有在选定实体后，才要求该实体可达；若不可达，转账绝不会改发到其他实体或品牌本身。当没有实体符合条件时（两者同样符合，或声明的司法辖区不属于任何一个实体），使用品牌自身的路由；若没有品牌路由，转账将返回 `COUNTERPARTY_VASP_NOT_FOUND`。究竟由哪个法律实体接收汇款人的数据，绝不是猜测的结果，也绝不是由哪个实体恰好有联系人来决定的。

若多个条目以相同方式命名，例如共用同一商号的集团公司，则以同一规则加以区分：选择声明的司法辖区内的条目，否则选择区块链分析归因所关联的条目。若两者均无法确定其一，则不使用任何条目，这些信息视为未指明任何目录条目。

### 联系人

`contacts` 是 `EMAIL` 通道发送邮件的地址。`scope: platform` 的联系人服务于所有 Didit 客户：由 Didit 筛选维护（`source: staff`），或该 VASP 自有域名下的某个地址通过其回复链接回应了请求（`source: learned`）。`scope: application` 的联系人仅对您的应用私有：您添加的联系人（`source: application`），以及 VASP 自有域名之外、回应过您请求的地址（`source: learned`）。转账时，会先向您应用自己的联系人发送邮件，其次是曾经回复过的平台联系人（已设置 `verified_at`），最后是其他联系人。

使用交易对手联系人 API 管理您应用自己的联系人：

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/counterparty-contacts/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"counterparty": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", "email": "compliance@counterparty.example", "name": "Compliance desk"}'
```

* `GET /v3/travel-rule/counterparty-contacts/` 列出您的应用可以发送到的所有联系人，包括平台联系人和退信的联系人，按最新排序，并通过 `limit` 和 `offset` 分页。可使用 `?counterparty=` 加目录条目 `id` 进行筛选；不是 UUID 的值会返回 `400`。
* `POST` 添加一个私有联系人（`counterparty`、`email`，可选 `name`），返回 `201`。若您已为该条目添加过相同的地址，则返回该联系人。未知条目返回 `400`（`"No directory entry with this id."`）。
* `DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/` 删除您自己的一个联系人（返回 `204`）。平台联系人无法删除，返回 `404`。

被退信或被投诉的联系人会带有 `is_invalid: true`，并附带 `invalid_reason`（`hard_bounce`、`soft_bounce`、`spam_complaint` 或 `suppressed`）、`invalid_detail` 和 `invalid_at`，此时 `EMAIL` 通道会跳过该联系人。对于平台联系人和通过 API 密钥添加的联系人，`created_by` 为空。

### 条目如何进入目录

* ESMA 的 MiCA 加密资产服务提供商临时登记册，每周读取一次：包括每家提供商的名称、LEI 和母国成员国。
* 公开的 TRISA 目录，每周读取一次：包括每位成员是谁、其所在国家/地区及其 TRISA 列表项。目录连续三周不再显示的列表项将被移除。
* 全球 LEI 登记册用于补充条目的 LEI、其他法定名称及国家/地区，并可根据条目在其所在国家/地区的准确法定名称查找该条目的 LEI。
* [区块链分析归因](#区块链分析归属) 用于添加或匹配拥有目标钱包的 VASP，并将归因得到的名称保留为别名。
* Didit 工作人员负责整理条目，包括已核实的路由标识符和网络登记信息、合规联系人、别名、品牌及其实体、已合并的重复条目以及隐藏条目。

客户就交易对手报告的数据，例如入站的 `originating_vasp` 或旅行地址，仅保留在该客户的转账记录中，绝不会更改共享条目。条目按 LEI 优先匹配，其次按准确名称或别名匹配，并以补充信息的方式丰富现有条目，而不是重复创建。

### Didit 网络

您的 VASP 资料属于 Didit 网络，因此只有同时满足以下所有条件时，其他 Didit 客户才能在目录和终端用户的 VASP 选择器中找到它：

* 您的应用处于生产环境（非沙盒），且已开启 Travel Rule；
* `is_discoverable` 为 `true`，并且已设置 `legal_name`；
* 已设置并确认 `compliance_email`：使用 `POST /v3/travel-rule/settings/compliance-email/confirmation/` 发送确认邮件，然后打开邮件中的链接。更改该地址需要重新确认；
* Didit 未将该应用标记为测试、演示或内部应用。

`INTERNAL` 通道本身会在您已验证的钱包上完成投递，如 [发送转账](#发送转账) 中所述：它只要求您的应用处于生产环境、已开启 Travel Rule 并且 `is_discoverable`，而不要求列表中的其他条件。

`GET /v3/travel-rule/settings/` 返回 `didit_network`：其中包含 `listed` 和 `compliance_email_confirmed`，以及在 `missing` 中列出的每个仍未满足的条件（`travel_rule_off`、`sandbox_application`、`not_discoverable`、`legal_name_missing`、`compliance_email_missing`、`compliance_email_unconfirmed`、`excluded_by_staff`、`internal_organization`）。

### 路由标识符

当您的转账通过 `counterparty_vasp_lei` 或 `counterparty_vasp_name` 指定交易对手时（也可以使用别名），出站路由会查询目录条目。每个条目针对其可触达的每个网络带有一个标识符，以及一个已验证的 TRP 端点及其合规联系人；`routes` 显示您的应用可以使用其中的哪些。您始终可以通过传入 [交易对手解析字段](#交易对手解析字段) 中的显式字段，为单笔转账绕过目录。

## 交易对手尽职调查

在您客户的资金转入另一家 VASP 之前，您需要确认该 VASP 是谁，以及您是否接受它。对交易对手的尽职调查分为两个层面：

* **共享评估。** 每条目录记录都带有尽职调查评分、风险等级、响应状态和核心检查报告。这些结果由 Didit 根据平台自身的数据计算，因此每位客户看到的数值都相同。
* **您自己的记录。** 您对该 VASP 的决策、对它进行的制裁筛查和负面新闻筛查，以及您与它交换的尽职调查问卷，仅属于您的应用。其他客户看不到这些记录，它们也不会改变共享记录。

无论是目录记录还是 [Didit 网络](#didit-网络)成员，您的自有记录都适用。请使用[目录](#目录)显示的 `id` 来引用它们。

| Endpoint | 作用 |
| - | - |
| `GET` / `PUT` / `DELETE /v3/travel-rule/vasps/{vasp_id}/decision/` | 读取、设置或清除您对该交易对手的[决策](#您对交易对手的决策)。 |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/screening/` | 读取您最新的[交易对手筛查](#交易对手筛查)结果，或发起一次筛查。 |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/` | 列出您与该交易对手之间的[尽职调查问卷](#尽职调查问卷)，或发送一份问卷。 |
| `GET /v3/travel-rule/transfers/?counterparty={vasp_id}` | 您与该交易对手之间的[转账](#与单一交易对手的转账)。 |

### 评分与风险等级

`dd_score` 的范围是 0 到 100，**分数越高越安全**。每个结果都会在 `dd_score_semantic` 中说明这一点，其值始终为 `higher_is_safer`。`risk_level` 是评分所在的区间：

| `risk_level` | `dd_score` |
| - | - |
| `LOW` | 70 或以上 |
| `MEDIUM` | 40 至 69 |
| `HIGH` | 低于 40 |

评分是七个组成部分的总和。`dd_score_breakdown` 以 `{points, max, reason, detail}` 的形式列出每个部分：`max` 是该部分的权重，`reason` 是可供您翻译的机器可读代码，`detail` 是一行英文说明。

| 组成部分 | 权重 | 计分方式 | `reason` 代码 |
| - | - | - | - |
| `jurisdiction` | 25 | 条目所在国家/地区不在默认高风险司法辖区列表（`IRN`、`PRK`、`SYR`、`AFG`、`RUS`、`MMR`）中时得满分；在列表中时得零分；国家/地区未知时得一半分值。 | `jurisdiction_not_high_risk`、`jurisdiction_high_risk`、`jurisdiction_unknown` |
| `verification` | 15 | Didit 对该条目的验证状态（`dd_status`）：`COMPLETED` 得满分，`IN_PROGRESS` 得一半，`NOT_STARTED` 得四分之一，`REJECTED` 得零分。 | `dd_status_completed`、`dd_status_in_progress`、`dd_status_not_started`、`dd_status_rejected` |
| `protocol` | 15 | 当原生协议通道可以到达该条目（TRP endpoint 或 `INTERNAL` 通道）时得满分；仅有邮件备用方式时得四成；没有可用通道时得零分。 | `native_rail`、`email_only`、`no_rail` |
| `lei` | 10 | 持有有效 LEI 记录时得满分，否则得零分。 | `lei_valid`、`lei_missing` |
| `history` | 10 | 该条目已决出结果的交换中成功的比例；没有任何已决交换时得一半分值。 | `history`、`no_history` |
| `essentials` | 15 | 已决[核心检查](#核心检查)中通过的比例；尚无已决检查时得一半分值。 | `essential_checks`、`no_essential_checks` |
| `responsiveness` | 10 | `RESPONSIVE` 得满分，`UNRESPONSIVE` 得零分，`UNKNOWN` 得一半分值。请参阅[响应能力](#响应能力)。 | `responsive`、`unresponsive`、`responsiveness_unknown` |

分值四舍五入到一位小数，总分四舍五入到整数。`history` 与[响应能力](#响应能力)读取的是同一批请求，但没有时间窗口：`COMPLETED` 或 `FINISHED` 的交换算作成功，`EXPIRED`、`COUNTERPARTY_MISMATCHED_DATA` 或 `COUNTERPARTY_VASP_GENERAL_DECLINE` 的交换算作失败。取消是发送方自己的行为，从不计入。

当与该条目的交换完成、结束、过期、出现数据不匹配或被拒绝时，当条目被刷新或整理时，以及在每周目录刷新中的每个条目上，Didit 都会重新计算评估。`dd_assessed_at` 表示上一次计算的时间；尚未评估的条目没有 `dd_assessed_at`，其 `dd_score` 为 `0`，`risk_level` 为空，因此请以 `risk_level` 为准，而不要依据原始分数判断。Didit 网络成员不参与评分：其 `dd_score` 为 `null`，`risk_level` 为空，`dd_score_breakdown` 也为空。

### 核心检查

`essential_checks` 是 Didit 对每条目录记录执行的四项检查的简要报告。这些检查仅使用平台已验证的来源，从不使用客户报告的数据。

| 检查 `id` | 通过条件 |
| - | - |
| `registration` | 官方登记册列出了该 VASP：欧盟的加密资产服务提供商临时 MiCA 登记册、公开的 TRISA 目录，或已颁发 LEI 的全球 LEI 登记册。 |
| `licence` | MiCA 登记册记录了该 VASP 的一项授权，且该授权尚未终止。 |
| `domain` | 条目的域名与其 TRP endpoint 或其登记册提供的网站相符。 |
| `lei` | 条目的 LEI 记录处于有效且已颁发状态，其法定名称和国家/地区与该条目相符。 |

`checks` 中的每项检查都会报告其 `status`（`pass`、`fail`，或当没有可用来源能够判定时为 `unknown`）、机器可读的 `reason`，以及一行英文 `detail`。`passed`、`failed`、`unknown` 和 `total` 统计检查数量，`level` 为 `essential`，`checked_at` 表示报告的生成时间。`essentials` 组成部分计算已决检查中通过的比例，因此 `unknown` 检查既不计入该条目的加分，也不计入减分。在每周目录刷新首次检查某条目之前，报告为空；对于 Didit 网络成员，报告始终为空。

### 响应能力

`responsiveness` 表示该 VASP 是否回应 Travel Rule 请求：

```json theme={null}
{ "status": "UNRESPONSIVE", "answered": 0, "total": 6, "computed_at": "2026-10-12T09:30:00Z" }
```

`total` 统计过去 90 天内，Didit 客户向该 VASP 发起且结果已决的请求数量；`answered` 统计其中获得回应的请求数量。

* **计入：** 通过网络或协议通道发送的请求，以及发送至共享地址的邮件请求。共享地址指条目的合规地址或平台联系人。
* **从不计入：** 发送至某位客户输入的地址，或客户私人联系方式的邮件请求。该交换只保留在该客户自己的转账历史中，不会为所有其他客户标记该 VASP。
* **获得回应：** `COMPLETED`、`FINISHED`、`COUNTERPARTY_VASP_GENERAL_DECLINE` 或 `COUNTERPARTY_MISMATCHED_DATA`。拒绝或数据不匹配也是一种回应，说明该 VASP 已参与交换。
* **未获回应：** `EXPIRED`。
* **不计入任何一类：** 其他所有状态。`CANCELLED` 请求是发送方自己的行为；`COUNTERPARTY_VASP_NOT_REACHABLE` 可能是发送方一侧的故障；仍在进行中的请求尚未有结果。

| `status` | 适用条件 |
| - | - |
| `UNKNOWN` | 时间窗口内已决请求少于 5 个，因此单个过期请求不会把某个 VASP 标记为某种状态。 |
| `UNRESPONSIVE` | 该 VASP 对其已决请求的响应率低于百分之十。 |
| `RESPONSIVE` | 其他情况。 |

Didit 网络成员在 Didit 内部作出响应，始终显示 `UNKNOWN`，且计数为零。对无响应的交易对手采取行动时，可使用[无响应策略](#sunrise-与无响应策略)或 `travel-rule-counterparty-unresponsive` [预设](#规则字段与预设)。

### 您对交易对手的决策

使用 `PUT /v3/travel-rule/vasps/{vasp_id}/decision/` 记录贵合规团队对某交易对手的结论：

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/decision/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision": "BLOCKED", "reason": "Unlicensed in its home jurisdiction"}'
```

```json theme={null}
{
  "decision": {
    "decision": "BLOCKED",
    "reason": "Unlicensed in its home jurisdiction",
    "decided_by": "",
    "updated_at": "2026-10-14T10:05:00Z",
    "inherited_from": null
  }
}
```

| `decision` | 含义 | 触发此操作的预设 |
| - | - | - |
| `APPROVED` | 贵团队已批准该交易对手。 | 无 |
| `RESTRICTED` | 向其转账经审核后可以继续进行。 | `travel-rule-counterparty-restricted` 会将这些转账保留待审核。 |
| `BLOCKED` | 贵团队拒绝了该交易对手。 | `travel-rule-counterparty-blocked` 会拒绝这些转账。 |

* 该决策仅对您的应用可见：其他客户永远看不到，它也绝不会改变共享条目。
* 决策本身不产生任何效果，也绝不会改变转账的路由方式。它通过 `counterparty_dd_status` [规则字段](#规则字段与预设)作用于您的交易：安装预设，或基于该字段编写您自己的规则。
* 对某品牌作出的决策适用于其每个尚无自身决策的[实体](#品牌及其实体)，该实体的 `inherited_from` 会显示该品牌。对实体作出的决策会覆盖该实体的品牌决策。
* `GET` 返回适用的决策，或 `{"decision": null}`。`DELETE` 会清除您自己的决策，并返回之后适用的决策：对实体而言是品牌决策，否则为 `null`。若您的应用对该交易对手没有自己的决策，则返回 `404`。
* `reason` 为可选项，最多 255 个字符，每次 `PUT` 都会替换它。`decided_by` 是最近一次作出或修改决策的控制台用户；若由 API 密钥操作，则为空。

### 交易对手筛查

交易对手筛查会将交易对手 VASP 本身作为公司，对照制裁和负面新闻进行检查。它**需主动开启并收费**：在您的[设置](#设置)中使用 `counterparty_screening_enabled` 开启。关闭期间，不会进行任何筛查，也不会计费。

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/screening/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"refresh": false}'
```

```json theme={null}
{
  "outcome": "screened",
  "screening": {
    "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "status": "In Review",
    "total_hits": 2,
    "score": 85.0,
    "screened_at": "2026-10-14T09:58:00Z",
    "fresh": true,
    "flagged": true
  }
}
```

* **计费。** 每次新的筛查都会按贵组织的企业 AML 筛查[价格](https://docs.didit.me/zh/getting-started/pricing.md)收费。`GET /v3/travel-rule/vasps/{vasp_id}/screening/` 免费返回该 `price`、`fresh_days` 以及筛查是否为 `enabled`。距今不满 30 天的筛查视为有效：`POST` 会以 `200` 和 `outcome: reused` 再次返回该结果，且不收费，除非您发送 `refresh: true`。新的筛查以 `201` 和 `outcome: screened` 返回。沙盒应用从不进行筛查，也从不收费。
* **自动筛查。** 开启筛查后，Didit 还会在外发转账开始时、即您的规则评估交易之前，对该转账所涉的交易对手进行筛查，复用与计费规则相同。该处的拒绝或失败绝不会导致转账失败。
* **结果。** 当没有需要关注的可能匹配时，`status` 为 `Approved`；若存在可能的制裁或负面新闻匹配，则为 `In Review` 或 `Declined`。当有效筛查的结果为 `In Review` 或 `Declined` 时，`flagged` 为 `true`。
* **效果。** 只要您对某交易对手的最新筛查已被标记且筛查保持开启，您的规则中 `counterparty_risk_level` 即为 `HIGH`，因此一旦安装，`travel-rule-counterparty-high-risk` 预设会保留其转账。目录中的共享 `risk_level` 不会改变。
* **隐私。** 该筛查仅对您的应用可见：其他客户永远看不到，它也绝不会改变共享条目。目录会以 `entity_screening` 显示您最新的一次筛查。
* **拒绝情况。** 筛查关闭、应用为沙盒应用，或同一交易对手的筛查正在进行时，返回 `409`。余额不足以支付价格时，返回 `403` 及 `code: insufficient_credits`。筛查无法完成时，返回 `502`。以上情况均不会产生任何费用。

### 尽职调查问卷

请求交易对手提供尽职调查答复，使用 Didit 的标准问卷。`POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/` 会将问卷发送至交易对手记录在案的合规邮箱，并附上一个通往答复页面的一次性链接：

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/vasps/0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91/questionnaires/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sent_to": "travelrule@counterparty.example"}'
```

```json theme={null}
{
  "id": "3f9e8d7c-6b5a-4c3d-9e2f-1a0b9c8d7e6f",
  "status": "SENT",
  "form_version": "1",
  "sent_to": "travelrule@counterparty.example",
  "sent_at": "2026-10-14T10:00:00Z",
  "expires_at": "2026-11-13T10:00:00Z",
  "answered_at": null,
  "valid_until": null,
  "current": false,
  "answers": {}
}
```

* **发送至何处。** `sent_to` 必须是记录在案的合规邮箱：对于目录条目，可以是其合规邮箱，或贵应用在 `contacts` 中看到的联系人；对于 Didit 网络成员，则为其已确认的合规邮箱。若省略该字段，则使用发给交易对手的 Travel Rule 邮件所用的地址，依次为贵方自己的联系人、平台联系人，然后是条目的合规邮箱。Didit 绝不会把问卷发送到其他任何地方；若交易对手没有记录在案的合规邮箱，则返回 `400`。
* **作答。** 该链接会打开 `/v3/travel-rule/questionnaires/{token}/`，这是供交易对手人员使用的页面，无需账户或 API 密钥。对方需回答每个问题，每题最多 2,000 个字符（不适用时填写 `N/A`），并提交一次。交易对手也可以向同一链接发送 `{"answers": {"<question id>": "<answer>"}}`。已使用、已被替换或已超过截止时间的链接会显示过期页面。邮件和页面已翻译；问题本身始终为英文，以确保其含义在各语言间保持一致。
* **截止时间。** 交易对手有 30 天时间作答（`expires_at`）。答复保持有效 365 天（`valid_until`），在此期间 `current` 为 `true`。超过截止时间仍未作答的问卷，以及超过有效期的答复，都会变为 `EXPIRED`。
* **每个交易对手仅保留一份未完成问卷。** 新发送的问卷会替换您先前发给同一交易对手但尚未作答的问卷：旧问卷变为 `EXPIRED`，其链接失效。同一邮箱每分钟只能接收一封邮件；发送更频繁的请求会返回 `429`。
* **读取答复。** `GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/` 会按从新到旧的顺序列出您与该交易对手的问卷；作答后，`answers` 以问题 ID 为键。目录以 `questionnaires` 显示这些问卷。它们仅对您的应用可见。

表单版本 `1` 包含以下十二个问题：

| 问题 ID | 问题 |
| - | - |
| `legal_name` | 法律实体名称及注册司法管辖区。 |
| `licensing` | 在各司法管辖区持有的虚拟资产服务许可证或注册信息：监管机构、许可证编号及状态。 |
| `jurisdictions` | 您开展业务的司法管辖区，以及被排除或受限的司法管辖区。 |
| `aml_program` | 您的 AML/CFT 计划概要：政策、程序，以及上一次独立审计或审查的日期。 |
| `compliance_officer` | 合规官的姓名、职务及联系方式。 |
| `sanctions_screening` | 您如何针对制裁和负面新闻筛查客户与交易对手：服务提供商或名单、频率及升级处理。 |
| `transaction_monitoring` | 您如何监控交易中的可疑活动：工具、阈值及申报做法。 |
| `travel_rule` | 您如何遵守 Travel Rule：所用的协议和网络，以及您通常对请求的响应时间。 |
| `custody` | 客户资产的托管安排：合格托管人、保险及隔离。 |
| `ownership` | 持股比例达百分之二十五或以上的实际受益人，以及任何母公司或伞形实体。 |
| `record_keeping` | Travel Rule 和交易数据的记录保存做法及保留期限。 |
| `data_protection` | 您如何保护为合规目的共享的个人数据，以及数据的存储位置。 |

### 与单一交易对手的转账

[`GET /v3/travel-rule/transfers/?counterparty={vasp_id}`](https://docs.didit.me/zh/management-api/travel-rule/list-transfers) 会按目录中显示的 `id`，按最新排序列出您应用中与某一交易对手之间的 Travel Rule 转账：即交易对手为该目录条目的转账，或与该 Didit 网络成员之间的 `INTERNAL` 交换。无效的 id 值会返回 `400`。
每一行在 `status` 之后紧跟 `reason`：即转账自身的 [`reason`](#为什么转账会卡住)。因此无需打开每笔转账，列表即可区分被退回的合规邮件与没有路由的交易对手。

### Sunrise 与无响应策略

转账过期时（即在 `AWAITING_COUNTERPARTY` 状态下已超过 `confirmation_timeout_hours`），由 `timeout_outcome` 决定其交易的处理方式。两项设置可让您针对超时的两种常见原因进行区别处理：

| 设置 | 适用于交易对手为目录条目且 |
| - | - |
| `sunrise_policy` | 位于 `sunrise_jurisdictions` 所列的司法管辖区：即尚未执行 Travel Rule 的国家/地区，因此 VASP 没有义务回复。 |
| `non_responsive_policy` | 其 `responsiveness.status` 为 `UNRESPONSIVE`。 |

两者均可设为 `HOLD`、`REJECT` 或 `PROCEED`，含义与 `timeout_outcome` 相同；也可设为空字符串，以跟随 `timeout_outcome`。两者默认均为空，因此在您设置之前不会有任何变化：

```bash theme={null}
curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sunrise_policy": "PROCEED", "non_responsive_policy": "HOLD"}'
```

首先适用的一项决定结果：

1. `sunrise_policy`：已设置，且交易对手的司法管辖区位于 `sunrise_jurisdictions` 中。
2. `non_responsive_policy`：已设置，且交易对手为 `UNRESPONSIVE`。
3. `timeout_outcome`：其他所有情况，包括转给 Didit 网络成员或没有目录条目的交易对手的转账。

`sunrise_jurisdictions` 为只读，当前为 `["CO", "KH", "VN"]`。只有这些国家/地区被视为处于 sunrise 期间：其他任何司法管辖区，以及空白或未知的司法管辖区，均跟随 `timeout_outcome`，因此交易对手绝不会意外适用您的 sunrise 策略。已过期转账的时间线会注明所适用的结果，例如 `sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet)`。过期后，您的规则仍会运行，因此在 `PROCEED` 下，诸如 EU TFR 的 `tr_eu_expired_review` 之类的规则仍可将交易转入审核。

### 规则字段与预设

四个 [规则](https://docs.didit.me/zh/transaction-monitoring/rules.md) 字段记录您的应用所掌握的、交易的 Travel Rule 交换所抵达的交易对手信息：即其目录条目；若为 `INTERNAL` 交换，则为接收该交换的 Didit 客户。若交换未抵达任何已知交易对手，则各字段均为空。

| 字段 | 值 |
| - | - |
| `counterparty_dd_status` | 您的[决策](#您对交易对手的决策)：`APPROVED`、`RESTRICTED` 或 `BLOCKED`；若实体跟随其品牌，则为该品牌的决策。若您未作出决策则为空。绝不会是其他客户的决策。 |
| `counterparty_risk_level` | 条目的 `risk_level`（`LOW`、`MEDIUM` 或 `HIGH`）；若您对该交易对手的[筛查](#交易对手筛查)已被标记，则为 `HIGH`。尚未评估的条目为空；对于 Didit 网络成员，除非您对其的筛查已被标记，否则同样为空。 |
| `counterparty_jurisdiction` | 交易对手所在的国家/地区，取自目录条目或 Didit 网络成员的资料。ISO-2 和 ISO-3 代码均可比对，与其他国家字段相同。 |
| `counterparty_responsiveness` | 条目的 `responsiveness.status`：`RESPONSIVE`、`UNRESPONSIVE` 或 `UNKNOWN`。对于 Didit 网络成员则为空。 |

`travel_rule` 套件中的五个预设作用于上述字段。您可以在 Business Console 中从 **Transactions → Rules → Library** 安装，也可以使用[规则库 API](https://docs.didit.me/zh/management-api/transactions/rules-install.md) 安装：

| `library_key` | 触发条件 | 默认操作 |
| - | - | - |
| `travel-rule-counterparty-blocked` | `counterparty_dd_status` 为 `BLOCKED` | +90 分，`DECLINED` |
| `travel-rule-counterparty-restricted` | `counterparty_dd_status` 为 `RESTRICTED` | +50 分，`IN_REVIEW` |
| `travel-rule-counterparty-high-risk` | `counterparty_risk_level` 为 `HIGH` | +40 分，`IN_REVIEW` |
| `travel-rule-counterparty-high-risk-jurisdiction` | `counterparty_jurisdiction` 位于默认高风险名单（`IRN`、`PRK`、`SYR`、`AFG`、`RUS`、`MMR`）中 | +60 分，`IN_REVIEW` |
| `travel-rule-counterparty-unresponsive` | `counterparty_responsiveness` 为 `UNRESPONSIVE` | +30 分，`IN_REVIEW` |

这五项预设仅适用于 Travel Rule 交易。

## Webhook

将一个 Webhook 目标订阅到 `travel_rule.status.updated` 后，您的应用中每笔转账的每次状态变更都会通知到该目标，包括出站和入站转账，也包括 `INTERNAL` 通道的各段。
路由在交易创建接口返回之后才运行，因此 `COUNTERPARTY_VASP_NOT_FOUND`、`COUNTERPARTY_VASP_NOT_REACHABLE` 等结果，以及立即完成结算的 `INTERNAL` 交换，都通过此事件送达，而不会出现在创建响应中。

```json theme={null}
{
  "event_id": "9c0c8b8a-1111-4222-9333-666666666666",
  "webhook_type": "travel_rule.status.updated",
  "timestamp": 1774970000,
  "created_at": 1774970000,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "transaction_id": "22222222-3333-4444-5555-666666666666",
  "txn_id": "trv-2026-07-05-001",
  "travel_rule_status": "COMPLETED",
  "rail": "INTERNAL",
  "direction": "OUTBOUND"
}
```

在选择通道之前，`rail` 为空。
对于同一笔交易，Didit 会先发送 `transaction.created`，然后才发送 `travel_rule.status.updated`。当 Didit 创建交易时（您调用 `POST /v3/transactions/`、sunrise 注册、收到入站 `TRP` 询问，或创建 `INTERNAL` 通道的入站段），它会先发送 `transaction.created`。
每个事件都独立投递和重试，因此一个需要重试的 `transaction.created` 可能晚于状态事件到达。请以 `transaction_id` 作为处理程序的关键字段；当状态事件指向您尚未见过的交易时，请读取该交易。
通过 `TRUST` 发送的转账不会收到 `COMPLETED` 事件：TRUST 不会返回 Didit 可读取的任何内容，因此除非发送失败或您取消，其最后一个事件为 `EXPIRED`。通过 `GTR`、`VERIFYVASP` 和 `SYGNA` 发送时，事件会跟随 Didit 从网络读取到的回复。
沙盒应用同样会发送此事件，并带有 `"environment": "sandbox"`。被 [Didit Test VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md) 应答的转账会带有 `rail: "TEST_VASP"`、`test_vasp: true` 和 `test_vasp_scenario`。

完整的事件目录、签名验证和重试策略，请参阅 [Webhook 指南](https://docs.didit.me/zh/integration/webhooks.md#事件类型)。

## 定价

Travel Rule 费用为**每次启动的出站信息交换 \$0.02**，在交易创建时收取一次，之后该信息交换经历多少次状态变更都不再重复收取。`resend` 不会再次收费。

* 适用于启用了 Travel Rule 的应用中的出站 `travelRule` 交易，且金额不低于您的 `threshold_amount`，因此 Didit 会为其发起托管信息交换。转账到自托管钱包，或转账到您用户已验证的自有钱包，同样计费，其所有权证明即构成该信息交换。
* 其他带有 Travel Rule 数据的出站交易，按标准交易筛查计费。您的套餐包含一定的[每月交易额度](https://docs.didit.me/zh/getting-started/pricing.md#交易监控-kyt)，超出部分按 \$0.001 计费。这包括：低于阈值的转账、Travel Rule 不适用的闪电网络支付，以及在 Travel Rule 关闭时被动记录的转账。每笔交易只按其中一项计费，绝不会两项同时计费。
* 入站 `travelRule` 交易不收取 Travel Rule 或监控费用。这包括 Didit 通过 `INTERNAL` 通道在收款人应用上创建的入站段、入站 `TRP` 询问，以及[充值后注册](#存款后的-sunrise-登记)。
* 免费加入 Travel Rule 的应用，在通过 `POST /v3/travel-rule/workspace/billing/` 开启出站计费之前，其出站 `travelRule` 交易两项费用均不收取；接收始终免费。
* 沙盒应用从不收费。
* Travel Rule 费用标价为 $0.02，超出额度的交易筛查标价为 $0.001。每笔交易按其所计入的项目价格收费。
* 如果同一笔交易同时运行加密货币 AML/钱包筛查（例如启用了监控的加密货币入站充值，其 `currency_kind: "crypto"`），该筛查将按标准钱包验证与筛查费率单独计费，不包含在 Travel Rule 费用中。
* [交易对手筛查](#交易对手筛查)为可选功能，单独计费，按每次筛查的企业 AML 筛查价格计算；距今不满 30 天的筛查结果可免费复用。决策、问卷以及目录的尽职调查评估均不收费。

### 归属分析定价

[区块链分析归属](#区块链分析归属)需要向分析服务商查询该钱包一次，您始终可获得该次查询的完整结果：

* **加密货币监控已对本笔交易的钱包进行过筛查**：复用已存储的结果。一次分析调用，一笔钱包验证与筛查费用（即加密货币监控已产生的那一笔）。归属分析不再增加任何费用。
* **加密货币监控未运行**：Didit 只进行一次所需的分析调用，并完整返回其结果。该交易按标准钱包验证与筛查费率收费（使用 Didit 托管分析服务时为 \$0.15），并获得完整的加密货币筛查输出，包括风险评分、钱包风险类别和网络快照，而不仅仅是 VASP 名称。用量明细项会标注 `travel_rule_attribution` 触发器，以便说明该笔费用的来源。
* **自带分析密钥**：这里适用与使用自带密钥的加密货币监控相同的平台费用（每次调用 \$0.02）。底层查询费用由您的服务商直接向您收取。
* **失败的查询从不收费**。重新发送时会复用已记录的归属结果，不会再次调用或收费。沙盒应用从不收费。

因此，需要归属分析的出站转账费用为 \$0.02（Travel Rule）加一笔钱包验证与筛查费用。两项产品同时启用时，分析调用及其费用只发生一次。

## EU TFR 规则包

`travel_rule_eu_tfr` 规则包内置四条预设规则，专为欧盟 TFR 义务而设计。
与其他所有[预设规则包](https://docs.didit.me/zh/transaction-monitoring/rules-library.md)一样，请在 Business Console 中前往 **Transactions → Rules → Library** 启用该规则包。

| 规则（`library_key`） | 触发条件 | 操作 |
| - | - | - |
| `tr_eu_mismatch_hold` | 转账状态为 `COUNTERPARTY_MISMATCHED_DATA` | 增加 `+50` 分，交易设为 `IN_REVIEW` |
| `tr_eu_unverified_selfhosted_review` | 转账状态为 `UNCONFIRMED_OWNERSHIP`，且默认货币金额至少为 `1000` | 交易设为 `IN_REVIEW` |
| `tr_eu_vasp_not_found_score` | 转账状态为 `COUNTERPARTY_VASP_NOT_FOUND` | 增加 `+25` 分，并标记为 `travel-rule-sunrise` |
| `tr_eu_expired_review` | 转账状态为 `EXPIRED` | 交易设为 `IN_REVIEW` |

这些规则会在每个相关的状态转换时触发，包括转账创建时、所有权确认时、收到邮件领取响应时，以及到期扫描程序运行时。因此，一笔始终无法完成的转账仍会进入合规审核，而不会悄无声息地完成结算。

## 为什么在单一平台上效果更好

Travel Rule 合规本质上是身份问题，只是披上了支付的外衣，而 Didit 已经掌握了身份信息：

* **手边即有已验证的数据。** 每笔转账的主体都通过 `vendor_data` 与 Didit 在入驻时验证过的 KYC 档案相关联（证件 OCR、活体检测、人脸比对），因此您可以在同一平台上基于已验证字段构建 `originator_data`。
  对于拥有已通过 KYC 验证的用户，Didit 传输的是已验证的身份信息，而不是您输入的值（参见[已验证数据与来源](#已验证数据与来源)）。
* **针对高风险转账的加强型生物识别。** [规则](https://docs.didit.me/zh/transaction-monitoring/rules.md)可以暂停任何高额转账，直到用户通过将用户当前的生物特征与入驻时采集的人脸进行比对的生物识别复核。这是在提款时应对账户接管的最有力手段。参见[实际示例](https://docs.didit.me/zh/transaction-monitoring/sdk-transaction-submission.md#示例：为高额提现进行生物识别升级验证)。
* **统一的设备图谱。** 通过 SDK 提交的转账与 KYC 会话携带相同的设备信息，因此"新设备 + 高金额 + 首次收款方"只需一条规则即可实现，无需数据仓库关联查询。
* **筛查、规则、案件与交易所集中于一处。** 用于识别交易对手 VASP 的钱包筛查，与您的规则和分析人员所用的筛查结果相同，即一次收费、一个控制台、一条审计轨迹。
* **公开定价。** [每次出站兑换 \$0.02](#定价)，无用量分级，无需联系销售。

## FAQ

### 交换如何运作

> [accordion] Didit 自己执行 VASP 之间的交换，还是只在另一网络完成握手后记录 Travel Rule 数据？
>
> Didit 自己执行交换。
> 当您提交 `travelRule` 转账时，Didit 会实时发现交易对手，通过承载这些数据的通道将汇款人和收款人数据发送给接收方 VASP，并且只有在交易对手一方确认后，才会将转账标记为 `COMPLETED`。这是托管式握手，而不是结算后的日志。
> 除 `TRUST` 外，所有通道都会处理交易对手的应答，TRUST 只负责发送，因此通过它的转账会过期，而不会完成。通过 `GTR`、`VERIFYVASP` 和 `SYGNA` 时，Didit 会从网络的响应中读取应答，或每 5 分钟轮询一次。目前，这些网络上的交换尚未在实际网络中完成过。通过 `TRP` 时，如果交易对手没有立即给出应答，则需要 Didit 的公共 TRP 主机，而该主机尚未上线（参见 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)）。
> Didit 还会保存每笔交换的完整记录和合规状态（包括 13 个状态的 [生命周期](#生命周期)、[Webhook](#webhook) 以及审计追踪）。
> 如果您希望通过自己的流程运行 Travel Rule，Didit 也可以被动记录数据，将 Didit 作为您唯一的记录系统（参见下方关于被动记录的问题）。
> 两种模式均可使用：在 [设置](#设置) 中设置 `is_enabled: true` 后，主动交换即会运行（默认为 `false`）。在此之前，Didit 会被动记录数据。

> [accordion] 交换是 Didit 原生的，还是依赖外部互操作性服务商（Notabene、Sygna、VerifyVASP、TRP）？
>
> 它是原生的。Didit 运行自己的 Travel Rule 协议（TRP）实现，该实现将在公共主机上线后部署于 `trp.didit.me`（目前尚未响应，因此交易对手今天无法通过 TRP 与您连接）。Didit 还直接实现了行业网络协议，包括 `GTR`（全球 Travel Rule）、`TRUST`、`VerifyVASP` 和 `Sygna Bridge`（原生支持，包括 Sygna 的 ECIES 加密 IVMS 101 和签名的许可请求）。Didit 并非某个第三方互操作性层的转售商。要连接位于特定会员网络上的交易对手，必然需要通过该网络进行，因为 Travel Rule 中并不存在单一的通用通道。但发现、握手、验证和记录保存均由 Didit 执行。
> 目前，每个网络通道都需要您自己的会员资格，而 `TRUST` 只负责发送。请参见 [概览](#概述)。

> [accordion] Didit 支持哪些 Travel Rule 网络与协议？
>
> 原生实现的网络与协议，按严格的优先级依次尝试：`INTERNAL`（两个 VASP 都在 Didit 上）、`TRP`（OpenVASP Travel Rule Protocol）、`GTR`、`TRUST`、`VerifyVASP`、`Sygna Bridge`，以及面向没有相关工具的交易对手的 `EMAIL` 备用方式。
> 目前，网络通道需要您自己的会员资格，`TRUST` 只负责发送，并且尚未开放通过网络接收。请参见 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)。
> CODE、TRISA、Notabene 和 Veriscope 以申请访问卡片的形式出现在 Didit 的 Marketplace 中，尚未集成。
> 完整的路由表请参见 [Didit 如何路由交换](https://docs.didit.me/zh/transaction-monitoring/travel-rule-interoperability.md#didit-如何路由交易)，每个网络目前可触达的范围请参见 [Travel Rule 网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)。

> [accordion] 我应该使用哪个网络？我需要自己的会员资格吗？
>
> 我们推荐以 `TRP` 作为基础：它不需要共享会员资格。共享您的 [Travel Address](#travel-address) 后，任何支持 TRP 的 VASP 都可以联系您。当交易对手只能通过地址识别时，只有在已归属的目录条目已经具备 TRP endpoint 时，[区块链分析归属](#区块链分析归属) 才会将其路由到 TRP。
> 在出站方向，Didit 会通过 TRP 发送 [TRP 资产标识符](#trp-资产标识符) 中列出的资产。如果交易对手没有立即应答，则需要 Didit 的公共 TRP 主机，而该主机尚未上线。
> 对于会员网络，请根据交易对手的运营地区进行选择：GTR 的全球覆盖范围较广，TRUST 主要面向美国 VASP，VerifyVASP（现也运营 Sygna Bridge）在亚洲的覆盖范围较强。
> 是的，目前您需要自己的会员资格。Didit 尚未持有这些网络的平台会员资格，目前正在作为服务商接入。
> 在 **Marketplace** 中连接您的会员资格。您不会被限制在单一网络上。
> 请注意，`TRUST` 只负责发送，因此通过它的转账不会完成；并且目前尚无任何网络上的交换在实际网络中完成过。

> [accordion] 只有钱包地址时，Didit 如何找到交易对手 VASP？
>
> 通过一条解析链：先是另一位证明了对该钱包拥有控制权的 Didit 客户（`INTERNAL` 通道），然后是您自己的 [地址簿](#地址簿)（用于查找已存储的 Travel Address），再是协议和网络通道，最后是 [区块链分析归属](#区块链分析归属)。
> 如果转账只携带目标钱包，Didit 会将其与区块链分析数据进行比对，以识别交易所或托管服务的归属，并将其记录在 [VASP 目录](#目录) 中。只有当该目录条目已经具备路由（例如 `TRP` 端点）时，转账才会自动路由；否则它会以 `COUNTERPARTY_VASP_NOT_FOUND` 结束，并记录交易对手的名称。当路由存在后，您可以重新提交，例如在您为该钱包的地址簿条目存储交易对手的 `travel_address` 之后（适用于 [TRP 资产标识符](#trp-资产标识符) 中列出的资产）。

### 定价与牌照

> [accordion] Travel Rule 的费用是多少？是否包含在我的 KYC 或验证套餐中？
>
> Travel Rule 是一款独立的按用量计费产品。
> 它不包含在身份验证中。
> \*\*每笔开始的出站交换收费 $0.02**（[公开定价](#定价)，无阶梯价格，无需联系销售）。
>         当路由需要进行区块链分析查询以识别交易对手 VASP 时，该查询按一次计费，费率为 [归属费率](#归属分析定价)：托管模式起价 $0.15，若您已有现成的服务商密钥则为 \$0.02。
> 如果加密货币监控已经筛查过同一钱包，则不会再次计费。
> 低于您阈值的转账，或在 Travel Rule 关闭时记录的转账，将改按交易筛查计费。入站转账（例如 Didit 在收款人一侧创建的入站转账）不收取 Travel Rule 费用。

> [accordion] Travel Rule 和加密货币监控同时运行时，我会被重复收费吗？
>
> 不会。
> `travelRule` 交易只计一笔费用：当它开始出站交换时收取 Travel Rule 费用（\$0.02），否则按标准交易筛查收费。区块链分析查询只计一次：如果加密货币监控已经对该交易的目标钱包进行过筛查，[归属](#区块链分析归属) 会复用已存储的结果，不产生额外费用。
> Didit 在收款人一侧创建的入站转账分支永远不会收取 Travel Rule 费用。

> [accordion] Didit 能否为我的 VASP 牌照申请或监管备案提供文档或能力说明？
>
> 可以。Didit 的 KYT 与交易监控、AML 和制裁筛查、区块链分析钱包风险以及 Travel Rule 功能均已在 docs.didit.me 上公开记录，您可以在监管备案中引用这些页面。如果您需要针对特定申请量身定制的正式产品能力说明（例如 AIFC/AFSA、MiCA 或其他 VASP 牌照申请），请联系您的 Didit 客户团队，并说明监管机构要求的格式。

### 运营与数据

> [accordion] 低于我设置的阈值的转账会怎样处理？
>
> 这些转账会立即返回 `NOT_APPLICABLE`：不会尝试任何通道，也不会有任何身份数据离开 Didit。
> 该交易按标准交易筛查计费，而不是按 Travel Rule 费用计费。
> Didit 会将阈值与金额换算为您应用默认货币后的数值进行比较。
> 如果 Didit 无法换算金额，则不应用阈值，该转账将进行信息交换。
> 欧盟 TFR 没有最低限额，因此欧盟 CASP 应将 `threshold_amount` 保持为 `0.00`。

> [accordion] 如果交易对手始终不响应怎么办？
>
> 转账会停留在 `AWAITING_COUNTERPARTY`，直到 `confirmation_timeout_hours` 到期，随后清理程序会将其转为 `EXPIRED`，并将您的 `timeout_outcome` 应用于底层交易：`HOLD` 表示转人工审核，`REJECT` 表示拒绝，`PROCEED` 表示不做任何改动。预设规则 `tr_eu_expired_review` 还会将每次到期情况提示给您的分析人员。若交易对手完全不接收消息，情况则不同：Didit 会重试，在第六次失败后，转账会转为 `COUNTERPARTY_VASP_NOT_REACHABLE`（请参阅[投递与重试](#投递与重试)）。

> [accordion] 我的交易对手完全没有 Travel Rule 工具，我还能合规吗？
>
> 可以，这就是 `EMAIL` 通道。对方的合规联系人会收到一次性取件链接，可在托管页面上确认或拒绝此次信息交换，无需 Didit 账户、无需 API 密钥，也无需任何集成。对于在任何信息交换成为可能之前就已结算的存款，请使用[存款后 sunrise 注册](#存款后的-sunrise-登记)。

> [accordion] 向用户自有钱包提现如何运作？
>
> 将目标声明为自托管钱包（`payment_method.method_type: "unhosted_wallet"`）。Didit 会跳过 VASP 路由，转而通过[钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)收集您的客户控制该钱包的证明，证明方式包括消息签名、Satoshi 测试、截图或自我声明。这就是欧盟 TFR 自托管钱包义务的 API 实现形式。

> [accordion] 交易对手的个人信息在交换期间是否受保护？
>
> 是的。
> 在您一方确认钱包所有权之前，入站转账中交易对手的个人数据会被遮盖，转账对象和交易中的交易对手方同样如此（请参阅[交易对手数据遮盖](#脱敏的交易对手数据)）。
> 邮件取件页面仅显示转账摘要（发起方 VASP 名称、金额、目标钱包），绝不显示个人 payload。
> 通道交换通过 HTTPS 进行，此外，网络通道（`GTR`、`TRUST`、`VERIFYVASP`、`SYGNA`）还会使用接收方 VASP 的公钥对 IVMS payload 进行加密。

> [accordion] 我已用自有流程跟踪 Travel Rule 合规，能否直接记录？
>
> 可以。在禁用 Travel Rule 的情况下，您提交的任何 `travel_rule_details`（包括调用方提供的 `status`，无论是当前版本还是旧版）都会被动记录在交易中：不会调度任何通道，也不会进行任何协商。您可以借此补录数据，或在保留单一记录系统的同时运行自己的流程。

> [accordion] 上线前如何测试？
>
> 请使用已开启 Travel Rule 的沙盒应用。沙盒会跳过规则、计费，以及 `transaction.created` 和 `transaction.status.updated` Webhook。所有**出站**信息交换都会通过 `TEST_VASP` 通道发送至[Didit 测试 VASP](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md)，它会在 Didit 内部按您选择的场景作出响应：接受、拒绝、姓名不符、无法连接、超时或自托管。您将收到标记为 `environment: sandbox` 的 `travel_rule.status.updated` Webhook，且不会有任何消息送达真实的交易对手。
> 对于**入站**转账，可让测试 VASP 通过[触发测试 VASP 入站转账](https://docs.didit.me/zh/management-api/travel-rule/trigger-test-vasp-inbound.md)向您发送转账，或使用 `POST /v3/travel-rule/inbound/` 登记一笔转账；两种方式均会根据您的钱包地址簿进行判定，结果为 `ON_HOLD`、`UNCONFIRMED_OWNERSHIP`、`COUNTERPARTY_MISMATCHED_DATA`，或先 `COMPLETED` 再 `FINISHED`。
> 若要与另一个真实应用进行双方信息交换，请在 `INTERNAL` 通道上使用您自己的两个生产环境应用：接收方应用必须可被发现，并通过签名消息或测试存款证明其对目标钱包的控制权。

## 后续步骤

> [card] [钱包所有权组件](https://docs.didit.me/zh/transaction-monitoring/wallet-ownership-widget.md)
>
> 通过签名、Satoshi 测试、截图或自我声明，证明客户控制某个钱包。

> [card] [互操作性](https://docs.didit.me/zh/transaction-monitoring/travel-rule-interoperability.md)
>
> 面向交易对手、供其他 VASP 联系您的端点：TRP、回调、邮件取件和 sunrise。

> [card] [网络](https://docs.didit.me/zh/transaction-monitoring/travel-rule-networks.md)
>
> 每个通道和网络目前可连接的范围，以及哪些仍需等待成员资格。

> [card] [在沙盒中测试](https://docs.didit.me/zh/transaction-monitoring/travel-rule-sandbox.md)
>
> 上线前，针对 Didit 测试 VASP 运行每种结果。

> [card] [提交交易](https://docs.didit.me/zh/transaction-monitoring/transactions.md)
>
> 如何在 Create Transaction payload 中包含 Travel Rule 数据。

> [card] [规则库](https://docs.didit.me/zh/transaction-monitoring/rules-library.md)
>
> 完整的预设规则目录，包括 Travel Rule 规则包。

> [card] [Webhook](https://docs.didit.me/zh/integration/webhooks.md)
>
> 订阅 travel\_rule.status.updated 及所有其他事件类别。

> [card] [控制台指南](https://docs.didit.me/zh/transaction-monitoring/console.md)
>
> 在交易详情视图中查看 Travel Rule 状态。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.