> ## 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.

# Travel Rule API по FATF для VASP: комплаенс

> Travel Rule API от Didit для VASP и PSP по FATF: обмен данными отправителя и получателя, отслеживание протокола, авторемедиация. $0.02 за обмен.

Didit проводит обмен данными отправителя и получателя в соответствии с требованиями FATF и Регламентом ЕС о переводе средств (Transfer of Funds Regulation, TFR) для переводов виртуальных активов. Платформа находит канал, по которому можно связаться с VASP контрагента, согласовывает по нему необходимые данные, отслеживает обмен через жизненный цикл из 13 состояний и сообщает, когда обмен завершён, чтобы вы могли разблокировать средства ончейн.

## Обзор

Правило Travel Rule FATF (Рекомендация 16), реализованное в ЕС в виде Регламента о переводах средств (TFR), требует, чтобы отправляющий поставщик услуг виртуальных активов (**VASP-отправитель**) собрал и передал информацию об отправителе и получателе VASP-получателю (**VASP-получатель**) до того, как перевод виртуальных активов будет завершён.

В отличие от большинства юрисдикций, TFR ЕС действует **без минимальной суммы**: в сферу действия попадает каждый криптоперевод, а не только переводы выше порога отчётности.

Продукт Didit для Travel Rule представляет собой управляемый обмен, а не просто поле с данными:

* Вы отправляете перевод с `transaction_category: "travelRule"`.
* Didit перебирает каналы в порядке приоритета, чтобы связаться с поставщиком-контрагентом, проводит обмен через первый канал, по которому удаётся связаться с ним, и, если другая сторона возвращает имя получателя, сверяет его с тем, которое отправили вы.
* Только после того, как обмен перейдёт в статус `COMPLETED`, следует транслировать перевод ончейн.
* Затем вы передаёте в Didit хеш блокчейн-транзакции, чтобы закрыть обмен со статусом `FINISHED`.

В этом релизе доступны семь каналов, которые перебираются в строгом порядке приоритета: побеждает первый, который может связаться с контрагентом.

| Приоритет | Канал | Как работает |
| - | - | - |
| 1 | `INTERNAL` | Используется, когда другой клиент Didit подтвердил контроль над кошельком получателя подписанным сообщением или тестовым зачислением. Didit создаёт соответствующий входящий перевод в приложении этого клиента и согласует обмен полностью внутри платформы, без исходящего сетевого вызова. Мгновенно. |
| 2 | `TRP` | Нативная интеграция с открытым протоколом Travel Rule Protocol (TRP / OpenVASP). Используется, когда перевод содержит [Travel Address](#travel-addresses), когда в записи адресной книги кошелька получателя он сохранён или когда контрагент-поставщик находится в [каталоге](#каталог) с endpoint TRP. Поддерживает активы, перечисленные в разделе [идентификаторы активов TRP](#идентификаторы-активов-trp). Didit обменивается данными отправителя и получателя между системами, без обмена письмами. |
| 3 | `GTR` | Глобальная сеть Travel Rule (GTR). Контрагент определяется по явному `counterparty_gtr_vasp_code` или по записи каталога, которая его содержит. Didit получает ответ контрагента из ответа GTR или опрашивает статус запроса каждые 5 минут, пока перевод ожидает. |
| 4 | `TRUST` | Сеть TRUST. Контрагент определяется по `counterparty_trust_membership_id` или по записи каталога, которая его содержит. Только отправка: TRUST не возвращает Didit ничего, что тот может прочитать, поэтому перевод ожидает в статусе `AWAITING_COUNTERPARTY`, пока не истечёт срок. |
| 5 | `VERIFYVASP` | Сеть VerifyVASP. Контрагент определяется по `counterparty_verifyvasp_id` или по записи каталога, которая его содержит. Didit получает ответ контрагента, опрашивая статус верификации каждые 5 минут. |
| 6 | `SYGNA` | Сеть Sygna Bridge (в 2026 году приобретена VerifyVASP и продолжает работать для существующих участников). Нативная реализация протокола Sygna: IVMS101, зашифрованный по ECIES, с подписанными запросами на разрешение. Контрагент определяется по `counterparty_sygna_vasp_code` или по записи каталога, которая его содержит. Ответ контрагента о принятии или отклонении завершает или отклоняет перевод: Didit опрашивает статус перевода каждые 5 минут, а также принимает ответ через свой обратный вызов Sygna, когда станет доступен публичный хост TRP Didit. |
| 7 | `EMAIL` | Запасной вариант, который используется, когда ни один протокольный или сетевой канал не может связаться с контрагентом, а вы указали его контактное лицо по комплаенсу в `counterparty_vasp_email` или в записи каталога контрагента есть [контакт по комплаенсу](#контакты). Didit отправляет этому контакту письмо с одноразовой ссылкой для получения, чтобы он мог подтвердить или отклонить обмен без учётной записи Didit. Это основной инструмент для решения проблемы sunrise: контрагенты, которые пока недоступны ни в одной сети. |

Сетевым каналам (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`) нужно членство в сети со стороны отправителя, и сейчас это членство должно быть **вашим собственным**.
Подключите учётные данные членства в консоли **Marketplace** (категория Travel Rule networks), и ваши переводы будут проходить под вашей собственной сетевой идентичностью.

**Didit сейчас не имеет платформенного членства ни в одной из четырёх сетей и проходит процедуру подключения в качестве провайдера.**
Пока эта процедура не завершена, `platform_available` равно `false` для всех четырёх сетей, режим `didit` в настройках (маршрутизация через платформенное членство Didit) недоступен ни для одной сети, а в `disabled_networks` нечего отключать.
Членство, которое вы подключаете самостоятельно, всегда имеет приоритет над платформенным членством.
Ни одно сетевое членство не включено по умолчанию, в том числе GTR.
Включение Travel Rule не активирует GTR, TRUST, VerifyVASP или Sygna.
Чтобы узнать действующие режимы `networks` для вашего приложения, см. [Get Travel Rule Settings](https://docs.didit.me/ru/management-api/travel-rule/get-settings.md), а что каждая сеть охватывает сегодня, см. [Travel Rule networks](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md).
Если все четыре сообщают `unavailable`, ни одна из них не может провести обмен: подключите собственное членство в **Marketplace** или используйте доступного контрагента `INTERNAL`, `TRP` или `EMAIL`.

При сохранении членства его код участника должен быть вашим собственным: сохранение возвращает `400`, если код совпадает с кодом Didit в этой сети или с кодом, который уже закреплён за членством другого приложения.
Для членства в GTR нужен приватный ключ ed25519 (base64), тот, чей публичный ключ зарегистрирован в портале GTR: он шифрует персональные данные, которые вы отправляете, и расшифровывает данные, которые вы получаете.
Членство в VerifyVASP указывает на ваш собственный VerifyVASP Enclave: его URL принадлежит вашему Enclave, а token: это тот, который ваш Enclave предъявляет Didit.

Даже при собственном членстве канал сети маршрутизирует перевод только тогда, когда в его `beneficiary_data` указан идентификатор контрагента в этой сети (`counterparty_gtr_vasp_code`, `counterparty_trust_membership_id`, `counterparty_verifyvasp_id` или `counterparty_sygna_vasp_code`), либо когда в записи каталога контрагента этот идентификатор указан для данной сети (см. [Routes](#маршруты)).

`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/ru/transaction-monitoring/travel-rule-networks.md).

Остальные сети: CODE (корейский альянс), TRISA, Notabene и Veriscope перечислены в **Marketplace** в консоли в виде плиток для запроса доступа.
Интеграции с ними у Didit пока нет, поэтому сегодня через них нельзя направить ни один перевод.

Если ни один из семи каналов не может определить контрагента, Didit выполняет еще один шаг определения, прежде чем сдаться: атрибуцию через блокчейн-аналитику.
Адрес кошелька назначения сверяется с данными блокчейн-аналитики; если кошелек принадлежит известной бирже или кастодиану, Didit записывает атрибутированный VASP в перевод, добавляет его в [справочник](#каталог) и один раз повторно запускает каналы с новым названием.
Подробнее о поведении и ценах см. [Атрибуция через блокчейн-аналитику](#атрибуция-в-блокчейн-аналитике).

Перевод, адрес назначения которого [объявлен как некастодиальный кошелек](#объявленные-некастодиальные-кошельки), пропускает все семь каналов, так как VASP-контрагента для обмена нет, и сразу переходит к подтверждению владения кошельком конечным пользователем.

> [!NOTE]
>
> Didit также управляет стороной обмена, обращенной к контрагентам: публичными эндпоинтами TRP для идентификации и запросов, которые другие VASP вызывают, чтобы связаться с вами, эндпоинтами обратного вызова и процессом получения по электронной почте.
> Вы никогда не вызываете их сами, это управляемая инфраструктура. Однако на странице [Совместимость Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule-interoperability.md) описан каждый из них, чтобы VASP-контрагенты могли подключиться к вам.
> Вы взаимодействуете с Travel Rule только через эндпоинты `/v3/`, описанные на этой странице.

## Что выполняется при отправке перевода

Проверки выполняются в рамках одного вызова `POST /v3/transactions/`. К моменту получения ответа скрининг уже выполнен, перевод сохранён, а правила оценены.
Обмен начинается уже после ответа: Didit выбирает канал и связывается с контрагентом вне вашего запроса, поэтому ни одна сеть и ни один контрагент не могут замедлить создание или привести к его ошибке.
Каждое последующее изменение статуса приходит в виде webhook [`travel_rule.status.updated`](#webhooks), в котором указан канал. Объект перевода, который возвращают endpoint'ы 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`. Если два запроса с одинаковым идентификатором приходят одновременно, один из них получает `409`.
   `subject` связывается с вашим профилем пользователя или компании через `vendor_data`, чтобы перевод отображался в этом профиле для мониторинга.
   Если `vendor_data` субъекта или контрагента находится в списке блокировки, запрос отклоняется с кодом `400`, и транзакция не создаётся.
   Кошельки, банковские счета, IP-адреса и устройства из списка блокировки проверяются на шаге 6, где транзакция отклоняется уже после маршрутизации.
2. **Обогащение данными об устройстве и сети.** IP-адреса участников обогащаются геоданными. Контекст устройства (он передаётся автоматически при [отправке через SDK](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md)) нормализуется и попадает в граф устройств. Это тот же граф, который наполняют сессии KYC, поэтому связь «по устройству» работает и при онбординге, и для транзакций.
3. **Криптоскрининг.** Для криптопереводов с включённым мониторингом соответствующий кошелёк (или ончейн-хеш) проверяется в аналитике блокчейна до того, как начнётся какой-либо обмен: риск санкций, близость к миксерам, украденные средства. См. [Криптоскрининг и скрининг кошельков](https://docs.didit.me/ru/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/ru/transaction-monitoring/wallet-ownership-widget.md), которая возвращается в `action_required`.
6. **Оценка правил.** Выполняются все активные правила. [Пакет правил EU TFR](#пакет-правил-eu-tfr) реагирует на статус перевода, заданный на шаге 4. Ваши пользовательские правила видят результаты скрининга шага 3. Правило может удержать транзакцию (`IN_REVIEW`), отклонить её или потребовать [дополнительной верификации](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md#требуемые-действия-пользователя) (`AWAITING_USER`).
7. **Биллинг и уведомления.** За исходящую транзакцию взимается плата по одной строке: [линия Travel Rule](#цены), если она запустила обмен, и скрининг транзакций в остальных случаях. С входящего перевода плата не взимается, а за приложение в песочнице плата никогда не взимается.
   Ответ возвращается, после чего `transaction.created` отправляется на ваши адреса назначения webhook, а затем следуют события `travel_rule.status.updated` шага 4.
8. **Маршрутизация, после ответа.** Didit последовательно перебирает [семь каналов](#обзор) в порядке приоритета. Если ни один из них не разрешает маршрут, [атрибуция контрагента по аналитике блокчейна](#атрибуция-в-блокчейн-аналитике) определяет контрагента, и каналы проверяются повторно один раз. Первый доступный канал ведёт обмен: перевод остаётся в `AWAITING_COUNTERPARTY` на этом канале, пока контрагент отвечает. Если найден контрагент `INTERNAL`, перевод сразу завершается (`COMPLETED` или `COUNTERPARTY_MISMATCHED_DATA`). Если ни один канал не достигает контрагента, статус становится `COUNTERPARTY_VASP_NOT_FOUND`. Каждое изменение запускает `travel_rule.status.updated`, а правила вычисляются заново при каждом изменении статуса или канала. Для перевода, который маршрутизация оставила в `COUNTERPARTY_VASP_NOT_FOUND`, сессия виджета подтверждения владения кошельком создаётся в этот момент (если включён `auto_wallet_verification`). Получить её можно из `action_required` транзакции с помощью [Получить транзакцию](https://docs.didit.me/ru/management-api/transactions/get.md).

### Как каждый этап соответствует вашим обязательствам

| Этап конвейера | Обязательство, которое он выполняет |
| - | - |
| Данные отправителя, которые вы передаёте в `originator_data` | Точная информация об отправителе (FATF R.16; EU TFR, статья 14). На каналах, которые передают персональные данные (`INTERNAL`, `TRP` и сетевые каналы), Didit передаёт проверенную личность, если субъект является пользователем с одобренной верификацией KYC в Didit (см. [Проверенные данные и их происхождение](#проверенные-данные-и-происхождение)). В остальных случаях передаются именно те данные `originator_data`, которые вы отправили. |
| Обмен по каналам до расчёта | Информация об отправителе и получателе передаётся контрагенту VASP **до** расчёта по переводу (FATF R.16; EU TFR, статьи 14–16) |
| Сверка имени получателя | Проверка того, что данные получателя соответствуют адресату (обязательство CASP получателя по EU TFR, статья 16) |
| Подтверждение владения некастодиальным кошельком | Обязательство EU TFR по некастодиальным кошелькам: проверка владения некастодиальным адресом вместо обмена с VASP |
| Криптоскрининг и пакет правил EU TFR | Процедуры на основе оценки риска для переводов с отсутствующей или неполной информацией и контроль риска санкций |
| Шлюз `COMPLETED` перед отправкой в сеть | Средства не выпускаются до завершения обмена. В этом суть правила |
| Закрытие `FINISHED` с ончейн-хешем и хронологией | Ведение записей: каждый перевод хранит полную хронологию обмена для аудита |

> [!NOTE]
>
> **Примечание о тестировании.** Приложения в песочнице не выполняют оценку правил и биллинг и не отправляют webhook `transaction.created` или `transaction.status.updated`. Транзакции в песочнице остаются в статусе `APPROVED`, если только при истечении перевода Travel Rule не применяется ваш `timeout_outcome`. Оплата в песочнице не взимается.
> Движок обмена Travel Rule работает в приложениях песочницы, если Travel Rule включён, и взаимодействует только с одним контрагентом. Каждый **исходящий** обмен направляется к [Didit Test VASP](https://docs.didit.me/ru/transaction-monitoring/travel-rule-sandbox.md) по каналу `TEST_VASP`. Тестовый VASP отвечает внутри Didit по выбранному вами сценарию, поэтому перевод может завершиться, быть отклонён, завершиться несоответствием данных, стать недоступным, истечь или перейти в ожидание подтверждения владения кошельком.
> Сообщения, связанные с комплаенсом, никогда не доходят до реального VASP-контрагента, а webhook `travel_rule.status.updated` отправляется с `environment: sandbox`.
> Для **входящих** переводов: тестовый VASP может отправить вам перевод ([Инициировать входящий перевод от тестового VASP](https://docs.didit.me/ru/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/ru/transaction-monitoring/travel-rule-sandbox.md).

## Lifecycle

Каждый перевод имеет `status` из 13 значений.
Обмен начинается со статуса `AWAITING_COUNTERPARTY` в обычном случае, со статуса `NOT_APPLICABLE`, если сумма ниже вашего `threshold_amount` или в запросе на платёж Lightning не указан получатель (см. [Lightning](#lightning)), либо со статуса `UNCONFIRMED_OWNERSHIP`, если адрес назначения [объявлен некастодиальным кошельком](#объявленные-некастодиальные-кошельки). Завершается он одним из конечных статусов: `FINISHED`, `CANCELLED` или `EXPIRED`.

У перевода собственный конечный автомат, **отдельный от** статуса проверки базовой транзакции.
Статус обмена сам по себе никогда не меняет статус транзакции, за одним исключением: при истечении срока применяется ваш настроенный `timeout_outcome` (или [политика sunrise или для неотвечающих контрагентов](#политики-переходного-периода-и-контрагентов-не-отвечающих-на-запросы), если она действует).
Вместо этого ваши правила пересчитываются при каждом переходе, поэтому [правила EU TFR](#пакет-правил-eu-tfr) или любое правило, которое опирается на `travel_rule_status`, может изменить транзакцию, например перевести её в `IN_REVIEW`.
Если переводу нужно подтверждение владения кошельком от конечного пользователя, в транзакцию добавляется блок `action_required`, но её `status` остаётся таким, каким его оставили ваши правила.
Состояние обмена читайте из `travel_rule.status`, а решение «разрешить / удержать / отклонить» из собственного `status` транзакции.

Каждый переход (и каждый статус, полученный пассивно) также дублируется в пользовательские значения транзакции, чтобы на них могли опираться [правила](https://docs.didit.me/ru/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/ru/transaction-monitoring/wallet-ownership-widget.md) или [`POST .../travel-rule/ownership/`](#подтверждение-владения-кошельком). |
| `ON_HOLD` | Didit присваивает этот статус только входящим переводам: кошелёк назначения при [регистрации после депозита](#регистрация-депозита-задним-числом) или кошелёк назначения во входящем запросе `TRP` отсутствует в вашей [адресной книге](#адресная-книга-кошельков), либо входящий запрос `TRP` ждёт вашего решения (автоматический приём выключен или автоматический ответ не удалось доставить). Устраните его с помощью [`POST .../travel-rule/ownership/`](#подтверждение-владения-кошельком). Сам по себе он не меняет статус транзакции; опциональное [правило из библиотеки](https://docs.didit.me/ru/transaction-monitoring/rules-library.md) `travel-rule-pending-action` переводит такие транзакции в `AWAITING_USER`. |
| `COMPLETED` | Обмен успешно завершён: контрагент (или, для входящего перевода, вы) одобрил его либо прошли проверки, которые Didit выполняет на этом пути (совпадение имени получателя, подтверждение владения кошельком). Можно безопасно отправить перевод ончейн. |
| `FINISHED` | Обмен завершается с указанием хеша ончейн-транзакции. Для исходящего перевода хеш передаётся через [`PATCH .../travel-rule/`](#отправка-перевода). Для входящего перевода Didit присваивает этот статус, когда депозит уже был проведён ([регистрация после депозита](#регистрация-депозита-задним-числом)), когда отправитель подтверждает перевод `TRP`, или когда исходящая сторона `INTERNAL` завершается. |
| `CANCELLED` | Отменён вручную через `action: "cancel"` из любого неконечного статуса либо отменён отправителем входящего перевода `TRP`. |
| `EXPIRED` | Истекло `confirmation_timeout_hours` при статусе `AWAITING_COUNTERPARTY`. Фоновый процесс (каждые 15 минут) помечает перевод как истёкший и применяет ваш настроенный `timeout_outcome` к базовой транзакции: `REJECT` отклоняет её, `HOLD` переводит на проверку, `PROCEED` оставляет без изменений. Если действует [политика sunrise или для неотвечающих контрагентов](#политики-переходного-периода-и-контрагентов-не-отвечающих-на-запросы), она заменяет `timeout_outcome`. |
| `COUNTERPARTY_MISMATCHED_DATA` | Имя получателя не соответствует настроенному `name_matching_strictness` относительно другой стороны: имени, которое вернул контрагент, или `holder_name` совпадающей записи в адресной книге. |
| `COUNTERPARTY_VASP_NOT_FOUND` | Ни один из семи каналов, как и резервный механизм [атрибуции блокчейн-аналитики](#атрибуция-в-блокчейн-аналитике), не смог определить доступного VASP-контрагента для этого перевода. Любая атрибутированная организация всё равно записывается в перевод и в справочник. Для исходящих переводов Didit автоматически создаёт сессию [виджета подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) (если включён `auto_wallet_verification`), чтобы перевод всё же мог завершиться как перевод на некастодиальный кошелёк. Поля `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"`, затем вызовите `PATCH .../travel-rule/` с полученным `payment_txn_id`, чтобы перевести перевод в `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/ru/management-api/travel-rule/update-transfer.md), [Подтверждение владения](https://docs.didit.me/ru/management-api/travel-rule/confirm-ownership.md) и [регистрация 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` до этого релиза, эти значения продолжают работать и автоматически сопоставляются.
Написание статусов в camelCase (например, `counterpartyVaspNotFound`) также принимается, как при отправке, так и в значениях условий [правил](https://docs.didit.me/ru/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` | boolean | Включает управляемый процесс согласования для исходящих транзакций `travelRule` из этого приложения. Требует непустого `legal_name` (уже сохранённого или переданного в том же запросе), иначе запрос возвращает `400`. |
| `legal_name` | string | Юридическое название вашего VASP. Показывается контрагенту на странице получения по email и возвращается в [справочнике VASP](#каталог). |
| `lei` | string | Ваш Legal Entity Identifier (LEI), возвращаемый в справочнике VASP. |
| `jurisdiction` | string | Код юрисдикции, в которой работает ваш VASP, до 8 символов (по умолчанию `"EU"`). |
| `name_matching_strictness` | string | `NONE`, `STRICT`, `CONTAINS`, `DEFAULT` или `FUZZY`. См. ниже. |
| `confirmation_timeout_hours` | integer | Количество часов, в течение которых перевод в статусе `AWAITING_COUNTERPARTY` может ожидать, прежде чем автоматически истечёт. Минимум `1`, по умолчанию `48`. |
| `timeout_outcome` | string | Что происходит с базовой транзакцией, когда перевод истекает: `HOLD` переводит её на проверку, `REJECT` отклоняет её, `PROCEED` оставляет её статус без изменений. По умолчанию `HOLD`. `sunrise_policy` и `non_responsive_policy` имеют приоритет, когда применимо одно из них. |
| `sunrise_policy` | string | `HOLD`, `REJECT`, `PROCEED` или `""` (по умолчанию), чтобы следовать `timeout_outcome`: что делает истёкший перевод, когда его контрагент является записью справочника в юрисдикции из списка `sunrise_jurisdictions`. См. [Политики переходного периода и неотвечающих контрагентов](#политики-переходного-периода-и-контрагентов-не-отвечающих-на-запросы). |
| `non_responsive_policy` | string | `HOLD`, `REJECT`, `PROCEED` или `""` (по умолчанию), чтобы следовать `timeout_outcome`: что делает истёкший перевод, когда его контрагент является записью справочника, для которой [статус отклика](#отзывчивость) имеет значение `UNRESPONSIVE`. `sunrise_policy` применяется первым, если подходят оба. |
| `sunrise_jurisdictions` | string\[] | Только для чтения. Страны, которые Didit считает находящимися в переходном периоде Travel Rule (sunrise). Сейчас это `["CO", "KH", "VN"]`. Для любой другой юрисдикции действует `timeout_outcome`. |
| `compliance_email` | string | Контактный адрес электронной почты для комплаенса для профиля вашей VASP. [Сеть Didit](#сеть-didit) включает ваш профиль в список только после подтверждения этого адреса по ссылке из письма. При смене адреса требуется новое подтверждение. |
| `is_discoverable` | boolean | Может ли профиль вашей VASP попасть в [сеть Didit](#сеть-didit): быть найденным в `GET /v3/travel-rule/vasps/` другими клиентами Didit и быть доступным по каналу INTERNAL. Для включения в список действуют дополнительные условия. `didit_network` в настройках показывает, каких не хватает. По умолчанию `true`. |
| `inbound_auto_accept` | boolean | Если `true`, входящий запрос `TRP`, чей кошелёк получателя является подтверждённой записью в вашей адресной книге и имя совпадает, одобряется автоматически. Если `false`, он ждёт вашего решения в статусе `ON_HOLD`. Другие входящие пути этот параметр не учитывают. По умолчанию `true`. |
| `allow_self_declaration` | boolean | Если `true`, [виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) предлагает самодекларацию как способ подтверждения, а вы можете создавать записи в [адресной книге кошельков](#адресная-книга-кошельков) с `self_declared: true`. По умолчанию `true`. |
| `allow_screenshot_proof` | boolean | Если `true`, [виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) предлагает загрузку скриншота как доказательство владения. Скриншот сам по себе никогда не подтверждает кошелёк: его проверяет ваша команда комплаенса и подтверждает перевод. Это слабее криптографической подписи, поэтому отключите этот параметр там, где ваша политика требует доказательства уровня подписи. По умолчанию `true`. |
| `auto_wallet_verification` | boolean | Если `true`, исходящий перевод, которому нужно доказательство контроля над кошельком со стороны конечного пользователя, автоматически создаёт сессию [виджета подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md), привязанную к транзакции, и выводит её в блоке `action_required` транзакции. SDK Didit запускают её автоматически при [отправке на стороне клиента](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md). По умолчанию `true`. |
| `counterparty_screening_enabled` | boolean | Включаемая по выбору проверка контрагентов-VASP по санкционным спискам и негативным публикациям в СМИ: [скрининг контрагентов VASP](#скрининг-контрагента), оплачиваемый за каждую проверку по цене AML-скрининга для компаний. Если `true`, Didit проверяет контрагента, которого достигает исходящий обмен, в момент начала обмена, повторно используя проверку, проведённую менее 30 дней назад. По умолчанию `false`: ничего не проверяется и не тарифицируется. |
| `vasp_attribution_enabled` | boolean | Если `true`, исходящий перевод, который не может быть направлен ни по одному каналу, атрибутируется через [блокчейн-аналитику](#атрибуция-в-блокчейн-аналитике): кошелёк назначения сопоставляется с VASP, которой он принадлежит, организация добавляется в справочник, а маршруты перезапускаются один раз с атрибутированным названием. По умолчанию `true`. |
| `threshold_amount` | string (decimal) | Минимальная сумма перевода, которая запускает управляемый обмен, в валюте по умолчанию вашего приложения. Didit сравнивает её с суммой перевода, пересчитанной в эту валюту (или с `amount_in_default_currency`, если вы его передаёте), а не с суммой в валюте самого перевода. Переводы ниже порога сразу получают статус `NOT_APPLICABLE` и никогда не согласовываются. Если Didit не может пересчитать сумму, порог не применяется и перевод проходит управляемый обмен. В приложениях в песочнице суммы не пересчитываются, поэтому там порог применяется только тогда, когда вы передаёте `amount_in_default_currency`. По умолчанию `0.00`, в соответствии с подходом EU TFR, где нет минимальной суммы, поэтому каждый перевод проверяется независимо от размера. |
| `ownership_validation_mode` | string | `FULL`, `SIMPLIFIED` или `AUTOMATED`: какой объём доказательств требуется для подтверждения владения кошельком. См. [Режимы проверки владения](#режимы-проверки-владения). По умолчанию `AUTOMATED`. |
| `simplified_validation_threshold` | string (decimal) | В режиме `SIMPLIFIED` для перевода на сумму ниже этого значения принимается самодекларация. По умолчанию `0.00`. |
| `data_sharing_fields` | object | Поля, которые вы передаёте о своих пользователях, по типам сущностей. См. [Передача данных](#передача-данных). |
| `matching_rules` | object | Насколько строго сравнивается каждое поле данных контрагента по типам сущностей. См. [Сопоставление по полям](#сопоставление-по-полям). |
| `disabled_networks` | string\[] | Сетевые каналы (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`), через которые ваши переводы не должны маршрутизироваться через членство Didit в платформе. Сеть, для которой вы подключили собственное членство (в консоли: Marketplace), этим списком не затрагивается. У Didit пока нет членства в платформе, поэтому список сегодня не влияет на маршрутизацию (канал из списка показывает `mode: off`). По умолчанию `[]`. |
| `networks` | object | Только для чтения. Статус каждой сети по ключам `gtr` / `trust` / `verifyvasp` / `sygna`: `{rail, mode, platform_available, connected}`, где `mode` принимает значения `own` (ваше подключённое членство), `didit` (членство платформы Didit, пока недоступно ни в одной сети), `off` (отключено вами) или `unavailable`. |
| `travel_address` | string | Только для чтения. Travel Address вашего VASP: закодированный входящий endpoint `TRP`-запросов, через который другие VASP обращаются к вам. Заполняется после включения 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`. Didit никогда не добавляет кошелёк, который адресная книга уже содержит под другим именем той же сети (`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 Recommendation 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** – Transfer of Funds Regulation (EU) 2023/1113 | **Без минимума** | Каждый перевод криптоактивов между CASP входит в сферу действия. Владение некастодиальным кошельком должно быть подтверждено для переводов сверх EUR 1,000. |
| **FATF Recommendation 16** (глобальный базовый уровень) | USD/EUR 1,000 | Ниже этого порога FATF допускает сокращённый набор данных (имена и адреса кошельков без верификации). |
| **США** – BSA Travel Rule | USD 3,000 | Давний порог FinCEN для переводов средств, применяемый к конвертируемой виртуальной валюте. |
| **Великобритания** | Эквивалент EUR 1,000 | Полная информация выше порога; сокращённый набор данных ниже (MLRs Part 7A). |
| **Сингапур** – MAS PSN02 | SGD 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. Didit упаковывает их в родной конверт каждого канала: канал TRP передаёт блоки `IVMS101` (`ivms101.2023`, версия указывается в поле `ivms_version` перевода), канал Sygna использует собственный вариант IVMS, зашифрованный по ECIES, а каналы `GTR`, `TRUST` и `VERIFYVASP` используют собственные зашифрованные конверты. Благодаря этому одна и та же структура данных доходит до каждого контрагента в формате, который ожидает его протокол.

Didit считывает следующие ключи из каждого блока данных лица при формировании сообщения IVMS 101: `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`, является пользователем с одобренной верификацией KYC в Didit, Didit передаёт проверенную личность, а не значения, которые вы ввели: см. [Проверенные данные и происхождение](#проверенные-данные-и-происхождение).
  Для любого другого субъекта Didit передаёт именно те `originator_data`, которые вы отправили, поэтому формируйте их из личности, которую вы проверили при онбординге, а не из непроверенного поля формы.
  Именно этого требует формулировка FATF «необходимая и **точная** информация об отправителе».
  На канале `INTERNAL` приложение получателя также получает `subject`, который вы отправили (имя, `vendor_data`, дату рождения, страну и адрес), как контрагента его входящей транзакции.

#### Проверенные данные и происхождение

Когда субъектом перевода является один из ваших пользователей, физическое лицо с одобренной верификацией KYC, Didit формирует эту сторону обмена из проверенных данных:

* отправитель исходящего перевода, который вы создаёте через `POST /v3/transactions/`;
* получатель входящего перевода, о котором вы сообщаете через `POST /v3/transactions/`, или через `POST /v3/travel-rule/inbound/`, если `holder_vendor_data` сопоставленного кошелька указывает на этого пользователя.

В `POST /v3/transactions/` оба варианта применяются к транзакции, у которой `transaction_category` равен `travel_rule`, пока 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 вычисляет оба этих ключа сам, поэтому их копии, а также копии `name_match`, которые вы передаёте в `travel_rule_details.metadata`, отбрасываются.

#### Отправка IVMS 101 напрямую

Помимо плоских объектов `originator_data` и `beneficiary_data`, `travel_rule_details` (в `POST /v3/transactions/`) и `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`, считается содержащим адрес. Перевод Lightning, к которому Travel Rule не применяется, не проверяется.

### Travel Addresses

**Travel Address** - это компактная закодированная строка, которая указывает на endpoint входящих запросов `TRP` VASP-получателя. Она никогда не раскрывает исходный URL или адрес кошелька.
После включения Travel Rule Didit формирует ваш собственный `travel_address` (он доступен только для чтения в настройках), чтобы вы могли передать его другим VASP, которые связываются с вами по каналу `TRP`.
Каждый ваш кошелёк в адресной книге также получает собственный `receiving_travel_address`: запрос, отправленный на него, указывает этот кошелёк, поэтому Didit не нужно искать его по номеру счёта или имени владельца.
Публичный хост TRP в Didit пока не отвечает другим VASP, поэтому приём по `TRP` станет доступен только после этого: см. [Сети Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md).
Если вам известен Travel Address контрагента, сохраните его в [записи адресной книги кошелька назначения](#адресная-книга-кошельков), чтобы Didit направлял переводы этому получателю напрямую по `TRP`, а не переходил к отправке по электронной почте.
Didit проверяет Travel Address, которые вы сохраняете в записи адресной книги кошелька или передаёте в `originating_vasp`: каждый должен начинаться с `ta` и декодироваться в URL запроса с параметром `t=i`, поэтому заглушка вида `ta...` возвращает `400`.
`beneficiary_data.travel_address` в транзакции не проверяется: если он не декодируется, канал `TRP` пропускается.

Кодирование выполняется в формате Base58Check с префиксом `ta`: хост и путь endpoint (который всегда содержит пару ключ–значение `t=i`) плюс четырёхбайтовая контрольная сумма double-SHA-256, закодированная в base58.
Поэтому настоящий адрес выглядит так и имеет длину около 100 символов:

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

В тех случаях, когда Didit проверяет Travel Address, значение, которое не является корректно оформленным адресом с верной контрольной суммой, возвращает `400` с `"Invalid travel address."`.
Берите настоящий адрес из ответа с вашими настройками или от самого контрагента.

## Тестирование без сетевых учётных данных

Для тестирования сценариев, не требующих членства, не нужно состоять в GTR, TRUST, VerifyVASP или Sygna. Но для тестируемого приложения по-прежнему нужен API-ключ Didit.

* **Исходящие в песочнице:** отправьте транзакцию `travelRule` из приложения песочницы. После проверок порога и некастодиального кошелька [Didit Test VASP](https://docs.didit.me/ru/transaction-monitoring/travel-rule-sandbox.md) отвечает на неё внутри Didit по сценарию, который вы выберете по кошельку или имени получателя: принятие, отклонение, несовпадение имени, недоступен, таймаут или некастодиальный. Ни сеть, ни электронная почта, ни другой клиент Didit ничего не получают.
* **Входящие в песочнице:** попросите тестовый VASP отправить вам перевод с помощью [Trigger Test VASP Inbound Transfer](https://docs.didit.me/ru/management-api/travel-rule/trigger-test-vasp-inbound.md) или зарегистрируйте его сами через [Register Inbound Travel Rule Transfer](https://docs.didit.me/ru/management-api/travel-rule/register-inbound.md). Проверьте неизвестный кошелёк, непроверенный кошелёк, несовпадение имени получателя и проверенный совпадающий кошелёк. Эти сценарии воспроизводят реальные решения по входящим переводам без обращения к сети.
* **Два контролируемых продакшн-приложения:** используйте канал `INTERNAL` между двумя вашими продакшн-приложениями (принимающее приложение доступно для поиска и подтверждает контроль над кошельком назначения подписанным сообщением или тестовым депозитом) либо контролируемого контрагента TRP, чтобы протестировать обмен между двумя сторонами без коммерческих сетевых учётных данных. Продакшн-приложения могут приводить к обычным списаниям и уведомлениям, поэтому используйте тестовые данные о личности и контрагентов, которых вы контролируете.

Все сценарии и их точные результаты описаны в [Тестирование Travel Rule в песочнице](https://docs.didit.me/ru/transaction-monitoring/travel-rule-sandbox.md). Успешные тесты в песочнице или локальном протоколе не подтверждают учётные данные, членство или совместимость коммерческой сети в продакшене. Для этого нужна авторизованная песочница сети или контролируемый обмен в продакшн-среде с действительными учётными данными членства.

## Отправка перевода

Включите перевод в [`POST /v3/transactions/`](https://docs.didit.me/ru/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`, созданное после последней смены владельца кошелька. Ваше собственное приложение никогда не учитывается, а если ваш конечный пользователь выбрал участника сети Didit в [виджете подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md), учитывается только доказательство этого участника. Приложение этого клиента работает в продакшн-среде (не в песочнице), имеет включённое Travel Rule и установленный `is_discoverable`, а также является тем VASP, которого вы указали при передаче `counterparty_vasp_lei` или `counterparty_vasp_name`. Кошелёк, который только заявлен самостоятельно, подтверждён скриншотом, не верифицирован или лишь указан в адресной книге, никогда не получает ничего через `INTERNAL`.
   * `TRP` – для актива перевода в сети перевода есть сопоставление Digital Token Identifier (см. [Идентификаторы активов TRP](#идентификаторы-активов-trp)), и в payload передан `beneficiary_data.travel_address`, либо в записи адресной книги получателя сохранён такой адрес, либо контрагент находится в [справочнике](#каталог) (по `counterparty_vasp_lei` или `counterparty_vasp_name`) и имеет endpoint TRP.
   * `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` – вы подключили собственное членство в этой сети в разделе **Marketplace**, и контрагент определяется по идентификатору сети (явное поле или запись в справочнике, см. [Поля определения контрагента](#поля-определения-контрагента)).
     У Didit пока нет членства в платформе в этих сетях (компания проходит подключение как провайдер), поэтому `disabled_networks` на сегодня не меняет этот шаг.
   * `EMAIL` – передан `beneficiary_data.counterparty_vasp_email`, или `counterparty_vasp_name` соответствует записи в справочнике с контактной электронной почтой для комплаенса.
4. Если ни один канал не может определить контрагента, Didit запускает [цепочку поиска контрагента](#поиск-контрагента). Если включён `vasp_attribution_enabled`, цепочка включает [атрибуцию через блокчейн-аналитику](#атрибуция-в-блокчейн-аналитике): адрес назначения кошелька сопоставляется с VASP, которому принадлежит кошелёк, атрибутированное имя записывается в `beneficiary_data.counterparty_vasp_name` (если поле пустое) и в [справочник](#каталог), после чего каналы запускаются повторно один раз. Поэтому кошелёк, атрибутированный VASP, у записи которого в справочнике есть endpoint TRP, идентификатор сети или контактная электронная почта для комплаенса, маршрутизируется уже на этом втором проходе.
5. Если контрагент всё ещё не определён, перевод переходит в `COUNTERPARTY_VASP_NOT_FOUND`. Для исходящих переводов Didit автоматически создаёт [виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) (когда включён `auto_wallet_verification`), чтобы перевод всё же мог завершиться как перевод на некастодиальный кошелёк. Позже, когда будут более точные данные о контрагенте, вы можете повторно запустить маршрутизацию с `action: "resend"`.

Шаги 1 и 2 выполняются внутри запроса. Шаги 3–5 запускаются сразу после ответа: перевод возвращается со статусом `AWAITING_COUNTERPARTY` и `protocol: null`, а каждый результат приходит в виде webhook [`travel_rule.status.updated`](#webhooks).

### Идентификаторы активов TRP

`TRP` обозначает передаваемый актив с помощью его ISO 24165 Digital Token Identifier (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, например собственному стейблкоину компании, нужно, чтобы его эмитент зарегистрировал DTI в регистрационном органе ISO 24165: по одному DTI на каждый токен в каждом блокчейне.
Когда DTI будут получены, свяжитесь со службой поддержки Didit и укажите каждый DTI, его блокчейн и количество десятичных знаков токена, после чего Didit добавит сопоставление.

Входящие запросы `TRP`, которые содержат сопоставленный DTI для конкретной сети, записываются с валютой токена, суммой в десятичных знаках токена и сетью.

### Поиск контрагента

Перевод, который содержит только адрес кошелька, всё равно доходит до контрагента, если его можно идентифицировать.
Прежде чем исходящий перевод будет признан неподдающимся маршрутизации, Didit по порядку проверяет следующие шаги и останавливается на первом, который называет VASP с маршрутом:

| Шаг | Что делает |
| - | - |
| `internal_address_book` | Другой клиент Didit подтвердил контроль над кошельком (подписью или тестовым депозитом), поэтому канал `INTERNAL` доставляет перевод ему. Непроверенная запись адресной книги никогда не учитывается. |
| `wallet_attribution` | Атрибуция кошелька через блокчейн-аналитику, сопоставленная с VASP из справочника, у которого есть маршрут. Сохранённый результат повторно используется и тарифицируется один раз. Требует `vasp_attribution_enabled`. |
| `network_lookup` | Сеть Travel Rule, которая отвечает на вопрос «кто владеет этим адресом», если у вашего приложения есть к ней доступ. Сегодня эту функцию предлагает только GTR, и она выполняется, когда отправку выполняет канал GTR. В остальных случаях шаг записывается как `skipped` с указанием причины. |
| `customer_hint` | Название VASP, LEI или юрисдикция, которые вы передали в `beneficiary_data`, сопоставленные с 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` | Конечный пользователь выбирает VASP-получателя в [виджете кошелька](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) или вы указываете его сами с помощью `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}` на [endpoint переводов](#отправка-перевода). Перевод направляется этому VASP и отправляется повторно. |
| `declare_self_hosted` | Конечный пользователь объявляет кошелёк некастодиальным в том же виджете. |
| `add_vasp_hint` | Передайте название VASP, LEI или `travel_address` в `beneficiary_data`, затем отправьте запрос повторно. |
| `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` | Тег назначения или memo для `wallet_address` в сети с memo. Является частью кошелька везде, где сопоставляется `wallet_address` (см. [Сети с memo и тегами](#сети-с-memo-и-тегами)). Синоним: `destinationTag`. |
| `travel_address` | Маршрутизирует напрямую через `TRP`: самый сильный сигнал, который можно передать. |
| `counterparty_vasp_lei` | Поиск в каталоге по LEI для `TRP`, `GTR`, `TRUST`, `VERIFYVASP` и `SYGNA`. Также сверяется с идентичностью, которую сообщает endpoint TRP: несовпадение или данные об идентичности VASP без LEI прерывают обмен со статусом `COUNTERPARTY_VASP_NOT_REACHABLE`. |
| `counterparty_vasp_name` | Поиск в каталоге для `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` и `EMAIL`: точное название, то же название, записанное иначе (`B.V.` вместо `BV`, диакритический знак), затем псевдоним записи. При наличии обоих полей выполняется нечёткая проверка по идентичности VASP в TRP. |
| `counterparty_vasp_jurisdiction` | Код страны по ISO 3166 для принимающего юридического лица, если названный VASP является брендом с несколькими регулируемыми юридическими лицами или если в каталоге несколько записей с одним названием; см. [Бренды и их юридические лица](#бренды-и-их-юрлица). |
| `counterparty_gtr_vasp_code` | Явный идентификатор сети `GTR`: пропускает каталог. |
| `counterparty_trust_membership_id` | Явный идентификатор сети `TRUST`: пропускает каталог. |
| `counterparty_verifyvasp_id` | Явный идентификатор сети `VerifyVASP`: пропускает каталог. |
| `counterparty_sygna_vasp_code` | Явный код VASP в `Sygna Bridge`: пропускает каталог. |
| `counterparty_vasp_email` | Явный контакт для комплаенса для канала `EMAIL`: пропускает каталог. |

### Типы цепочек

В цепочке на основе аккаунтов (EVM-сети, Solana, Tron) перевод перемещает средства с одного адреса на другой, и Didit считывает оба кошелька непосредственно из транзакции.
Три других типа требуют дополнительных данных, и то, что Didit определил для них, хранится в переводе в поле `chain_details` (пустое, `{}`, в цепочке на основе аккаунтов).
`chain_details.originator` и `chain_details.beneficiary` содержат `address`, его `memo` и источник (`source`), из которого он получен; сопоставление и маршрутизация сначала используют кошельки, указанные там.

#### Переводы UTXO

Транзакция в Bitcoin, Litecoin, Bitcoin Cash или Dogecoin расходует входы с любого количества адресов и выплачивает любое количество выходов, часть которых возвращается отправителю в виде сдачи.
Передавайте входы и выходы в `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` (см. [Регистрация после депозита](#регистрация-депозита-задним-числом)).

#### Сети с memo и тегами

В сетях XRP, Stellar (XLM), EOS, Cosmos (ATOM) и Hedera (HBAR) биржа получает всех клиентов на одном общем адресе и различает их по destination tag или memo.
Поэтому memo является частью кошелька: передайте его как `payment_method.memo` у участника или как `memo` в `originator_data` / `beneficiary_data` (псевдоним `destinationTag`). Didit сравнивает адрес и memo вместе.

* Запись адресной книги с memo совпадает только с переводами, в которых указано это memo. Запись без memo совпадает со всем адресом. Перевод без memo никогда не попадает в запись, которая его имеет: на общем адресе депозит без тега никому не принадлежит.
* X-адрес XRP содержит тег внутри себя: Didit разделяет его на классический адрес и тег (`source: x_address`), поэтому кошелёк, зарегистрированный в любой из этих форм, сопоставляется с переводом в другой форме.
* Каждое memo на общем адресе является отдельным способом оплаты в транзакции.
* Что принимает каждая сеть: destination tag XRP - целое число от 0 до 4294967295 (допускается число в JSON); memo Stellar - текст до 28 байт, 64-битный идентификатор или 32-байтовый хеш в hex; EOS принимает до 256 байт, Cosmos до 256 символов, Hedera до 100 байт. memo в сети, которая его не поддерживает (Bitcoin, EVM-сети, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin, Lightning), возвращает `400`.

#### Lightning

У платежа Lightning нет адреса получателя в блокчейне: плательщик владеет запросом на платёж.
В криптопереводе bitcoin или Lightning `payment_method.account_id` может быть этим запросом, и Didit определяет получателя по нему, не обращаясь ни к одной сети:

| Что вы отправляете | Получатель |
| - | - |
| Счёт BOLT11 (`lnbc...`) | Узел получателя, указанный в счёте или восстановленный по его подписи. |
| Предложение или счёт BOLT12 (`lno1...`, `lni1...`) | Узел эмитента, если только он не скрыт за скрытыми путями (blinded paths). |
| Lightning Address (`name@domain`) | Сам адрес. |
| Идентификатор узла | Сам узел. |
| 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`.
Значение считается Lightning только в сети bitcoin, в `LIGHTNING` или когда сеть не указана (Lightning Address только в bitcoin или `LIGHTNING`). В любой другой сети это собственный адрес этой сети.

> [!NOTE]
>
> Канал `INTERNAL` достигает другого клиента Didit только через кошелёк, контроль над которым клиент подтвердил подписанным сообщением или тестовым депозитом. Такие подтверждения сегодня существуют для Bitcoin, EVM-сетей, Solana и Tron.
> Поэтому в сетях с memo, Lightning, Litecoin, Bitcoin Cash и Dogecoin перевод никогда не достигает другого клиента Didit через `INTERNAL`.
> Сопоставление по 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/ru/transaction-monitoring/aml-screening.md), знает, какие кошельки принадлежат каким биржам и кастодианам.
Атрибуция выполняется только для исходящих переводов, которые содержат и кошелёк назначения, и `chain` (см. [Поля определения контрагента](#поля-определения-контрагента)). Без указания сети она пропускается.

Если кошелёк атрибутирован известной сущности, Didit:

1. Фиксирует атрибуцию в переводе: `beneficiary_data.counterparty_vasp_name` заполняется (если вы не передали его сами), а метаданные перевода содержат блок `vasp_attribution` с названием сущности и типом сущности, страной и `source: "blockchain_analytics"`.
2. Добавляет организацию в [справочник VASP](#каталог) (или сопоставляет с существующей записью), чтобы атрибуция оставалась стабильной между переводами.
3. Повторно запускает каналы один раз с атрибутированным названием. Запись справочника, уже содержащая endpoint 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`, так как ни одно правило не повлияло на транзакцию; ваши собственные правила могут установить другой статус.
* Контакт комплаенса получает ссылку для получения, и перевод завершается, не проходит проверку совпадения имени или отклоняется, когда он отвечает, либо истекает, если он не отвечает (см. [Получение через email](#отправка-по-email-контрагентам-которые-недоступны)).

`protocol` остаётся `null`, потому что канал выбирается после ответа (например, `EMAIL`, если вы передали `counterparty_vasp_email`): [Получить транзакцию](https://docs.didit.me/ru/management-api/transactions/get.md) показывает его после выбора, а каждое последующее событие webhook `travel_rule.status.updated` содержит его в `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/ru/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` (с `resend queued` в его `timeline`), а результат приходит как webhook `travel_rule.status.updated`.
Повторный запрос, отправленный во время выполнения предыдущей попытки, будет обработан после завершения этой попытки.
Любой другой статус или входящий перевод возвращает `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` сообщает, почему он застрял и что позволяет ему продвинуться дальше, при каждом запросе его транзакции.
Блок `travel_rule` в [`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/ru/management-api/transactions/get), в ответе `201` на [`POST /v3/transactions/`](https://docs.didit.me/ru/management-api/transactions/create) и в каждой строке [`GET /v3/transactions/`](https://docs.didit.me/ru/management-api/transactions/list) содержит его, так же как и объект перевода, который возвращает [эндпоинт переводов](https://docs.didit.me/ru/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": "..."}` на endpoint перевода. |
| `resend` | Повторно запустите маршрутизацию с `{"action": "resend"}`, когда что-то изменится. |
| `select_counterparty_vasp` | Конечный пользователь выбирает VASP получателя в [виджете подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md). Ссылка `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`.

### Объявленные некастодиальные кошельки

Если ваш клиент сообщает, что кошелёк назначения является его собственным некастодиальным (unhosted) кошельком, объявите это в транзакции, а не ждите, пока маршрутизация выяснит, что за кошельком нет 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/ru/transaction-monitoring/wallet-ownership-widget.md), привязанную к транзакции, и возвращает её в блоке `action_required` транзакции; SDK запускают её автоматически при [отправке на стороне клиента](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md).
* Как только клиент подтверждает владение кошельком, перевод выполняет сверку имени получателя и переходит в `COMPLETED` (или в `COUNTERPARTY_MISMATCHED_DATA`, если сверка не прошла).

Это требование EU TFR к некастодиальным кошелькам в виде API: проверить владение некастодиальным кошельком вместо обмена данными с контрагентом-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`.

Endpoint принимает перевод в любом из следующих статусов и возвращает `409` с `{"detail": "Transfer is not awaiting ownership confirmation."}` для любого другого статуса:

* `UNCONFIRMED_OWNERSHIP`, в любом направлении.
* `ON_HOLD` для входящего перевода.
* `UNCONFIRMED_OWNERSHIP`, `ON_HOLD` или `COUNTERPARTY_MISMATCHED_DATA` для входящего перевода `TRP`, чтобы комплаенс-офицер мог вручную обработать запрос контрагента.

Для перевода, который Didit создал сам (канал `INTERNAL`, регистрация sunrise или заявленный некастодиальный кошелёк):

* `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 создаёт или подтверждает запись, отправляет контрагенту callback с решением по запросу, устанавливает `ownership_confirmed` и переводит перевод в `COMPLETED`.
* `confirmed: false` отправляет ответ об отказе и переводит перевод в `COUNTERPARTY_VASP_GENERAL_DECLINE`.
* Если callback endpoint контрагента недоступен, запрос возвращает `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/ru/transaction-monitoring/travel-rule-sandbox.md)), подтверждение добавляет кошелёк в вашу адресную книгу на имя получателя и запускает сверку имени: при совпадении перевод завершается, а кошелёк помечается как подтверждённый.

### Переводы из сети

Didit также может принимать переводы Travel Rule, которые контрагент отправляет по сетям `GTR`, `VERIFYVASP` или `SYGNA`.
Didit отдельно включает приём для каждой сети, и **сейчас он не включён ни для одной**: для этого также нужна регистрация endpoint'ов Didit в каждой сети, а членства Didit в этих сетях пока нет.
Переводы `TRUST` принять нельзя.

Когда сеть включена, сообщение, которое она доставляет, обрабатывается так:

* **Маршрутизация.** Сообщение попадает в ваше приложение, если в нём указан код участника вашего подключённого членства в этой сети. Сообщение, адресованное членству самого Didit, попадает к вам только в том случае, если вы являетесь единственным действующим клиентом Didit, подтвердившим контроль над кошельком получателя. Всё остальное отклоняется, поэтому ни один клиент никогда не получает перевод другого клиента.
* **Решение.** Didit создаёт входящий перевод в вашем приложении и принимает по нему решение так же, как по [запросу TRP](https://docs.didit.me/ru/transaction-monitoring/travel-rule-interoperability.md#trp-endpoint): сопоставление кошелька, владение, сверка имени и `inbound_auto_accept`.
* **Однократность.** Сети повторно отправляют сообщение, пока на него не ответят. Повтор получает тот же ответ, что и первая копия, и ничего не создаётся и не решается дважды.
* **Ваша проверка по `GTR` и `SYGNA`.** Решение по переводу, который ждёт вашего решения (`UNCONFIRMED_OWNERSHIP`, `ON_HOLD` или `COUNTERPARTY_MISMATCHED_DATA`), принимается с помощью [`POST .../travel-rule/ownership/`](#подтверждение-владения-кошельком): `confirmed: true` одобряет его (передайте `wallet_address`, если в переводе он не указан), а `confirmed: false` отклоняет. Didit отвечает сети после ответа endpoint, с [теми же повторными попытками](#доставка-и-повторные-попытки). Перевод, на который уже ответили, возвращает `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` содержит email пользователя консоли или метку 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" }
    }
  }
}
```

Запись хронологии называет поля, например «дата рождения получателя не совпадает с владельцем кошелька».
После расхождения отказ по `TRP` сообщает VASP-отправителю, какие поля различаются, например: «Данные получателя не совпадают с владельцем кошелька: имя».
Объём информации, которую получает другая 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` (имя, которое контрагент указал на странице ответа по email, или ответ Didit Test VASP).
* Исход перевода определяется `data_match`, который сравнивает все поля, поэтому перевод может иметь статус `COUNTERPARTY_MISMATCHED_DATA`, при этом `name_match.matched` равно `true`.
* `name_match` есть в объекте перевода, который возвращают эндпоинты Travel Rule, и во встроенном объекте `travel_rule` транзакции, и он равен `null`, пока имя не сравнивалось. Список переводов и webhook'и этот объект не содержат.
* Имена скрыты, пока стороны [замаскированы](#маскирование-данных-контрагента), а также в копии обмена `INTERNAL` у другого клиента.

### Сообщения и запросы документов

Два VASP могут разрешить спорный перевод внутри Didit, а не по email.
`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 МБ каждый в форматах PDF, PNG, JPEG или WebP. Didit проверяет содержимое каждого файла, а не только заявленный тип. При загрузке любого другого файла запрос возвращает `400` с ошибкой в поле `files`: «Attach PDF, PNG, JPEG or WebP files only.».
* Перевод вмещает до 100 сообщений, из них до 20 могут содержать вложения (до 5 файлов в каждом), с учётом обеих сторон. При превышении любого из этих лимитов запрос возвращает `400`. Вложения в сообщениях не учитываются в документах самой транзакции.
* Сообщения работают по каналам `INTERNAL` и `EMAIL`. При `INTERNAL` другой клиент Didit видит сообщение на своей стороне пары. При `EMAIL` контрагенту отправляется email, и он отвечает на портале получения. Если уведомление по email не удалось отправить, сообщение всё равно сохраняется, а запрос возвращает `201`.
* Запрос возвращает `409` с причиной в `detail` для перевода по `TRP`, `GTR`, `TRUST`, `VerifyVASP` или `Sygna` (эти протоколы не передают сообщения), для перевода без контрагента, для перевода `INTERNAL`, данные которого не совпали с владельцем кошелька (получатель ничего не получил), для перевода `INTERNAL`, где получатель ещё не доказал контроль над кошельком (с обеих сторон, пока он не докажет), для перевода `EMAIL`, у контрагента которого больше нет адреса email для комплаенса, и для перевода `EMAIL`, где ссылка на портал получения истекла или перевод закрыт.
* Webhook не срабатывает, когда контрагент отвечает. Читайте эндпоинт сообщений.
* Письмо контрагенту о новом сообщении отправляется на английском языке.

### Адресная книга кошельков

Регистрируйте кошельки, которыми вы управляете, чтобы входящие переводы (канал `INTERNAL`, входящие запросы `TRP` и [регистрации после депозита](#регистрация-депозита-задним-числом)) могли сопоставляться с ними. Используйте `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 | Необязательное. Destination tag или memo, который является частью кошелька в [сети с memo](#сети-с-memo-и-тегами); для всего адреса пусто. Синоним: `destinationTag`. |
| `chain` | string | Необязательный идентификатор сети, до 32 символов. Используйте тот же токен, что и в ваших переводах (например, `ETH` или `BTC`), или оставьте поле пустым, чтобы сопоставлять переводы в любой сети – см. примечание ниже. Получатель Lightning (ID узла или Lightning Address), зарегистрированный в bitcoin, сохраняется в нижнем регистре как `LIGHTNING`; здесь сопоставляются переводы [Lightning](#lightning). |
| `holder_name` | string | Имя владельца кошелька, используется для сверки имени получателя. |
| `holder_vendor_data` | string | Ваш внутренний идентификатор владельца. |
| `entity_type` | string | Произвольный текст до 16 символов, по умолчанию `"individual"`. |
| `travel_address` | string | Необязательное. Travel Address VASP-контрагента для этого кошелька, если вы его знаете. Позволяет Didit направлять переводы этому получателю по каналу `TRP`, а не по email. |
| `receiving_travel_address` | string | Только для чтения. Ваш собственный Travel Address для этого кошелька. Передайте его VASP, который отправляет на этот кошелёк: его запрос TRP будет напрямую указывать на кошелёк. Пусто для кошелька, записанного как кошелёк другого VASP, пока Travel Rule отключён, и до сохранения профиля вашего VASP. После удаления кошелька перестаёт отвечать. |
| `is_ownership_verified` | boolean | Только для чтения. Подтверждено ли владение этим адресом (через [виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md) или самодекларацию). |
| `self_declared` | boolean | Только для записи, только `POST`. Если `true`, запись создаётся уже подтверждённой через доказательство `SELF_DECLARATION`: входящие переводы на этот адрес полностью пропускают `UNCONFIRMED_OWNERSHIP` и сразу переходят к сверке имени. Возвращает `400`, если в ваших настройках выключен `allow_self_declaration`. |

`PATCH` принимает `holder_name`, `holder_vendor_data`, `entity_type` и `travel_address` и возвращает только эти четыре поля.
`DELETE` удаляет запись (`204`): её адрес освобождается для повторной регистрации, и она сразу перестаёт сопоставляться с новыми переводами.

> [!NOTE]
>
> Сопоставление нечувствительно к регистру адреса, учитывает его `memo` в сети с 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` и эндпоинт владения берут любую из совпадающих записей, поэтому по возможности держите одну запись на один адрес.
> Канал `INTERNAL` не сопоставляет ни одной записи, если подтверждённые записи для адреса существуют более чем в одном другом приложении.

#### Массовый импорт кошельков

Импортируйте до 10 000 кошельков одним вызовом [`POST /v3/travel-rule/wallet-addresses/import/`](https://docs.didit.me/ru/management-api/travel-rule/import-wallet-addresses) в формате JSON (`{"entries": [...]}` или простой массив) или как файл CSV в кодировке UTF-8 размером не более 5 МБ со строкой заголовков.
Строка задаёт кошелёк через `address`, через `address_hash` (SHA-256 адреса в нижнем регистре, 64 шестнадцатеричных или 44 символа base64) либо через оба поля, а также любые поля записи, перечисленные выше.
Чтобы зарегистрировать кошелёк, не раскрывая его адрес, передайте только хеш: такая запись сопоставляется с переводами на этот адрес, как любая другая, и её владение можно подтвердить через [виджет подтверждения владения кошельком](https://docs.didit.me/ru/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` рядом с двумя кошельками.

### Отправка по email контрагентам, которые недоступны

Если ни один протокольный канал не может связаться с контрагентом, Didit переходит на `EMAIL`: он отправляет одноразовую ссылку для получения перевода на `beneficiary_data.counterparty_vasp_email` (или на email, найденный по `beneficiary_data.counterparty_vasp_name` в реестре VASP).
Получателю не нужна учётная запись Didit или API-ключ: аутентификацией служит токен в ссылке.
Как отправляется запрос:

* **На правильные адреса.** Переданный вами `counterparty_vasp_email` – единственный получатель. Иначе Didit пишет на до трёх адресов: [контакты](#контакты) контрагента, на которые ранее не приходили возвраты (сначала ваши, затем контакты платформы, затем те, что отвечали раньше), а затем email из записи реестра, если остался свободный слот. Каждый адрес получает своё письмо.
* **От вашего имени.** Письмо приходит с адреса Didit в виде «*ваше юридическое название* via Didit» на языке юрисдикции контрагента (английский, если она неизвестна). Ссылка открывает страницу ответа на этом языке. Копия вам не приходит: отслеживайте хронологию перевода.
* **С напоминаниями.** Пока контрагент не ответил, Didit напоминает ему за 72, 24 и 6 часов до срока, не раньше чем через 12 часов после запроса или последнего напоминания, и никогда не после срока.
* **С обработкой возвратов.** Возврат письма или жалоба на спам помечают контакт, до которого дошли, как недействительный, с указанием причины, и хронология перевода это фиксирует. Контакт платформы тогда недействителен для всех клиентов Didit, а собственный контакт вашего приложения – только для вашего приложения. Если Didit полностью прекращает отправку email на адрес (после постоянного возврата, жалобы на спам или отписки), каждый контакт по этому адресу помечается как недействительный (`suppressed`), и эти контакты снова становятся действительными, когда отправка на адрес возобновляется. Если не осталось адреса для отправки, перевод переходит в статус `COUNTERPARTY_VASP_NOT_REACHABLE`, а причина от почтового сервера получателя записывается в хронологию. Постоянный возврат или жалоба на спам на переданный вами `counterparty_vasp_email` приводят к этому сразу, поскольку это единственный получатель. Временный возврат (`soft_bounce`) на него или на собственный compliance email записи реестра оставляет запрос открытым для напоминаний.

Ссылка открывает размещённую страницу, на которой показан ожидающий обмен. Для получателя страница обращается к двум публичным 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`. `join_invitation.eligible` равно `true`, пока у контрагента есть действующее приглашение присоединиться к Didit. Сама ссылка на приглашение передаётся только в письме.

Этот 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 101 в поле `ivms_response`. Тогда имя получателя из него имеет приоритет над `beneficiary_name`.
При `decline` перевод переходит в статус `COUNTERPARTY_VASP_GENERAL_DECLINE`. Контрагент может указать `decline_reason` из `decline_reasons` и добавить `decline_detail` (до 512 символов, обязательно для `other`). Ваша хронология перевода сохраняет эту причину, например: `counterparty declined: The beneficiary is not our customer`.
Ответ содержит итоговый `{"status": "..."}`, а после отказа также `decline_reason`. После ответа контрагента оба endpoint'а возвращают `410`.
Оба endpoint'а возвращают `404` для неизвестного токена и `410`, когда окно закрыто: либо перевод вышел из статуса `AWAITING_COUNTERPARTY`, либо истёк срок подтверждения (`confirmation_timeout_hours` с момента создания или последней повторной отправки).

Контрагент читает и отвечает по ссылке для получения с помощью `GET` и `POST /v3/travel-rule/pickup/{token}/messages/`.
API-ключ для этого не нужен. Принимаются только вложения PDF, PNG, JPEG или WebP. Если ссылка истекла или перевод закрыт, endpoint возвращает `410`.
Сообщение, которое отправили вы, показывает название вашего VASP в поле `author` и в поле `uploaded_by` вложения, а не имя человека или API-ключ, использованный для отправки.

### Непривязанные депозиты

Входящий депозит может прийти раньше, чем данные Travel Rule от отправляющего VASP.
Такой депозит остаётся в статусе `AWAITING_COUNTERPARTY` или `NOT_ENOUGH_COUNTERPARTY_DATA`, пока данные не поступят.
Чтобы получить список депозитов, которые всё ещё ждут, используйте API-ключ с привилегией transactions:

```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."), если одна из сторон уже перешла в другой статус.

### Регистрация депозита задним числом

Когда криптодепозит зачисляется ончейн **до** того, как состоялся какой-либо обмен 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](#travel-addresses). Заглушка вернёт `400` с сообщением `"Invalid travel address."`.
`originating_vasp.lei` должен быть пустым или состоять из 20 символов в формате LEI (18 букв или цифр, затем 2 цифры). Любое другое значение вернёт `400` с сообщением `"Invalid LEI: expected 20 alphanumeric characters (ISO 17442)."`.
Didit проверяет только формат, а не контрольные цифры ISO 17442.

| Поле | Тип | Описание |
| - | - | - |
| `chain` | string | Сеть, в которой зачислен депозит, до 32 символов. Didit переводит значение в нижний регистр и использует его для сопоставления с записями в вашей [адресной книге](#адресная-книга-кошельков), поэтому используйте тот же токен, что и в записях (`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 | Целевой кошелёк, на который поступил депозит. X-адрес XRP разбивается на классический адрес и тег. |
| `memo` | string | Необязательное поле. Тег назначения или мемо депозита в [сети с мемо](#сети-с-memo-и-тегами): на общем адресе депозита оно указывает клиента, которому принадлежит депозит. |
| `amount` | string (decimal) | Сумма депозита. |
| `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-`, первых 80 символов `txid` и 12-символьного хеша от `txid` и кошелька.
`created` равно `true` (HTTP `201`), если этот вызов зарегистрировал депозит, и `false` (HTTP `200`), если такие же `txid` и `wallet_address` уже были зарегистрированы. В этом случае существующий перевод возвращается без изменений.
В сценарии с верифицированной записью обе смены статуса происходят до ответа endpoint, поэтому синхронный ответ уже показывает конечный статус `FINISHED`. Промежуточный шаг `COMPLETED` при этом всё равно фиксируется в `timeline` перевода.
Каждый переход отправляет webhook `travel_rule.status.updated` (`COMPLETED` и `FINISHED` отправляют по одному).
Если Didit создала новую транзакцию, сначала отправляется [`transaction.created`](https://docs.didit.me/ru/integration/webhooks#типы-событий), а затем эти события.
Регистрация не взимает плату за Travel Rule или мониторинг.
Если включён мониторинг криптовалют и `chain` поддерживается Didit для скрининга, новый депозит также проходит скрининг и тарифицируется один раз по тарифу Wallet Verification & Screening.

### Виджет подтверждения владения кошельком

Чтобы вывести перевод из статуса `UNCONFIRMED_OWNERSHIP` или заранее верифицировать кошелёк до поступления перевода, создайте размещённый виджет подтверждения владения через `POST /v3/travel-rule/widget-session/` и отправьте клиента по возвращённому `url`. Виджет подтверждает контроль над кошельком подписью сообщения (включая смарт-контрактные аккаунты EVM по ERC-1271 и ERC-6492), тестом Сатоши, скриншотом или самодекларацией. Затем он помечает соответствующую запись адресной книги как верифицированную, чтобы обмен мог продолжиться.
Исключение составляет скриншот: он сам по себе никогда не подтверждает кошелёк, поэтому команда комплаенса проверяет его и подтверждает перевод в `UNCONFIRMED_OWNERSHIP` через [`POST .../travel-rule/ownership/`](#подтверждение-владения-кошельком).

См. [Виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md): там описаны полный процесс, методы подтверждения и важное требование. Виджет нужно открывать в настоящем браузере, но никогда внутри webview мобильного SDK.

## Каталог

`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, в том порядке, в котором их пробует лестница маршрутизации. `reachable` равен `true`, когда хотя бы один маршрут имеет статус `available`:

* `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 Test VASP](https://docs.didit.me/ru/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` пропускает его. Поле `created_by` пустое у контактов платформы и у контактов, которые добавил API-ключ.

### Как записи попадают в каталог

* Промежуточный реестр ESMA провайдеров услуг, связанных с криптоактивами, в соответствии с MiCA, который читается раз в неделю: название каждого провайдера, LEI и государство-член происхождения.
* Публичный каталог TRISA, который читается раз в неделю: кто является каждым участником, его страна и его листинг в TRISA. Если листинг пропадает из каталога на три недели, он удаляется.
* Глобальный реестр LEI дополняет запись LEI, добавляя другие юридические названия и страну. Он находит LEI записи по точному юридическому названию в её стране.
* [Атрибуция в блокчейн-аналитике](#атрибуция-в-блокчейн-аналитике) добавляет или сопоставляет VASP, которому принадлежит адрес кошелька назначения, и сохраняет атрибутированное название как псевдоним.
* Сотрудники Didit курируют записи: идентификаторы маршрутизации и сетевые листинги, которые они проверили, контакты для комплаенса, псевдонимы, бренды и их юридические лица, объединённые дубликаты и скрытые записи.

Данные, которые клиент сообщает о контрагенте, например входящий `originating_vasp` или Travel Address, остаются в переводе этого клиента и никогда не меняют общую запись. Записи сопоставляются сначала по 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 | Полный балл, если до записи доступен нативный канал протокола (endpoint TRP или канал `INTERNAL`), 40 процентов, если доступен только резервный канал по электронной почте, ноль без доступного канала. | `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` | Домен записи совпадает с её endpoint TRP или с сайтом, указанным в её реестре. |
| `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` подсчитывает запросы, которые клиенты Didit направили VASP за последние 90 дней и результат которых известен, а `answered` те из них, на которые VASP ответил:

* **Учитываются:** запросы, отправленные по сетевому каналу или каналу протокола, и запросы, отправленные по электронной почте на общий адрес, то есть на адрес комплаенса записи или на контакт платформы.
* **Никогда не учитываются:** запрос, отправленный на адрес электронной почты, который ввёл один клиент, или на личный контакт клиента. Такой обмен остаётся в истории переводов этого клиента и никогда не присваивает VASP метку для всех остальных.
* **Ответ получен:** `COMPLETED`, `FINISHED`, `COUNTERPARTY_VASP_GENERAL_DECLINE` или `COUNTERPARTY_MISMATCHED_DATA`. Отказ или несоответствие данных тоже считаются ответом: VASP вступил во взаимодействие.
* **Ответа нет:** `EXPIRED`.
* **Ни то ни другое:** любой другой статус. Запрос со статусом `CANCELLED` является действием самого отправителя, `COUNTERPARTY_VASP_NOT_REACHABLE` может быть сбоем на стороне отправителя, а запрос, который ещё выполняется, пока не имеет результата.

| `status` | Когда |
| - | - |
| `UNKNOWN` | Менее 5 запросов с решением за период, поэтому один просроченный запрос никогда не присваивает VASP статус. |
| `UNRESPONSIVE` | VASP ответил менее чем на 10 процентов своих запросов с решением. |
| `RESPONSIVE` | В остальных случаях. |

Участники сети Didit отвечают внутри Didit и всегда показывают `UNKNOWN` с нулевыми счётчиками. Для неотвечающего контрагента действуйте по [политике неответов](#политики-переходного-периода-и-контрагентов-не-отвечающих-на-запросы) или по [пресету](#поля-правил-и-пресеты) `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
  }
}
```

* **Оплата.** Каждый новый скрининг стоит [цену](https://docs.didit.me/ru/getting-started/pricing.md) Company AML Screening для вашей организации. `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` при возможном совпадении по санкциям или негативным публикациям в СМИ. `flagged` равен `true`, пока актуальный скрининг имеет статус `In Review` или `Declined`.
* **Влияние.** Пока ваш последний скрининг контрагента помечен и скрининг остаётся включённым, `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` | Бенефициарные владельцы с долей 25 процентов и более, а также любые материнские или зонтичные структуры. |
| `record_keeping` | Практика ведения записей и срок хранения данных Travel Rule и данных о транзакциях. |
| `data_protection` | Как вы защищаете персональные данные, передаваемые для целей комплаенса, и где они хранятся. |

### Переводы с одним контрагентом

[`GET /v3/travel-rule/transfers/?counterparty={vasp_id}`](https://docs.didit.me/ru/management-api/travel-rule/list-transfers) выводит переводы по Travel Rule вашего приложения с одним контрагентом, от новых к старым, по `id`, который показывает справочник: ваши переводы, контрагентом которых является эта запись справочника, или ваши обмены `INTERNAL` с этим участником сети Didit. Значение, которое не является допустимым id, возвращает `400`.

Каждая строка содержит `reason` сразу после `status`: [`reason`](#почему-перевод-застрял) самого перевода. Так список позволяет отличить письмо комплаенса, которое не было доставлено, от контрагента, до которого нет маршрута, не открывая каждый перевод.

### Политики переходного периода и контрагентов, не отвечающих на запросы

Когда срок перевода истекает (прошло `confirmation_timeout_hours`, пока он находился в статусе `AWAITING_COUNTERPARTY`), `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"]`. Только эти страны считаются находящимися в переходном периоде: для любой другой, а также для пустой или неизвестной юрисдикции используется `timeout_outcome`, поэтому контрагент никогда случайно не получит вашу политику переходного периода. В хронологии истёкшего перевода указывается результат, который был применён, например `sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet)`. Ваши правила продолжают выполняться после истечения срока, поэтому такое правило, как EU TFR `tr_eu_expired_review`, по-прежнему может перевести транзакцию на проверку при `PROCEED`.

### Поля правил и пресеты

Четыре поля [правила](https://docs.didit.me/ru/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` работают с этими полями. Установите их в **Transactions → Rules → Library** в Business Console или с помощью [API библиотеки правил](https://docs.didit.me/ru/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.

## Webhooks

Настройте адрес назначения 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 отправляет `travel_rule.status.updated` после `transaction.created`: когда 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/ru/transaction-monitoring/travel-rule-sandbox.md), содержит `rail: "TEST_VASP"`, `test_vasp: true` и `test_vasp_scenario`.

Полный каталог событий, проверку подписи и политику повторных попыток см. в [руководстве по webhook](https://docs.didit.me/ru/integration/webhooks.md#типы-событий).

## Цены

Сбор за Travel Rule составляет **\$0.02 за каждый исходящий обмен, который начинается**, и списывается однократно при создании транзакции, независимо от того, сколько переходов статуса обмен проходит потом. Повторная отправка (`resend`) никогда не списывает его снова.

* Он применяется к исходящей транзакции `travelRule` в приложении с включённым Travel Rule, сумма которой равна или превышает ваш `threshold_amount`, поэтому Didit запускает для неё управляемый обмен. Перевод на некастодиальный кошелёк или на собственный верифицированный кошелёк пользователя также подпадает под это правило: доказательство владения и есть его обмен.
* Каждая другая исходящая транзакция, которая содержит данные Travel Rule, тарифицируется как стандартный Transaction Screening. Ваш тариф включает его до [месячного лимита транзакций](https://docs.didit.me/ru/getting-started/pricing.md#мониторинг-транзакций-kyt), а сверх лимита он стоит \$0.001: это перевод ниже вашего порога, платёж Lightning, к которому Travel Rule не применяется, и перевод, зафиксированный в пассивном режиме, когда Travel Rule выключен. Транзакция тарифицируется по одной из двух строк, никогда по обеим.
* Входящие транзакции `travelRule` не несут сбора за Travel Rule или мониторинг: это входящая часть перевода, которую Didit создаёт в приложении получателя на канале `INTERNAL`, входящие запросы `TRP` и [регистрации после депозита](#регистрация-депозита-задним-числом).
* Приложение, которое подключилось к Travel Rule бесплатно, не платит ни один из сборов за исходящие транзакции `travelRule`, пока не включит биллинг исходящих через `POST /v3/travel-rule/workspace/billing/`. Приём остаётся бесплатным.
* Приложения в песочнице никогда не тарифицируются.
* Сбор за Travel Rule по прайсу составляет $0.02, а Transaction Screening стоит $0.001 сверх вашего лимита. Каждая транзакция оплачивается по цене той строки, по которой она тарифицируется.
* Если крипто-AML или скрининг кошельков также выполняется для той же транзакции (например, входящий депозит с `currency_kind: "crypto"` и включённым мониторингом), он тарифицируется отдельно по стандартной ставке Wallet Verification & Screening. В сбор за Travel Rule он не входит.
* [Скрининг контрагента](#скрининг-контрагента) включается по выбору и тарифицируется отдельно, по цене Company AML Screening за каждую проверку. Проверка, выполненная менее 30 дней назад, используется повторно без оплаты. Решения, анкеты и оценка должной осмотрительности в каталоге ничего не стоят.

### Цены на атрибуцию

[Атрибуция через блокчейн-аналитику](#атрибуция-в-блокчейн-аналитике) требует одного запроса по кошельку к провайдеру аналитики, и вы всегда получаете полный результат этого запроса:

* **Крипто-мониторинг уже проверил кошелёк в этой транзакции** - сохранённый результат используется повторно. Один вызов аналитики и одно списание Wallet Verification & Screening (то, которое уже выполнил крипто-мониторинг). Атрибуция ничего не добавляет.
* **Крипто-мониторинг не запускался** - Didit делает необходимый вызов аналитики один раз и передаёт его полный результат дальше. Транзакция оплачивается по стандартной ставке Wallet Verification & Screening (\$0.15 при аналитике, управляемой Didit) и получает полный вывод крипто-скрининга: оценку риска, категорию риска кошелька, снимок сети, а не только название VASP. Строка использования помечается триггером `travel_rule_attribution`, чтобы списание можно было объяснить.
* **Собственный ключ аналитики** - здесь применяется та же платформенная комиссия, что и для крипто-мониторинга с вашим ключом (\$0.02 за вызов). Ваш провайдер выставляет счёт за базовый запрос напрямую вам.
* **Неуспешные запросы никогда не тарифицируются**. Повторные отправки переиспользуют записанную атрибуцию без нового вызова и списания, а приложения в песочнице никогда не тарифицируются.

Итак, исходящий перевод, которому понадобилась атрибуция, стоит \$0.02 (Travel Rule) плюс одно списание Wallet Verification & Screening. Когда включены оба продукта, вызов аналитики и его списание происходят только один раз.

## Пакет правил EU TFR

В пакет `travel_rule_eu_tfr` входят четыре предустановленных правила, созданных специально для обязательств по EU TFR.
Как и любой другой [предустановленный пакет](https://docs.didit.me/ru/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` с KYC-профилем, который Didit верифицировал при онбординге (OCR документа, проверка живости, сравнение лиц). Поэтому вы можете формировать `originator_data` из проверенных полей на той же платформе.
  Для пользователя с одобренной KYC-верификацией Didit передаёт проверенную идентичность, а не значения, которые вы ввели вручную (см. [Проверенные данные и их происхождение](#проверенные-данные-и-происхождение)).
* **Биометрическая проверка для рискованных переводов.** [Правило](https://docs.didit.me/ru/transaction-monitoring/rules.md) может удерживать любой перевод на крупную сумму до тех пор, пока пользователь не пройдёт повторную биометрическую проверку по лицу, снятому при онбординге. Это самый надёжный ответ на захват учётной записи в момент вывода средств. См. [пример с разбором](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md#пример-повторная-биометрическая-проверка-для-крупного-вывода-средств).
* **Один граф устройств.** Переводы, отправленные через SDK, содержат те же данные об устройстве, что и KYC-сессии, поэтому правило «новое устройство + крупная сумма + первый получатель» пишется одним правилом, а не соединением таблиц в хранилище данных.
* **Скрининг, правила, кейсы и обмен в одном месте.** Скрининг кошелька, который определяет VASP контрагента, это тот же скрининг, который видят ваши правила и аналитики: одно списание, одна консоль, один журнал аудита.
* **Опубликованные цены.** [\$0.02 за исходящий обмен](#цены), без тарифов, зависящих от объёма, и без звонка в отдел продаж.

## FAQ

### Как работает обмен

> [accordion] Didit сам проводит обмен между VASP, или он только записывает данные Travel Rule после того, как другая сеть завершит обмен подтверждениями?
>
> Didit проводит обмен сам.
> Когда вы отправляете перевод `travelRule`, Didit выполняет поиск контрагента в реальном времени, передаёт данные отправителя и получателя принимающей VASP по соответствующим каналам и присваивает переводу статус `COMPLETED` только после того, как сторона контрагента подтвердит это - это управляемый обмен подтверждениями, а не журнал после расчёта.
> Ответ контрагента обрабатывается по всем каналам, кроме `TRUST`, который только отправляет, поэтому перевод по нему истекает, а не завершается. По `GTR`, `VERIFYVASP` и `SYGNA` Didit получает ответ из ответа сети или опрашивает её каждые 5 минут, и ни один обмен по этим каналам пока не был завершён в работающей сети. По `TRP`, если контрагент не отвечает сразу, нужен публичный хост TRP Didit, который пока не запущен (см. [Сети Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md)).
> Didit также хранит полную запись и статус соответствия каждого обмена ([жизненный цикл](#lifecycle) из 13 статусов, [webhook](#webhooks) и журнал аудита).
> Если вы предпочитаете вести Travel Rule в собственном процессе, Didit может вместо этого записывать данные пассивно как вашу единственную систему учёта (см. вопрос о пассивной записи ниже).
> Доступны оба режима: активный обмен запускается, когда вы задаёте `is_enabled: true` в [настройках](#настройка) (по умолчанию `false`). До этого Didit записывает данные пассивно.

> [accordion] Обмен нативный для Didit или зависит от внешнего провайдера взаимодействия (Notabene, Sygna, VerifyVASP, TRP)?
>
> Обмен нативный. Didit использует собственную реализацию Travel Rule Protocol (TRP), которая будет доступна по адресу `trp.didit.me` после запуска публичного хоста (пока он не отвечает, контрагенты не могут связаться с вами по TRP), и напрямую реализует сетевые протоколы отрасли: `GTR` (Global Travel Rule), `TRUST`, `VerifyVASP` и `Sygna Bridge` (поддерживается нативно, включая зашифрованный ECIES формат IVMS 101 от Sygna и подписанные запросы на разрешение). Didit не является перепродавцом одного стороннего уровня взаимодействия. Связь с контрагентом, который работает в конкретной сети членства, обязательно проходит через эту сеть, потому что в Travel Rule нет единого универсального канала. Однако поиск, обмен подтверждениями, проверку и ведение записей выполняет Didit.
> Сегодня для каждого сетевого канала нужно собственное членство, а `TRUST` только отправляет: см. [Обзор](#обзор).

> [accordion] Какие сети и протоколы Travel Rule поддерживает Didit?
>
> Реализованы нативно и используются в строгом порядке приоритета: `INTERNAL` (обе VASP на Didit), `TRP` (OpenVASP Travel Rule Protocol), `GTR`, `TRUST`, `VerifyVASP`, `Sygna Bridge` и резервный канал `EMAIL` для контрагентов без инструментов.
> Сетевым каналам сегодня нужно ваше собственное членство, `TRUST` только отправляет, а приём через сеть пока не включён: см. [Сети Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md).
> CODE, TRISA, Notabene и Veriscope появляются в Marketplace Didit как плитки запроса доступа, интеграции с ними пока нет.
> Полную таблицу маршрутизации см. в разделе [Как Didit маршрутизирует обмен](https://docs.didit.me/ru/transaction-monitoring/travel-rule-interoperability.md#как-didit-маршрутизирует-обмен), а о том, до кого каждая сеть может дотянуться сегодня, см. в разделе [Сети Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md).

> [accordion] Какую сеть мне использовать и нужно ли мне собственное членство?
>
> `TRP` - базовый вариант, который мы рекомендуем: для него не нужно общее членство. Если вы делитесь своим [Travel Address](#travel-addresses), с вами может связаться любая VASP, которая поддерживает TRP. Когда контрагент известен только по адресу кошелька, [атрибуция по блокчейн-аналитике](#атрибуция-в-блокчейн-аналитике) направляет его по TRP только в том случае, если у записи каталога с атрибуцией уже есть конечная точка TRP.
> При исходящих переводах Didit отправляет по этому каналу активы из списка [идентификаторов активов TRP](#идентификаторы-активов-trp). Если контрагент не отвечает сразу, нужен публичный хост TRP Didit, который пока не запущен.
> Для сетей членства выбирайте по тому, где работают ваши контрагенты: 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.
> Транзакция тарифицируется как обычный Transaction Screening, а не по тарифу Travel Rule.
> Didit сравнивает порог с суммой, пересчитанной в валюту приложения по умолчанию.
> Если Didit не может пересчитать сумму, порог не применяется, и перевод обрабатывается как обмен данными по Travel Rule.
> У регламента EU 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 после депозита](#регистрация-депозита-задним-числом).

> [accordion] Как работают выводы на собственный кошелёк пользователя?
>
> Укажите адрес назначения как некастодиальный кошелёк (`payment_method.method_type: "unhosted_wallet"`). Didit пропускает маршрутизацию через VASP и вместо этого собирает подтверждение того, что ваш клиент контролирует кошелёк: подписание сообщения, тест Satoshi, скриншот или самодекларацию, через [виджет подтверждения владения кошельком](https://docs.didit.me/ru/transaction-monitoring/wallet-ownership-widget.md). Это требование EU TFR к некастодиальным кошелькам в виде API.

> [accordion] Защищены ли персональные данные контрагента во время обмена?
>
> Да.
> Пока владение кошельком не подтверждено на вашей стороне, персональные данные контрагента во входящем переводе маскируются как в объекте перевода, так и в данных стороны контрагента в транзакции (см. [Маскированные данные контрагента](#маскирование-данных-контрагента)).
> Страницы получения по email показывают только сводку перевода (название VASP отправителя, сумму, адрес назначения кошелька), но никогда не показывают данные физического лица.
> Обмен по каналам выполняется по HTTPS, а сетевые каналы (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`) дополнительно шифруют payload IVMS открытым ключом принимающего VASP.

> [accordion] Я уже отслеживаю соответствие Travel Rule своим процессом. Могу ли я просто записать это?
>
> Да. Если Travel Rule отключено, любые `travel_rule_details`, которые вы передаёте (включая переданный вами `status`, текущий или устаревший), записываются в транзакцию пассивно: рельс не запускается, ничего не согласовывается. Используйте это, чтобы внести данные задним числом или вести собственный процесс, сохраняя одну основную систему учёта.

> [accordion] Как протестировать это перед запуском?
>
> Используйте приложение в песочнице с включённым Travel Rule. В песочнице правила и биллинг не применяются, а вебхуки `transaction.created` и `transaction.status.updated` не отправляются, и каждый **исходящий** обмен направляется в [Didit Test VASP](https://docs.didit.me/ru/transaction-monitoring/travel-rule-sandbox.md) по каналу `TEST_VASP`. Тестовый VASP отвечает внутри Didit по выбранному вами сценарию: принятие, отклонение, несовпадение имени, недоступность, тайм-аут или некастодиальный кошелёк. Вы получаете вебхуки `travel_rule.status.updated` с пометкой `environment: sandbox`, и ни одно сообщение не доходит до реального контрагента.
> Для **входящих переводов** попросите тестовый VASP отправить вам перевод с помощью [Trigger Test VASP Inbound Transfer](https://docs.didit.me/ru/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/ru/transaction-monitoring/wallet-ownership-widget.md)
>
> Подтвердите, что клиент контролирует кошелёк: подписью, тестом Satoshi, скриншотом или самодекларацией.

> [card] [Интероперабельность](https://docs.didit.me/ru/transaction-monitoring/travel-rule-interoperability.md)
>
> Эндпоинты для контрагентов, которые другие VASP используют, чтобы связаться с вами: TRP, колбэки, получение по email, sunrise.

> [card] [Сети](https://docs.didit.me/ru/transaction-monitoring/travel-rule-networks.md)
>
> Что каждый рельс и каждая сеть поддерживают сейчас и что ещё ждёт членства.

> [card] [Тестирование в песочнице](https://docs.didit.me/ru/transaction-monitoring/travel-rule-sandbox.md)
>
> Проверьте каждый исход на Didit Test VASP перед запуском в продакшн.

> [card] [Отправка транзакций](https://docs.didit.me/ru/transaction-monitoring/transactions.md)
>
> Как добавить данные Travel Rule в payload Create Transaction.

> [card] [Библиотека правил](https://docs.didit.me/ru/transaction-monitoring/rules-library.md)
>
> Полный каталог предустановленных правил, включая пакет Travel Rule.

> [card] [Вебхуки](https://docs.didit.me/ru/integration/webhooks.md)
>
> Подпишитесь на travel\_rule.status.updated и на все остальные семейства событий.

> [card] [Руководство по консоли](https://docs.didit.me/ru/transaction-monitoring/console.md)
>
> Просмотр статуса Travel Rule в детальном представлении транзакции.


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